Fix bug 1231: k8scluster-list now shows the project name it belongs to
[osm/osmclient.git] / osmclient / scripts / osm.py
index 3f269bb..1df00d6 100755 (executable)
@@ -20,13 +20,34 @@ OSM shell/cli
 
 import click
 from osmclient import client
 
 import click
 from osmclient import client
-from osmclient.common.exceptions import ClientException
+from osmclient.common.exceptions import ClientException, NotFound
 from prettytable import PrettyTable
 import yaml
 import json
 import time
 import pycurl
 import os
 from prettytable import PrettyTable
 import yaml
 import json
 import time
 import pycurl
 import os
+import textwrap
+import pkg_resources
+import logging
+from datetime import datetime
+
+
+# Global variables
+
+CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'], max_content_width=160)
+
+def wrap_text(text, width):
+    wrapper = textwrap.TextWrapper(width=width)
+    lines = text.splitlines()
+    return "\n".join(map(wrapper.fill, lines))
+
+
+def trunc_text(text, length):
+   if len(text) > length:
+       return text[:(length - 3)] + '...'
+   else:
+       return text
 
 
 def check_client_version(obj, what, version='sol005'):
 
 
 def check_client_version(obj, what, version='sol005'):
@@ -38,6 +59,7 @@ def check_client_version(obj, what, version='sol005'):
     :return: -
     :raises ClientError: if the specified version does not match the client version
     """
     :return: -
     :raises ClientError: if the specified version does not match the client version
     """
+    logger.debug("")
     fullclassname = obj.__module__ + "." + obj.__class__.__name__
     message = 'The following commands or options are only supported with the option "--sol005": {}'.format(what)
     if version == 'v1':
     fullclassname = obj.__module__ + "." + obj.__class__.__name__
     message = 'The following commands or options are only supported with the option "--sol005": {}'.format(what)
     if version == 'v1':
@@ -47,9 +69,22 @@ def check_client_version(obj, what, version='sol005'):
     return
 
 
     return
 
 
-CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'], max_content_width=160)
+def get_project(project_list, item):
+    # project_list = ctx.obj.project.list()
+    item_project_list = item.get('_admin').get('projects_read')
+    project_id = 'None'
+    project_name = 'None'
+    if item_project_list:
+        for p1 in item_project_list:
+            project_id = p1
+            for p2 in project_list:
+                if p2['_id'] == project_id:
+                    project_name = p2['name']
+                    return project_id, project_name
+    return project_id, project_name
 
 
-@click.group(context_settings=CONTEXT_SETTINGS)
+
+@click.group(context_settings=dict(help_option_names=['-h', '--help'], max_content_width=160))
 @click.option('--hostname',
               default="127.0.0.1",
               envvar='OSM_HOSTNAME',
 @click.option('--hostname',
               default="127.0.0.1",
               envvar='OSM_HOSTNAME',
@@ -75,6 +110,24 @@ CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'], max_content_width=16
               envvar='OSM_PROJECT',
               help='project (defaults to admin). ' +
                    'Also can set OSM_PROJECT in environment')
               envvar='OSM_PROJECT',
               help='project (defaults to admin). ' +
                    'Also can set OSM_PROJECT in environment')
+@click.option('-v', '--verbose', count=True,
+              help='increase verbosity (-v INFO, -vv VERBOSE, -vvv DEBUG)')
+@click.option('--all-projects',
+              default=None,
+              is_flag=True,
+              help='include all projects')
+@click.option('--public/--no-public', default=None,
+              help='flag for public items (packages, instances, VIM accounts, etc.)')
+@click.option('--project-domain-name', 'project_domain_name',
+              default=None,
+              envvar='OSM_PROJECT_DOMAIN_NAME',
+              help='project domain name for keystone authentication (default to None). ' +
+                   'Also can set OSM_PROJECT_DOMAIN_NAME in environment')
+@click.option('--user-domain-name', 'user_domain_name',
+              default=None,
+              envvar='OSM_USER_DOMAIN_NAME',
+              help='user domain name for keystone authentication (default to None). ' +
+                   'Also can set OSM_USER_DOMAIN_NAME in environment')
 #@click.option('--so-port',
 #              default=None,
 #              envvar='OSM_SO_PORT',
 #@click.option('--so-port',
 #              default=None,
 #              envvar='OSM_SO_PORT',
@@ -96,13 +149,16 @@ CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'], max_content_width=16
 #              help='hostname of RO server.  ' +
 #                   'Also can set OSM_RO_PORT in environment')
 @click.pass_context
 #              help='hostname of RO server.  ' +
 #                   'Also can set OSM_RO_PORT in environment')
 @click.pass_context
-def cli(ctx, hostname, user, password, project):
+def cli_osm(ctx, **kwargs):
+    global logger
+    hostname = kwargs.pop("hostname", None)
     if hostname is None:
         print((
             "either hostname option or OSM_HOSTNAME " +
             "environment variable needs to be specified"))
         exit(1)
     if hostname is None:
         print((
             "either hostname option or OSM_HOSTNAME " +
             "environment variable needs to be specified"))
         exit(1)
-    kwargs={}
+    # Remove None values
+    kwargs = {k: v for k, v in kwargs.items() if v is not None}
 #    if so_port is not None:
 #        kwargs['so_port']=so_port
 #    if so_project is not None:
 #    if so_port is not None:
 #        kwargs['so_port']=so_port
 #    if so_project is not None:
@@ -112,25 +168,31 @@ def cli(ctx, hostname, user, password, project):
 #    if ro_port is not None:
 #        kwargs['ro_port']=ro_port
     sol005 = os.getenv('OSM_SOL005', True)
 #    if ro_port is not None:
 #        kwargs['ro_port']=ro_port
     sol005 = os.getenv('OSM_SOL005', True)
-    if user is not None:
-        kwargs['user']=user
-    if password is not None:
-        kwargs['password']=password
-    if project is not None:
-        kwargs['project']=project
-
+#    if user is not None:
+#        kwargs['user']=user
+#    if password is not None:
+#        kwargs['password']=password
+#    if project is not None:
+#        kwargs['project']=project
+#    if all_projects:
+#        kwargs['all_projects']=all_projects
+#    if public is not None:
+#        kwargs['public']=public
     ctx.obj = client.Client(host=hostname, sol005=sol005, **kwargs)
     ctx.obj = client.Client(host=hostname, sol005=sol005, **kwargs)
+    logger = logging.getLogger('osmclient')
 
 
 ####################
 # LIST operations
 ####################
 
 
 
 ####################
 # LIST operations
 ####################
 
-@cli.command(name='ns-list', short_help='list all NS instances')
-@click.option('--filter', default=None,
+@cli_osm.command(name='ns-list', short_help='list all NS instances')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NS instances matching the filter.')
               help='restricts the list to the NS instances matching the filter.')
+@click.option('--long', is_flag=True,
+              help='get more details of the NS (project, vim, deployment status, configuration status.')
 @click.pass_context
 @click.pass_context
-def ns_list(ctx, filter):
+def ns_list(ctx, filter, long):
     """list all NS instances
 
     \b
     """list all NS instances
 
     \b
@@ -178,86 +240,258 @@ def ns_list(ctx, filter):
        --filter  nsd.vendor=<VENDOR>&nsd-ref=<NSD_NAME>
        --filter  nsd.constituent-vnfd.vnfd-id-ref=<VNFD_NAME>
     """
        --filter  nsd.vendor=<VENDOR>&nsd-ref=<NSD_NAME>
        --filter  nsd.constituent-vnfd.vnfd-id-ref=<VNFD_NAME>
     """
+    def summarize_deployment_status(status_dict):
+        #Nets
+        summary = ""
+        if not status_dict:
+            return summary
+        n_nets = 0
+        status_nets = {}
+        net_list = status_dict.get('nets',[])
+        for net in net_list:
+            n_nets += 1
+            if net['status'] not in status_nets:
+                status_nets[net['status']] = 1
+            else:
+                status_nets[net['status']] +=1
+        message = "Nets: "
+        for k,v in status_nets.items():
+            message += "{}:{},".format(k,v)
+        message += "TOTAL:{}".format(n_nets)
+        summary += "{}".format(message)
+        #VMs and VNFs
+        n_vms = 0
+        status_vms = {}
+        status_vnfs = {}
+        vnf_list = status_dict['vnfs']
+        for vnf in vnf_list:
+            member_vnf_index = vnf['member_vnf_index']
+            if member_vnf_index not in status_vnfs:
+                status_vnfs[member_vnf_index] = {}
+            for vm in vnf['vms']:
+                n_vms += 1
+                if vm['status'] not in status_vms:
+                    status_vms[vm['status']] = 1
+                else:
+                    status_vms[vm['status']] +=1
+                if vm['status'] not in status_vnfs[member_vnf_index]:
+                    status_vnfs[member_vnf_index][vm['status']] = 1
+                else:
+                    status_vnfs[member_vnf_index][vm['status']] += 1
+        message = "VMs: "
+        for k,v in status_vms.items():
+            message += "{}:{},".format(k,v)
+        message += "TOTAL:{}".format(n_vms)
+        summary += "\n{}".format(message)
+        summary += "\nNFs:"
+        for k,v in status_vnfs.items():
+            total = 0
+            message = "\n  {} VMs: ".format(k)
+            for k2,v2 in v.items():
+                message += "{}:{},".format(k2,v2)
+                total += v2
+            message += "TOTAL:{}".format(total)
+        summary += message
+        return summary
+        
+    def summarize_config_status(ee_list):
+        summary = ""
+        if not ee_list:
+            return summary
+        n_ee = 0
+        status_ee = {}
+        for ee in ee_list:
+            n_ee += 1
+            if ee['elementType'] not in status_ee:
+                status_ee[ee['elementType']] = {}
+                status_ee[ee['elementType']][ee['status']] = 1
+                continue
+            if ee['status'] in status_ee[ee['elementType']]:
+                status_ee[ee['elementType']][ee['status']] += 1
+            else:
+                status_ee[ee['elementType']][ee['status']] = 1
+        for elementType in ["KDU", "VDU", "PDU", "VNF", "NS"]:
+            if elementType in status_ee:
+                message = ""
+                total = 0
+                for k,v in status_ee[elementType].items():
+                    message += "{}:{},".format(k,v)
+                    total += v
+                message += "TOTAL:{}\n".format(total)
+                summary += "{}: {}".format(elementType, message)
+        summary += "TOTAL Exec. Env.: {}".format(n_ee)
+        return summary
+
+    logger.debug("")
     if filter:
         check_client_version(ctx.obj, '--filter')
     if filter:
         check_client_version(ctx.obj, '--filter')
+        filter='&'.join(filter)
         resp = ctx.obj.ns.list(filter)
     else:
         resp = ctx.obj.ns.list()
         resp = ctx.obj.ns.list(filter)
     else:
         resp = ctx.obj.ns.list()
-    table = PrettyTable(
+    if long:
+        table = PrettyTable(
         ['ns instance name',
          'id',
         ['ns instance name',
          'id',
-         'operational status',
-         'config status',
-         'detailed status'])
+         'date',
+         'ns state',
+         'current operation',
+         'error details',
+         'project',
+         'vim (inst param)',
+         'deployment status',
+         'configuration status'])
+        project_list = ctx.obj.project.list()
+        vim_list = ctx.obj.vim.list()
+    else:
+        table = PrettyTable(
+        ['ns instance name',
+         'id',
+         'date',
+         'ns state',
+         'current operation',
+         'error details'])
     for ns in resp:
         fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
         if fullclassname == 'osmclient.sol005.client.Client':
             nsr = ns
     for ns in resp:
         fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
         if fullclassname == 'osmclient.sol005.client.Client':
             nsr = ns
+            logger.debug('NS info: {}'.format(nsr))
             nsr_name = nsr['name']
             nsr_id = nsr['_id']
             nsr_name = nsr['name']
             nsr_id = nsr['_id']
+            date = datetime.fromtimestamp(nsr['create-time']).strftime("%Y-%m-%dT%H:%M:%S")
+            ns_state = nsr.get('nsState', nsr['_admin']['nsState'])
+            if long:
+                deployment_status = summarize_deployment_status(nsr.get('deploymentStatus'))
+                config_status = summarize_config_status(nsr.get('configurationStatus'))
+                project_id = nsr.get('_admin').get('projects_read')[0]
+                project_name = '-'
+                for p in project_list:
+                    if p['_id'] == project_id:
+                        project_name = p['name']
+                        break
+                #project = '{} ({})'.format(project_name, project_id)
+                project = project_name
+                vim_id = nsr.get('datacenter')
+                vim_name = '-'
+                for v in vim_list:
+                    if v['uuid'] == vim_id:
+                        vim_name = v['name']
+                        break
+                #vim = '{} ({})'.format(vim_name, vim_id)
+                vim = vim_name
+            if 'currentOperation' in nsr:
+                current_operation = "{} ({})".format(nsr['currentOperation'],nsr['currentOperationID'])
+            else:
+                current_operation = "{} ({})".format(nsr['_admin'].get('current-operation','-'), nsr['_admin']['nslcmop'])
+            error_details = "N/A"
+            if ns_state == "BROKEN" or ns_state == "DEGRADED" or \
+                ('currentOperation' not in nsr and nsr.get('errorDescription')):
+                error_details = "{}\nDetail: {}".format(nsr['errorDescription'], nsr['errorDetail'])
         else:
             nsopdata = ctx.obj.ns.get_opdata(ns['id'])
             nsr = nsopdata['nsr:nsr']
             nsr_name = nsr['name-ref']
             nsr_id = nsr['ns-instance-config-ref']
         else:
             nsopdata = ctx.obj.ns.get_opdata(ns['id'])
             nsr = nsopdata['nsr:nsr']
             nsr_name = nsr['name-ref']
             nsr_id = nsr['ns-instance-config-ref']
-        opstatus = nsr['operational-status'] if 'operational-status' in nsr else 'Not found'
-        configstatus = nsr['config-status'] if 'config-status' in nsr else 'Not found'
-        detailed_status = nsr['detailed-status'] if 'detailed-status' in nsr else 'Not found'
-        if configstatus == "config_not_needed":
-            configstatus = "configured (no charms)"
-        table.add_row(
-            [nsr_name,
-             nsr_id,
-             opstatus,
-             configstatus,
-             detailed_status])
+            date = '-'
+            project = '-'
+            deployment_status = nsr['operational-status'] if 'operational-status' in nsr else 'Not found'
+            ns_state = deployment_status
+            config_status = nsr.get('config-status', 'Not found')
+            current_operation = "Unknown"
+            error_details = nsr.get('detailed-status', 'Not found')
+            if config_status == "config_not_needed":
+                config_status = "configured (no charms)"
+
+        if long:
+            table.add_row(
+                 [nsr_name,
+                 nsr_id,
+                 date,
+                 ns_state,
+                 current_operation,
+                 wrap_text(text=error_details,width=40),
+                 project,
+                 vim,
+                 deployment_status,
+                 config_status])
+        else:
+            table.add_row(
+                 [nsr_name,
+                 nsr_id,
+                 date,
+                 ns_state,
+                 current_operation,
+                 wrap_text(text=error_details,width=40)])
     table.align = 'l'
     print(table)
     table.align = 'l'
     print(table)
+    print('To get the history of all operations over a NS, run "osm ns-op-list NS_ID"')
+    print('For more details on the current operation, run "osm ns-op-show OPERATION_ID"')
 
 
-
-def nsd_list(ctx, filter):
+def nsd_list(ctx, filter, long):
+    logger.debug("")
     if filter:
         check_client_version(ctx.obj, '--filter')
     if filter:
         check_client_version(ctx.obj, '--filter')
+        filter='&'.join(filter)
         resp = ctx.obj.nsd.list(filter)
     else:
         resp = ctx.obj.nsd.list()
     # print(yaml.safe_dump(resp))
         resp = ctx.obj.nsd.list(filter)
     else:
         resp = ctx.obj.nsd.list()
     # print(yaml.safe_dump(resp))
-    table = PrettyTable(['nsd name', 'id'])
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname == 'osmclient.sol005.client.Client':
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname == 'osmclient.sol005.client.Client':
-        for ns in resp:
-            name = ns['name'] if 'name' in ns else '-'
-            table.add_row([name, ns['_id']])
+        if long:
+            table = PrettyTable(['nsd name', 'id', 'onboarding state', 'operational state',
+                                 'usage state', 'date', 'last update'])
+        else:
+            table = PrettyTable(['nsd name', 'id'])
+        for nsd in resp:
+            name = nsd.get('name','-')
+            if long:
+                onb_state = nsd['_admin'].get('onboardingState','-')
+                op_state = nsd['_admin'].get('operationalState','-')
+                usage_state = nsd['_admin'].get('usageState','-')
+                date = datetime.fromtimestamp(nsd['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S")
+                last_update = datetime.fromtimestamp(nsd['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S")
+                table.add_row([name, nsd['_id'], onb_state, op_state, usage_state, date, last_update])
+            else:
+                table.add_row([name, nsd['_id']])
     else:
     else:
-        for ns in resp:
-            table.add_row([ns['name'], ns['id']])
+        table = PrettyTable(['nsd name', 'id'])
+        for nsd in resp:
+            table.add_row([nsd['name'], nsd['id']])
     table.align = 'l'
     print(table)
 
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='nsd-list', short_help='list all NS packages')
-@click.option('--filter', default=None,
+@cli_osm.command(name='nsd-list', short_help='list all NS packages')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NSD/NSpkg matching the filter')
               help='restricts the list to the NSD/NSpkg matching the filter')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def nsd_list1(ctx, filter):
+def nsd_list1(ctx, filter, long):
     """list all NSD/NS pkg in the system"""
     """list all NSD/NS pkg in the system"""
-    nsd_list(ctx, filter)
+    logger.debug("")
+    nsd_list(ctx, filter, long)
 
 
 
 
-@cli.command(name='nspkg-list', short_help='list all NS packages')
-@click.option('--filter', default=None,
+@cli_osm.command(name='nspkg-list', short_help='list all NS packages')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NSD/NSpkg matching the filter')
               help='restricts the list to the NSD/NSpkg matching the filter')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def nsd_list2(ctx, filter):
+def nsd_list2(ctx, filter, long):
     """list all NS packages"""
     """list all NS packages"""
-    nsd_list(ctx, filter)
+    logger.debug("")
+    nsd_list(ctx, filter, long)
 
 
 
 
-def vnfd_list(ctx, nf_type, filter):
+def vnfd_list(ctx, nf_type, filter, long):
+    logger.debug("")
     if nf_type:
         check_client_version(ctx.obj, '--nf_type')
     elif filter:
         check_client_version(ctx.obj, '--filter')
     if nf_type:
         check_client_version(ctx.obj, '--nf_type')
     elif filter:
         check_client_version(ctx.obj, '--filter')
+    if filter:
+        filter='&'.join(filter)
     if nf_type:
         if nf_type == "vnf":
             nf_filter = "_admin.type=vnfd"
     if nf_type:
         if nf_type == "vnf":
             nf_filter = "_admin.type=vnfd"
@@ -276,87 +510,183 @@ def vnfd_list(ctx, nf_type, filter):
     else:
         resp = ctx.obj.vnfd.list()
     # print(yaml.safe_dump(resp))
     else:
         resp = ctx.obj.vnfd.list()
     # print(yaml.safe_dump(resp))
-    table = PrettyTable(['nfpkg name', 'id'])
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname == 'osmclient.sol005.client.Client':
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname == 'osmclient.sol005.client.Client':
+        if long:
+            table = PrettyTable(['nfpkg name', 'id', 'vendor', 'version', 'onboarding state', 'operational state',
+                                  'usage state', 'date', 'last update'])
+        else:
+            table = PrettyTable(['nfpkg name', 'id'])
         for vnfd in resp:
             name = vnfd['name'] if 'name' in vnfd else '-'
         for vnfd in resp:
             name = vnfd['name'] if 'name' in vnfd else '-'
-            table.add_row([name, vnfd['_id']])
+            if long:
+                onb_state = vnfd['_admin'].get('onboardingState','-')
+                op_state = vnfd['_admin'].get('operationalState','-')
+                vendor = vnfd.get('vendor')
+                version = vnfd.get('version')
+                usage_state = vnfd['_admin'].get('usageState','-')
+                date = datetime.fromtimestamp(vnfd['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S")
+                last_update = datetime.fromtimestamp(vnfd['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S")
+                table.add_row([name, vnfd['_id'], vendor, version, onb_state, op_state, usage_state, date, last_update])
+            else:
+                table.add_row([name, vnfd['_id']])
     else:
     else:
+        table = PrettyTable(['nfpkg name', 'id'])
         for vnfd in resp:
             table.add_row([vnfd['name'], vnfd['id']])
     table.align = 'l'
     print(table)
 
 
         for vnfd in resp:
             table.add_row([vnfd['name'], vnfd['id']])
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='vnfd-list', short_help='list all xNF packages (VNF, HNF, PNF)')
+@cli_osm.command(name='vnfd-list', short_help='list all xNF packages (VNF, HNF, PNF)')
 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
-@click.option('--filter', default=None,
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NF pkg matching the filter')
               help='restricts the list to the NF pkg matching the filter')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def vnfd_list1(ctx, nf_type, filter):
+def vnfd_list1(ctx, nf_type, filter, long):
     """list all xNF packages (VNF, HNF, PNF)"""
     """list all xNF packages (VNF, HNF, PNF)"""
-    vnfd_list(ctx, nf_type, filter)
+    logger.debug("")
+    vnfd_list(ctx, nf_type, filter, long)
 
 
 
 
-@cli.command(name='vnfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)')
+@cli_osm.command(name='vnfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)')
 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
-@click.option('--filter', default=None,
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NFpkg matching the filter')
               help='restricts the list to the NFpkg matching the filter')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def vnfd_list2(ctx, nf_type, filter):
+def vnfd_list2(ctx, nf_type, filter, long):
     """list all xNF packages (VNF, HNF, PNF)"""
     """list all xNF packages (VNF, HNF, PNF)"""
-    vnfd_list(ctx, nf_type, filter)
+    logger.debug("")
+    vnfd_list(ctx, nf_type, filter, long)
 
 
 
 
-@cli.command(name='nfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)')
+@cli_osm.command(name='nfpkg-list', short_help='list all xNF packages (VNF, HNF, PNF)')
 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
 @click.option('--nf_type', help='type of NF (vnf, pnf, hnf)')
-@click.option('--filter', default=None,
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NFpkg matching the filter')
               help='restricts the list to the NFpkg matching the filter')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def nfpkg_list(ctx, nf_type, filter):
+def nfpkg_list(ctx, nf_type, filter, long):
     """list all xNF packages (VNF, HNF, PNF)"""
     """list all xNF packages (VNF, HNF, PNF)"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        vnfd_list(ctx, nf_type, filter)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    vnfd_list(ctx, nf_type, filter, long)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-def vnf_list(ctx, ns, filter):
-    try:
-        if ns or filter:
-            if ns:
-                check_client_version(ctx.obj, '--ns')
-            if filter:
-                check_client_version(ctx.obj, '--filter')
-            resp = ctx.obj.vnf.list(ns, filter)
+def pkg_repo_list(ctx, pkgtype, filter, repo, long):
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.osmrepo.pkg_list(pkgtype, filter, repo)
+    if long:
+        table = PrettyTable(['nfpkg name', 'vendor', 'version', 'latest', 'description', 'repository'])
+    else:
+        table = PrettyTable(['nfpkg name', 'repository'])
+    for vnfd in resp:
+        name = vnfd.get('name', '-')
+        repository = vnfd.get('repository')
+        if long:
+            vendor = vnfd.get('vendor')
+            version = vnfd.get('version')
+            description = vnfd.get('description')
+            latest = vnfd.get('latest')
+            table.add_row([name, vendor, version, latest, description, repository])
         else:
         else:
-            resp = ctx.obj.vnf.list()
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+            table.add_row([name, repository])
+        table.align = 'l'
+    print(table)
+
+
+@cli_osm.command(name='vnfpkg-repo-list', short_help='list all xNF from OSM repositories')
+@click.option('--filter', default=None, multiple=True,
+              help='restricts the list to the NFpkg matching the filter')
+@click.option('--repo', default=None,
+              help='restricts the list to a particular OSM repository')
+@click.option('--long', is_flag=True, help='get more details')
+@click.pass_context
+def nfpkg_repo_list1(ctx, filter, repo, long):
+    """list xNF packages from OSM repositories"""
+    pkgtype = 'vnf'
+    pkg_repo_list(ctx, pkgtype, filter, repo, long)
+
+
+@cli_osm.command(name='nfpkg-repo-list', short_help='list all xNF from OSM repositories')
+@click.option('--filter', default=None, multiple=True,
+              help='restricts the list to the NFpkg matching the filter')
+@click.option('--repo', default=None,
+              help='restricts the list to a particular OSM repository')
+@click.option('--long', is_flag=True, help='get more details')
+@click.pass_context
+def nfpkg_repo_list2(ctx, filter, repo, long):
+    """list xNF packages from OSM repositories"""
+    pkgtype = 'vnf'
+    pkg_repo_list(ctx, pkgtype, filter, repo, long)
+
+
+@cli_osm.command(name='nsd-repo-list', short_help='list all NS from OSM repositories')
+@click.option('--filter', default=None, multiple=True,
+              help='restricts the list to the NS matching the filter')
+@click.option('--repo', default=None,
+              help='restricts the list to a particular OSM repository')
+@click.option('--long', is_flag=True, help='get more details')
+@click.pass_context
+def nspkg_repo_list(ctx, filter, repo, long):
+    """list xNF packages from OSM repositories"""
+    pkgtype = 'ns'
+    pkg_repo_list(ctx, pkgtype, filter, repo, long)
+
+
+@cli_osm.command(name='nspkg-repo-list', short_help='list all NS from OSM repositories')
+@click.option('--filter', default=None, multiple=True,
+              help='restricts the list to the NS matching the filter')
+@click.option('--repo', default=None,
+              help='restricts the list to a particular OSM repository')
+@click.option('--long', is_flag=True, help='get more details')
+@click.pass_context
+def nspkg_repo_list2(ctx, filter, repo, long):
+    """list xNF packages from OSM repositories"""
+    pkgtype = 'ns'
+    pkg_repo_list(ctx, pkgtype, filter, repo, long)
+
+
+def vnf_list(ctx, ns, filter, long):
+    # try:
+    if ns or filter:
+        if ns:
+            check_client_version(ctx.obj, '--ns')
+        if filter:
+            filter='&'.join(filter)
+            check_client_version(ctx.obj, '--filter')
+        resp = ctx.obj.vnf.list(ns, filter)
+    else:
+        resp = ctx.obj.vnf.list()
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname == 'osmclient.sol005.client.Client':
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname == 'osmclient.sol005.client.Client':
-        table = PrettyTable(
-            ['vnf id',
-             'name',
-             'ns id',
-             'vnf member index',
-             'vnfd name',
-             'vim account id',
-             'ip address'])
+        field_names = ['vnf id', 'name', 'ns id', 'vnf member index',
+                       'vnfd name', 'vim account id', 'ip address']
+        if long:
+            field_names = ['vnf id', 'name', 'ns id', 'vnf member index',
+                           'vnfd name', 'vim account id', 'ip address',
+                           'date', 'last update']
+        table = PrettyTable(field_names)
         for vnfr in resp:
             name = vnfr['name'] if 'name' in vnfr else '-'
         for vnfr in resp:
             name = vnfr['name'] if 'name' in vnfr else '-'
-            table.add_row(
-                [vnfr['_id'],
-                 name,
-                 vnfr['nsr-id-ref'],
-                 vnfr['member-vnf-index-ref'],
-                 vnfr['vnfd-ref'],
-                 vnfr['vim-account-id'],
-                 vnfr['ip-address']])
+            new_row = [vnfr['_id'], name, vnfr['nsr-id-ref'],
+                       vnfr['member-vnf-index-ref'], vnfr['vnfd-ref'],
+                       vnfr['vim-account-id'], vnfr['ip-address']]
+            if long:
+                date = datetime.fromtimestamp(vnfr['_admin']['created']).strftime("%Y-%m-%dT%H:%M:%S")
+                last_update = datetime.fromtimestamp(vnfr['_admin']['modified']).strftime("%Y-%m-%dT%H:%M:%S")
+                new_row.extend([date, last_update])
+            table.add_row(new_row)
     else:
         table = PrettyTable(
             ['vnf name',
     else:
         table = PrettyTable(
             ['vnf name',
@@ -376,22 +706,25 @@ def vnf_list(ctx, ns, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='vnf-list', short_help='list all NF instances')
+@cli_osm.command(name='vnf-list', short_help='list all NF instances')
 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
-@click.option('--filter', default=None,
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NF instances matching the filter.')
               help='restricts the list to the NF instances matching the filter.')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def vnf_list1(ctx, ns, filter):
+def vnf_list1(ctx, ns, filter, long):
     """list all NF instances"""
     """list all NF instances"""
-    vnf_list(ctx, ns, filter)
+    logger.debug("")
+    vnf_list(ctx, ns, filter, long)
 
 
 
 
-@cli.command(name='nf-list', short_help='list all NF instances')
+@cli_osm.command(name='nf-list', short_help='list all NF instances')
 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
 @click.option('--ns', default=None, help='NS instance id or name to restrict the NF list')
-@click.option('--filter', default=None,
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NF instances matching the filter.')
               help='restricts the list to the NF instances matching the filter.')
+@click.option('--long', is_flag=True, help='get more details')
 @click.pass_context
 @click.pass_context
-def nf_list(ctx, ns, filter):
+def nf_list(ctx, ns, filter, long):
     """list all NF instances
 
     \b
     """list all NF instances
 
     \b
@@ -439,40 +772,88 @@ def nf_list(ctx, ns, filter):
        --filter  vdur.ip-address=<IP_ADDRESS>
        --filter  vnfd-ref=<VNFD_NAME>,vdur.ip-address=<IP_ADDRESS>
     """
        --filter  vdur.ip-address=<IP_ADDRESS>
        --filter  vnfd-ref=<VNFD_NAME>,vdur.ip-address=<IP_ADDRESS>
     """
-    vnf_list(ctx, ns, filter)
+    logger.debug("")
+    vnf_list(ctx, ns, filter, long)
 
 
 
 
-@cli.command(name='ns-op-list', short_help='shows the history of operations over a NS instance')
+@cli_osm.command(name='ns-op-list', short_help='shows the history of operations over a NS instance')
 @click.argument('name')
 @click.argument('name')
+@click.option('--long', is_flag=True,
+              help='get more details of the NS operation (date, ).')
 @click.pass_context
 @click.pass_context
-def ns_op_list(ctx, name):
+def ns_op_list(ctx, name, long):
     """shows the history of operations over a NS instance
 
     NAME: name or ID of the NS instance
     """
     """shows the history of operations over a NS instance
 
     NAME: name or ID of the NS instance
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.ns.list_op(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    def formatParams(params):
+        if params['lcmOperationType']=='instantiate':
+            params.pop('nsDescription')
+            params.pop('nsName')
+            params.pop('nsdId')
+            params.pop('nsr_id')
+        elif params['lcmOperationType']=='action':
+            params.pop('primitive')
+        params.pop('lcmOperationType')
+        params.pop('nsInstanceId')
+        return params
+
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.ns.list_op(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+    if long:
+        table = PrettyTable(['id', 'operation', 'action_name', 'operation_params', 'status', 'date', 'last update', 'detail'])
+    else:
+        table = PrettyTable(['id', 'operation', 'action_name', 'status', 'date', 'detail'])
 
 
-    table = PrettyTable(['id', 'operation', 'status'])
+    #print(yaml.safe_dump(resp))
     for op in resp:
     for op in resp:
-        table.add_row([op['id'], op['lcmOperationType'],
-                       op['operationState']])
+        action_name = "N/A"
+        if op['lcmOperationType']=='action':
+            action_name = op['operationParams']['primitive']
+        detail = "-"
+        if op['operationState'] == 'PROCESSING':
+            if op['queuePosition'] is not None and op['queuePosition'] > 0:
+                detail = "In queue. Current position: {}".format(op['queuePosition'])
+            elif op['lcmOperationType'] in ('instantiate', 'terminate'):
+                if op['stage']:
+                    detail = op['stage']
+        elif op['operationState'] in ('FAILED', 'FAILED_TEMP'):
+            detail = op.get('errorMessage','-')
+        date = datetime.fromtimestamp(op['startTime']).strftime("%Y-%m-%dT%H:%M:%S")
+        last_update = datetime.fromtimestamp(op['statusEnteredTime']).strftime("%Y-%m-%dT%H:%M:%S")
+        if long:
+            table.add_row([op['id'],
+                           op['lcmOperationType'],
+                           action_name,
+                           wrap_text(text=json.dumps(formatParams(op['operationParams']),indent=2),width=50),
+                           op['operationState'],
+                           date,
+                           last_update,
+                           wrap_text(text=detail,width=50)])
+        else:
+            table.add_row([op['id'], op['lcmOperationType'], action_name,
+                           op['operationState'], date, wrap_text(text=detail or "",width=50)])
     table.align = 'l'
     print(table)
 
 
 def nsi_list(ctx, filter):
     """list all Network Slice Instances"""
     table.align = 'l'
     print(table)
 
 
 def nsi_list(ctx, filter):
     """list all Network Slice Instances"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.nsi.list(filter)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.nsi.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(
         ['netslice instance name',
          'id',
     table = PrettyTable(
         ['netslice instance name',
          'id',
@@ -497,31 +878,36 @@ def nsi_list(ctx, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='nsi-list', short_help='list all Network Slice Instances (NSI)')
-@click.option('--filter', default=None,
+@cli_osm.command(name='nsi-list', short_help='list all Network Slice Instances (NSI)')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the Network Slice Instances matching the filter')
 @click.pass_context
 def nsi_list1(ctx, filter):
     """list all Network Slice Instances (NSI)"""
               help='restricts the list to the Network Slice Instances matching the filter')
 @click.pass_context
 def nsi_list1(ctx, filter):
     """list all Network Slice Instances (NSI)"""
+    logger.debug("")
     nsi_list(ctx, filter)
 
 
     nsi_list(ctx, filter)
 
 
-@cli.command(name='netslice-instance-list', short_help='list all Network Slice Instances (NSI)')
-@click.option('--filter', default=None,
+@cli_osm.command(name='netslice-instance-list', short_help='list all Network Slice Instances (NSI)')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the Network Slice Instances matching the filter')
 @click.pass_context
 def nsi_list2(ctx, filter):
     """list all Network Slice Instances (NSI)"""
               help='restricts the list to the Network Slice Instances matching the filter')
 @click.pass_context
 def nsi_list2(ctx, filter):
     """list all Network Slice Instances (NSI)"""
+    logger.debug("")
     nsi_list(ctx, filter)
 
 
 def nst_list(ctx, filter):
     nsi_list(ctx, filter)
 
 
 def nst_list(ctx, filter):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.nst.list(filter)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.nst.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     # print(yaml.safe_dump(resp))
     table = PrettyTable(['nst name', 'id'])
     for nst in resp:
     # print(yaml.safe_dump(resp))
     table = PrettyTable(['nst name', 'id'])
     for nst in resp:
@@ -531,31 +917,34 @@ def nst_list(ctx, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='nst-list', short_help='list all Network Slice Templates (NST)')
-@click.option('--filter', default=None,
+@cli_osm.command(name='nst-list', short_help='list all Network Slice Templates (NST)')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NST matching the filter')
 @click.pass_context
 def nst_list1(ctx, filter):
     """list all Network Slice Templates (NST) in the system"""
               help='restricts the list to the NST matching the filter')
 @click.pass_context
 def nst_list1(ctx, filter):
     """list all Network Slice Templates (NST) in the system"""
+    logger.debug("")
     nst_list(ctx, filter)
 
 
     nst_list(ctx, filter)
 
 
-@cli.command(name='netslice-template-list', short_help='list all Network Slice Templates (NST)')
-@click.option('--filter', default=None,
+@cli_osm.command(name='netslice-template-list', short_help='list all Network Slice Templates (NST)')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the NST matching the filter')
 @click.pass_context
 def nst_list2(ctx, filter):
     """list all Network Slice Templates (NST) in the system"""
               help='restricts the list to the NST matching the filter')
 @click.pass_context
 def nst_list2(ctx, filter):
     """list all Network Slice Templates (NST) in the system"""
+    logger.debug("")
     nst_list(ctx, filter)
 
 
 def nsi_op_list(ctx, name):
     nst_list(ctx, filter)
 
 
 def nsi_op_list(ctx, name):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.nsi.list_op(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.nsi.list_op(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(['id', 'operation', 'status'])
     for op in resp:
         table.add_row([op['id'], op['lcmOperationType'],
     table = PrettyTable(['id', 'operation', 'status'])
     for op in resp:
         table.add_row([op['id'], op['lcmOperationType'],
@@ -564,7 +953,7 @@ def nsi_op_list(ctx, name):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='nsi-op-list', short_help='shows the history of operations over a Network Slice Instance (NSI)')
+@cli_osm.command(name='nsi-op-list', short_help='shows the history of operations over a Network Slice Instance (NSI)')
 @click.argument('name')
 @click.pass_context
 def nsi_op_list1(ctx, name):
 @click.argument('name')
 @click.pass_context
 def nsi_op_list1(ctx, name):
@@ -572,10 +961,11 @@ def nsi_op_list1(ctx, name):
 
     NAME: name or ID of the Network Slice Instance
     """
 
     NAME: name or ID of the Network Slice Instance
     """
+    logger.debug("")
     nsi_op_list(ctx, name)
 
 
     nsi_op_list(ctx, name)
 
 
-@cli.command(name='netslice-instance-op-list', short_help='shows the history of operations over a Network Slice Instance (NSI)')
+@cli_osm.command(name='netslice-instance-op-list', short_help='shows the history of operations over a Network Slice Instance (NSI)')
 @click.argument('name')
 @click.pass_context
 def nsi_op_list2(ctx, name):
 @click.argument('name')
 @click.pass_context
 def nsi_op_list2(ctx, name):
@@ -583,21 +973,25 @@ def nsi_op_list2(ctx, name):
 
     NAME: name or ID of the Network Slice Instance
     """
 
     NAME: name or ID of the Network Slice Instance
     """
+    logger.debug("")
     nsi_op_list(ctx, name)
 
 
     nsi_op_list(ctx, name)
 
 
-@cli.command(name='pdu-list', short_help='list all Physical Deployment Units (PDU)')
-@click.option('--filter', default=None,
+@cli_osm.command(name='pdu-list', short_help='list all Physical Deployment Units (PDU)')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the Physical Deployment Units matching the filter')
 @click.pass_context
 def pdu_list(ctx, filter):
     """list all Physical Deployment Units (PDU)"""
               help='restricts the list to the Physical Deployment Units matching the filter')
 @click.pass_context
 def pdu_list(ctx, filter):
     """list all Physical Deployment Units (PDU)"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.pdu.list(filter)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.pdu.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(
         ['pdu name',
          'id',
     table = PrettyTable(
         ['pdu name',
          'id',
@@ -626,25 +1020,26 @@ def pdu_list(ctx, filter):
 ####################
 
 def nsd_show(ctx, name, literal):
 ####################
 
 def nsd_show(ctx, name, literal):
-    try:
-        resp = ctx.obj.nsd.get(name)
-        # resp = ctx.obj.nsd.get_individual(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    resp = ctx.obj.nsd.get(name)
+    # resp = ctx.obj.nsd.get_individual(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(resp))
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(resp.items()):
         return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(resp.items()):
-        table.add_row([k, json.dumps(v, indent=2)])
+        table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
     table.align = 'l'
     print(table)
 
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='nsd-show', short_help='shows the content of a NSD')
+@cli_osm.command(name='nsd-show', short_help='shows the details of a NS package')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -654,10 +1049,11 @@ def nsd_show1(ctx, name, literal):
 
     NAME: name or ID of the NSD/NSpkg
     """
 
     NAME: name or ID of the NSD/NSpkg
     """
+    logger.debug("")
     nsd_show(ctx, name, literal)
 
 
     nsd_show(ctx, name, literal)
 
 
-@cli.command(name='nspkg-show', short_help='shows the content of a NSD')
+@cli_osm.command(name='nspkg-show', short_help='shows the details of a NS package')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -667,29 +1063,56 @@ def nsd_show2(ctx, name, literal):
 
     NAME: name or ID of the NSD/NSpkg
     """
 
     NAME: name or ID of the NSD/NSpkg
     """
+    logger.debug("")
     nsd_show(ctx, name, literal)
 
 
 def vnfd_show(ctx, name, literal):
     nsd_show(ctx, name, literal)
 
 
 def vnfd_show(ctx, name, literal):
-    try:
-        resp = ctx.obj.vnfd.get(name)
-        # resp = ctx.obj.vnfd.get_individual(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    resp = ctx.obj.vnfd.get(name)
+    # resp = ctx.obj.vnfd.get_individual(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(resp))
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(resp.items()):
         return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(resp.items()):
-        table.add_row([k, json.dumps(v, indent=2)])
+        table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
+    table.align = 'l'
+    print(table)
+
+
+def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal):
+    logger.debug("")
+    if filter:
+        filter='&'.join(filter)
+    # try:
+    resp = ctx.obj.osmrepo.pkg_get(pkgtype, name, repo, version, filter)
+
+    if literal:
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+        return
+    pkgtype += 'd'
+    catalog = pkgtype + '-catalog'
+    full_catalog = pkgtype + ':' + catalog
+    if resp.get(catalog):
+        resp = resp.pop(catalog)[pkgtype][0]
+    elif resp.get(full_catalog):
+        resp = resp.pop(full_catalog)[pkgtype][0]
+
+    table = PrettyTable(['field', 'value'])
+    for k, v in list(resp.items()):
+        table.add_row([k, wrap_text(text=json.dumps(v, indent=2), width=100)])
     table.align = 'l'
     print(table)
 
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='vnfd-show', short_help='shows the content of a VNFD')
+@cli_osm.command(name='vnfd-show', short_help='shows the details of a NF package')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -699,10 +1122,11 @@ def vnfd_show1(ctx, name, literal):
 
     NAME: name or ID of the VNFD/VNFpkg
     """
 
     NAME: name or ID of the VNFD/VNFpkg
     """
+    logger.debug("")
     vnfd_show(ctx, name, literal)
 
 
     vnfd_show(ctx, name, literal)
 
 
-@cli.command(name='vnfpkg-show', short_help='shows the content of a VNFD')
+@cli_osm.command(name='vnfpkg-show', short_help='shows the details of a NF package')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -712,10 +1136,77 @@ def vnfd_show2(ctx, name, literal):
 
     NAME: name or ID of the VNFD/VNFpkg
     """
 
     NAME: name or ID of the VNFD/VNFpkg
     """
+    logger.debug("")
     vnfd_show(ctx, name, literal)
 
 
     vnfd_show(ctx, name, literal)
 
 
-@cli.command(name='nfpkg-show', short_help='shows the content of a NF Descriptor')
+@cli_osm.command(name='vnfpkg-repo-show', short_help='shows the details of a NF package in an OSM repository')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.option('--repo',
+              required=True,
+              help='Repository name')
+@click.argument('name')
+@click.option('--filter', default=None, multiple=True,
+              help='filter by fields')
+@click.option('--version',
+              default='latest',
+              help='package version')
+@click.pass_context
+def vnfd_show3(ctx, name, repo, version, literal=None, filter=None):
+    """shows the content of a VNFD in a repository
+
+    NAME: name or ID of the VNFD/VNFpkg
+    """
+    pkgtype = 'vnf'
+    pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
+
+
+@cli_osm.command(name='nsd-repo-show', short_help='shows the details of a NS package in an OSM repository')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.option('--repo',
+              required=True,
+              help='Repository name')
+@click.argument('name')
+@click.option('--filter', default=None, multiple=True,
+              help='filter by fields')
+@click.option('--version',
+              default='latest',
+              help='package version')
+@click.pass_context
+def nsd_repo_show(ctx, name, repo, version, literal=None, filter=None):
+    """shows the content of a VNFD in a repository
+
+    NAME: name or ID of the VNFD/VNFpkg
+    """
+    pkgtype = 'ns'
+    pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
+
+
+@cli_osm.command(name='nspkg-repo-show', short_help='shows the details of a NS package in an OSM repository')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.option('--repo',
+              required=True,
+              help='Repository name')
+@click.argument('name')
+@click.option('--filter', default=None, multiple=True,
+              help='filter by fields')
+@click.option('--version',
+              default='latest',
+              help='package version')
+@click.pass_context
+def nsd_repo_show2(ctx, name, repo, version, literal=None, filter=None):
+    """shows the content of a VNFD in a repository
+
+    NAME: name or ID of the VNFD/VNFpkg
+    """
+    pkgtype = 'ns'
+    pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
+
+
+@cli_osm.command(name='nfpkg-show', short_help='shows the details of a NF package')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -725,86 +1216,167 @@ def nfpkg_show(ctx, name, literal):
 
     NAME: name or ID of the NFpkg
     """
 
     NAME: name or ID of the NFpkg
     """
+    logger.debug("")
     vnfd_show(ctx, name, literal)
 
 
     vnfd_show(ctx, name, literal)
 
 
-@cli.command(name='ns-show', short_help='shows the info of a NS instance')
+@cli_osm.command(name='nfpkg-repo-show', short_help='shows the details of a NF package in an OSM repository')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.option('--repo',
+              required=True,
+              help='Repository name')
+@click.argument('name')
+@click.option('--filter', default=None, multiple=True,
+              help='filter by fields')
+@click.option('--version',
+              default='latest',
+              help='package version')
+@click.pass_context
+def vnfd_show4(ctx, name, repo, version, literal=None, filter=None):
+    """shows the content of a VNFD in a repository
+
+    NAME: name or ID of the VNFD/VNFpkg
+    """
+    pkgtype = 'vnf'
+    pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
+
+
+@cli_osm.command(name='ns-show', short_help='shows the info of a NS instance')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 def ns_show(ctx, name, literal, filter):
     """shows the info of a NS instance
 
     NAME: name or ID of the NS instance
     """
 @click.pass_context
 def ns_show(ctx, name, literal, filter):
     """shows the info of a NS instance
 
     NAME: name or ID of the NS instance
     """
-    try:
-        ns = ctx.obj.ns.get(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    ns = ctx.obj.ns.get(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(ns))
+        print(yaml.safe_dump(ns, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
 
     for k, v in list(ns.items()):
         return
 
     table = PrettyTable(['field', 'value'])
 
     for k, v in list(ns.items()):
-        if filter is None or filter in k:
-            table.add_row([k, json.dumps(v, indent=2)])
+        if not filter or k in filter:
+            table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
 
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname != 'osmclient.sol005.client.Client':
         nsopdata = ctx.obj.ns.get_opdata(ns['id'])
         nsr_optdata = nsopdata['nsr:nsr']
         for k, v in list(nsr_optdata.items()):
 
     fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
     if fullclassname != 'osmclient.sol005.client.Client':
         nsopdata = ctx.obj.ns.get_opdata(ns['id'])
         nsr_optdata = nsopdata['nsr:nsr']
         for k, v in list(nsr_optdata.items()):
-            if filter is None or filter in k:
-                table.add_row([k, json.dumps(v, indent=2)])
+            if not filter or k in filter:
+                table.add_row([k, wrap_text(json.dumps(v, indent=2),width=100)])
     table.align = 'l'
     print(table)
 
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='vnf-show', short_help='shows the info of a VNF instance')
+@cli_osm.command(name='vnf-show', short_help='shows the info of a VNF instance')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
+@click.option('--kdu', default=None, help='KDU name (whose status will be shown)')
 @click.pass_context
 @click.pass_context
-def vnf_show(ctx, name, literal, filter):
+def vnf_show(ctx, name, literal, filter, kdu):
     """shows the info of a VNF instance
 
     NAME: name or ID of the VNF instance
     """
     """shows the info of a VNF instance
 
     NAME: name or ID of the VNF instance
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.vnf.get(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    def print_kdu_status(op_info_status):
+        """print KDU status properly formatted
+        """
+        try:
+            op_status = yaml.safe_load(op_info_status)
+            if "namespace" in op_status and "info" in op_status and \
+            "last_deployed" in op_status["info"] and "status" in op_status["info"] and \
+            "code" in op_status["info"]["status"] and "resources" in op_status["info"]["status"] and \
+            "seconds" in op_status["info"]["last_deployed"]:
+                last_deployed_time = datetime.fromtimestamp(op_status["info"]["last_deployed"]["seconds"]).strftime("%a %b %d %I:%M:%S %Y")
+                print("LAST DEPLOYED: {}".format(last_deployed_time))
+                print("NAMESPACE: {}".format(op_status["namespace"]))
+                status_code = "UNKNOWN"
+                if op_status["info"]["status"]["code"]==1:
+                    status_code = "DEPLOYED"
+                print("STATUS: {}".format(status_code))
+                print()
+                print("RESOURCES:")
+                print(op_status["info"]["status"]["resources"])
+                if "notes" in op_status["info"]["status"]:
+                    print("NOTES:")
+                    print(op_status["info"]["status"]["notes"])
+            else:
+                print(op_info_status)
+        except Exception:
+            print(op_info_status)
+
+    logger.debug("")
+    if kdu:
+        if literal:
+            raise ClientException('"--literal" option is incompatible with "--kdu" option')
+        if filter:
+            raise ClientException('"--filter" option is incompatible with "--kdu" option')
+
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.vnf.get(name)
+
+    if kdu:
+        ns_id = resp['nsr-id-ref']
+        op_data={}
+        op_data['member_vnf_index'] = resp['member-vnf-index-ref']
+        op_data['kdu_name'] = kdu
+        op_data['primitive'] = 'status'
+        op_data['primitive_params'] = {}
+        op_id = ctx.obj.ns.exec_op(ns_id, op_name='action', op_data=op_data, wait=False)
+        t = 0
+        while t<30:
+            op_info = ctx.obj.ns.get_op(op_id)
+            if op_info['operationState'] == 'COMPLETED':
+                print_kdu_status(op_info['detailed-status'])
+                return
+            time.sleep(5)
+            t += 5
+        print ("Could not determine KDU status")
+        return
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(resp))
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
         return
 
     table = PrettyTable(['field', 'value'])
+
     for k, v in list(resp.items()):
     for k, v in list(resp.items()):
-        if filter is None or filter in k:
-            table.add_row([k, json.dumps(v, indent=2)])
+        if not filter or k in filter:
+            table.add_row([k, wrap_text(text=json.dumps(v,indent=2),width=100)])
     table.align = 'l'
     print(table)
     table.align = 'l'
     print(table)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-#@cli.command(name='vnf-monitoring-show')
+#@cli_osm.command(name='vnf-monitoring-show')
 #@click.argument('vnf_name')
 #@click.pass_context
 #def vnf_monitoring_show(ctx, vnf_name):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        resp = ctx.obj.vnf.get_monitoring(vnf_name)
 #@click.argument('vnf_name')
 #@click.pass_context
 #def vnf_monitoring_show(ctx, vnf_name):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        resp = ctx.obj.vnf.get_monitoring(vnf_name)
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 #
 #    table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
 #        exit(1)
 #
 #    table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
@@ -819,15 +1391,15 @@ def vnf_show(ctx, name, literal, filter):
 #    print(table)
 
 
 #    print(table)
 
 
-#@cli.command(name='ns-monitoring-show')
+#@cli_osm.command(name='ns-monitoring-show')
 #@click.argument('ns_name')
 #@click.pass_context
 #def ns_monitoring_show(ctx, ns_name):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        resp = ctx.obj.ns.get_monitoring(ns_name)
 #@click.argument('ns_name')
 #@click.pass_context
 #def ns_monitoring_show(ctx, ns_name):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        resp = ctx.obj.ns.get_monitoring(ns_name)
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 #
 #    table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
 #        exit(1)
 #
 #    table = PrettyTable(['vnf name', 'monitoring name', 'value', 'units'])
@@ -842,51 +1414,60 @@ def vnf_show(ctx, name, literal, filter):
 #    print(table)
 
 
 #    print(table)
 
 
-@cli.command(name='ns-op-show', short_help='shows the info of a NS operation')
+@cli_osm.command(name='ns-op-show', short_help='shows the info of a NS operation')
 @click.argument('id')
 @click.argument('id')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
 @click.pass_context
 @click.pass_context
-def ns_op_show(ctx, id, filter):
+def ns_op_show(ctx, id, filter, literal):
     """shows the detailed info of a NS operation
 
     ID: operation identifier
     """
     """shows the detailed info of a NS operation
 
     ID: operation identifier
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        op_info = ctx.obj.ns.get_op(id)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    op_info = ctx.obj.ns.get_op(id)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+    if literal:
+        print(yaml.safe_dump(op_info, indent=4, default_flow_style=False))
+        return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(op_info.items()):
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(op_info.items()):
-        if filter is None or filter in k:
-            table.add_row([k, json.dumps(v, indent=2)])
+        if not filter or k in filter:
+            table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
     table.align = 'l'
     print(table)
 
 
 def nst_show(ctx, name, literal):
     table.align = 'l'
     print(table)
 
 
 def nst_show(ctx, name, literal):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.nst.get(name)
-        #resp = ctx.obj.nst.get_individual(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.nst.get(name)
+    #resp = ctx.obj.nst.get_individual(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(resp))
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(resp.items()):
         return
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(resp.items()):
-        table.add_row([k, json.dumps(v, indent=2)])
+        table.add_row([k, wrap_text(json.dumps(v, indent=2), 100)])
     table.align = 'l'
     print(table)
 
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='nst-show', short_help='shows the content of a Network Slice Template (NST)')
+@cli_osm.command(name='nst-show', short_help='shows the content of a Network Slice Template (NST)')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -896,10 +1477,11 @@ def nst_show1(ctx, name, literal):
 
     NAME: name or ID of the NST
     """
 
     NAME: name or ID of the NST
     """
+    logger.debug("")
     nst_show(ctx, name, literal)
 
 
     nst_show(ctx, name, literal)
 
 
-@cli.command(name='netslice-template-show', short_help='shows the content of a Network Slice Template (NST)')
+@cli_osm.command(name='netslice-template-show', short_help='shows the content of a Network Slice Template (NST)')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
@@ -909,125 +1491,138 @@ def nst_show2(ctx, name, literal):
 
     NAME: name or ID of the NST
     """
 
     NAME: name or ID of the NST
     """
+    logger.debug("")
     nst_show(ctx, name, literal)
 
 
 def nsi_show(ctx, name, literal, filter):
     nst_show(ctx, name, literal)
 
 
 def nsi_show(ctx, name, literal, filter):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        nsi = ctx.obj.nsi.get(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    nsi = ctx.obj.nsi.get(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(nsi))
+        print(yaml.safe_dump(nsi, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
 
     for k, v in list(nsi.items()):
         return
 
     table = PrettyTable(['field', 'value'])
 
     for k, v in list(nsi.items()):
-        if filter is None or filter in k:
+        if not filter or k in filter:
             table.add_row([k, json.dumps(v, indent=2)])
 
     table.align = 'l'
     print(table)
 
 
             table.add_row([k, json.dumps(v, indent=2)])
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='nsi-show', short_help='shows the content of a Network Slice Instance (NSI)')
+@cli_osm.command(name='nsi-show', short_help='shows the content of a Network Slice Instance (NSI)')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 def nsi_show1(ctx, name, literal, filter):
     """shows the content of a Network Slice Instance (NSI)
 
     NAME: name or ID of the Network Slice Instance
     """
 @click.pass_context
 def nsi_show1(ctx, name, literal, filter):
     """shows the content of a Network Slice Instance (NSI)
 
     NAME: name or ID of the Network Slice Instance
     """
+    logger.debug("")
     nsi_show(ctx, name, literal, filter)
 
 
     nsi_show(ctx, name, literal, filter)
 
 
-@cli.command(name='netslice-instance-show', short_help='shows the content of a Network Slice Instance (NSI)')
+@cli_osm.command(name='netslice-instance-show', short_help='shows the content of a Network Slice Instance (NSI)')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 def nsi_show2(ctx, name, literal, filter):
     """shows the content of a Network Slice Instance (NSI)
 
     NAME: name or ID of the Network Slice Instance
     """
 @click.pass_context
 def nsi_show2(ctx, name, literal, filter):
     """shows the content of a Network Slice Instance (NSI)
 
     NAME: name or ID of the Network Slice Instance
     """
+    logger.debug("")
     nsi_show(ctx, name, literal, filter)
 
 
 def nsi_op_show(ctx, id, filter):
     nsi_show(ctx, name, literal, filter)
 
 
 def nsi_op_show(ctx, id, filter):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        op_info = ctx.obj.nsi.get_op(id)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    op_info = ctx.obj.nsi.get_op(id)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(op_info.items()):
 
     table = PrettyTable(['field', 'value'])
     for k, v in list(op_info.items()):
-        if filter is None or filter in k:
+        if not filter or k in filter:
             table.add_row([k, json.dumps(v, indent=2)])
     table.align = 'l'
     print(table)
 
 
             table.add_row([k, json.dumps(v, indent=2)])
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='nsi-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
+@cli_osm.command(name='nsi-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
 @click.argument('id')
 @click.argument('id')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 def nsi_op_show1(ctx, id, filter):
     """shows the info of an operation over a Network Slice Instance(NSI)
 
     ID: operation identifier
     """
 @click.pass_context
 def nsi_op_show1(ctx, id, filter):
     """shows the info of an operation over a Network Slice Instance(NSI)
 
     ID: operation identifier
     """
+    logger.debug("")
     nsi_op_show(ctx, id, filter)
 
 
     nsi_op_show(ctx, id, filter)
 
 
-@cli.command(name='netslice-instance-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
+@cli_osm.command(name='netslice-instance-op-show', short_help='shows the info of an operation over a Network Slice Instance(NSI)')
 @click.argument('id')
 @click.argument('id')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 def nsi_op_show2(ctx, id, filter):
     """shows the info of an operation over a Network Slice Instance(NSI)
 
     ID: operation identifier
     """
 @click.pass_context
 def nsi_op_show2(ctx, id, filter):
     """shows the info of an operation over a Network Slice Instance(NSI)
 
     ID: operation identifier
     """
+    logger.debug("")
     nsi_op_show(ctx, id, filter)
 
 
     nsi_op_show(ctx, id, filter)
 
 
-@cli.command(name='pdu-show', short_help='shows the content of a Physical Deployment Unit (PDU)')
+@cli_osm.command(name='pdu-show', short_help='shows the content of a Physical Deployment Unit (PDU)')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
 @click.argument('name')
 @click.option('--literal', is_flag=True,
               help='print literally, no pretty table')
-@click.option('--filter', default=None)
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 def pdu_show(ctx, name, literal, filter):
     """shows the content of a Physical Deployment Unit (PDU)
 
     NAME: name or ID of the PDU
     """
 @click.pass_context
 def pdu_show(ctx, name, literal, filter):
     """shows the content of a Physical Deployment Unit (PDU)
 
     NAME: name or ID of the PDU
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        pdu = ctx.obj.pdu.get(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    pdu = ctx.obj.pdu.get(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     if literal:
 
     if literal:
-        print(yaml.safe_dump(pdu))
+        print(yaml.safe_dump(pdu, indent=4, default_flow_style=False))
         return
 
     table = PrettyTable(['field', 'value'])
 
     for k, v in list(pdu.items()):
         return
 
     table = PrettyTable(['field', 'value'])
 
     for k, v in list(pdu.items()):
-        if filter is None or filter in k:
+        if not filter or k in filter:
             table.add_row([k, json.dumps(v, indent=2)])
 
     table.align = 'l'
             table.add_row([k, json.dumps(v, indent=2)])
 
     table.align = 'l'
@@ -1038,90 +1633,199 @@ def pdu_show(ctx, name, literal, filter):
 # CREATE operations
 ####################
 
 # CREATE operations
 ####################
 
-def nsd_create(ctx, filename, overwrite):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.nsd.create(filename, overwrite)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+def nsd_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if repo:
+        filename = ctx.obj.osmrepo.get_pkg('ns', filename, repo, vendor, version)
+    ctx.obj.nsd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nsd-create', short_help='creates a new NSD/NSpkg')
+@cli_osm.command(name='nsd-create', short_help='creates a new NSD/NSpkg')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in NSD')
+@click.option('--overwrite', 'overwrite', default=None,  # hidden=True,
+              help='Deprecated. Use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='The charm will not be compiled, it is assumed to already exist')
+@click.option('--repo', default=None,
+              help='[repository]: Repository name')
+@click.option('--vendor', default=None,
+              help='[repository]: filter by vendor]')
+@click.option('--version', default='latest',
+              help='[repository]: filter by version. Default: latest')
 @click.pass_context
 @click.pass_context
-def nsd_create1(ctx, filename, overwrite):
-    """creates a new NSD/NSpkg
+def nsd_create1(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
+    """onboards a new NSpkg (alias of nspkg-create) (TO BE DEPRECATED)
 
 
-    FILENAME: NSD yaml file or NSpkg tar.gz file
+    \b
+    FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
+              If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
+              If FILENAME is an NF Package folder, it is built and then onboarded.
     """
     """
-    nsd_create(ctx, filename, overwrite)
+    logger.debug("")
+    nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, repo=repo, vendor=vendor,
+               version=version)
 
 
 
 
-@cli.command(name='nspkg-create', short_help='creates a new NSD/NSpkg')
+@cli_osm.command(name='nspkg-create', short_help='creates a new NSD/NSpkg')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in NSD')
+@click.option('--overwrite', 'overwrite', default=None,  # hidden=True,
+              help='Deprecated. Use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='The charm will not be compiled, it is assumed to already exist')
+@click.option('--repo', default=None,
+              help='[repository]: Repository name')
+@click.option('--vendor', default=None,
+              help='[repository]: filter by vendor]')
+@click.option('--version', default='latest',
+              help='[repository]: filter by version. Default: latest')
 @click.pass_context
 @click.pass_context
-def nsd_create2(ctx, filename, overwrite):
-    """creates a new NSD/NSpkg
-
-    FILENAME: NSD yaml file or NSpkg tar.gz file
+def nsd_pkg_create(ctx, filename, overwrite, skip_charm_build, repo, vendor, version):
+    """onboards a new NSpkg
+    \b
+    FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
+              If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
+              If FILENAME is an NF Package folder, it is built and then onboarded.
     """
     """
-    nsd_create(ctx, filename, overwrite)
-
-
-def vnfd_create(ctx, filename, overwrite):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.vnfd.create(filename, overwrite)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-@cli.command(name='vnfd-create', short_help='creates a new VNFD/VNFpkg')
+    logger.debug("")
+    nsd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build, repo=repo, vendor=vendor,
+               version=version)
+
+
+def vnfd_create(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt,
+                repo, vendor, version):
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if repo:
+        filename = ctx.obj.osmrepo.get_pkg('vnf', filename, repo, vendor, version)
+    ctx.obj.vnfd.create(filename, overwrite=overwrite, skip_charm_build=skip_charm_build,
+                        override_epa=override_epa, override_nonepa=override_nonepa,
+                        override_paravirt=override_paravirt)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='vnfd-create', short_help='creates a new VNFD/VNFpkg')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in VNFD')
+@click.option('--overwrite', 'overwrite', default=None,
+              help='overwrite deprecated, use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='The charm will not be compiled, it is assumed to already exist')
+@click.option('--override-epa', required=False, default=False, is_flag=True,
+              help='adds guest-epa parameters to all VDU')
+@click.option('--override-nonepa', required=False, default=False, is_flag=True,
+              help='removes all guest-epa parameters from all VDU')
+@click.option('--override-paravirt', required=False, default=False, is_flag=True,
+              help='overrides all VDU interfaces to PARAVIRT')
+@click.option('--repo', default=None,
+              help='[repository]: Repository name')
+@click.option('--vendor', default=None,
+              help='[repository]: filter by vendor]')
+@click.option('--version', default='latest',
+              help='[repository]: filter by version. Default: latest')
 @click.pass_context
 @click.pass_context
-def vnfd_create1(ctx, filename, overwrite):
+def vnfd_create1(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt,
+                 repo,vendor, version):
     """creates a new VNFD/VNFpkg
     """creates a new VNFD/VNFpkg
-
-    FILENAME: VNFD yaml file or VNFpkg tar.gz file
+    \b
+    FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
+              If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
+              If FILENAME is an NF Package folder, it is built and then onboarded.
     """
     """
-    vnfd_create(ctx, filename, overwrite)
+    logger.debug("")
+    vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build,
+                override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt,
+                repo=repo, vendor=vendor, version=version)
 
 
 
 
-@cli.command(name='vnfpkg-create', short_help='creates a new VNFD/VNFpkg')
+@cli_osm.command(name='vnfpkg-create', short_help='creates a new VNFD/VNFpkg')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in VNFD')
+@click.option('--overwrite', 'overwrite', default=None,  # hidden=True,
+              help='Deprecated. Use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='The charm will not be compiled, it is assumed to already exist')
+@click.option('--override-epa', required=False, default=False, is_flag=True,
+              help='adds guest-epa parameters to all VDU')
+@click.option('--override-nonepa', required=False, default=False, is_flag=True,
+              help='removes all guest-epa parameters from all VDU')
+@click.option('--override-paravirt', required=False, default=False, is_flag=True,
+              help='overrides all VDU interfaces to PARAVIRT')
+@click.option('--repo', default=None,
+              help='[repository]: Repository name')
+@click.option('--vendor', default=None,
+              help='[repository]: filter by vendor]')
+@click.option('--version', default='latest',
+              help='[repository]: filter by version. Default: latest')
 @click.pass_context
 @click.pass_context
-def vnfd_create2(ctx, filename, overwrite):
+def vnfd_create2(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt,
+                 repo, vendor, version):
     """creates a new VNFD/VNFpkg
     """creates a new VNFD/VNFpkg
-
-    FILENAME: VNFD yaml file or VNFpkg tar.gz file
+    \b
+    FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
+              If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
+              If FILENAME is an NF Package folder, it is built and then onboarded.
     """
     """
-    vnfd_create(ctx, filename, overwrite)
+    logger.debug("")
+    vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build,
+                override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt,
+                repo=repo, vendor=vendor, version=version)
 
 
-
-@cli.command(name='nfpkg-create', short_help='creates a new NFpkg')
+@cli_osm.command(name='nfpkg-create', short_help='creates a new NFpkg')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in NFD')
+@click.option('--overwrite', 'overwrite', default=None,  # hidden=True,
+              help='Deprecated. Use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='The charm will not be compiled, it is assumed to already exist')
+@click.option('--override-epa', required=False, default=False, is_flag=True,
+              help='adds guest-epa parameters to all VDU')
+@click.option('--override-nonepa', required=False, default=False, is_flag=True,
+              help='removes all guest-epa parameters from all VDU')
+@click.option('--override-paravirt', required=False, default=False, is_flag=True,
+              help='overrides all VDU interfaces to PARAVIRT')
+@click.option('--repo', default=None,
+              help='[repository]: Repository name')
+@click.option('--vendor', default=None,
+              help='[repository]: filter by vendor]')
+@click.option('--version', default='latest',
+              help='[repository]: filter by version. Default: latest')
 @click.pass_context
 @click.pass_context
-def nfpkg_create(ctx, filename, overwrite):
+def nfpkg_create(ctx, filename, overwrite, skip_charm_build, override_epa, override_nonepa, override_paravirt,
+                 repo, vendor, version):
     """creates a new NFpkg
 
     """creates a new NFpkg
 
-    FILENAME: NF Descriptor yaml file or NFpkg tar.gz file
+    \b
+    FILENAME: NF Package tar.gz file, NF Descriptor YAML file or NF Package folder
+              If FILENAME is a file (NF Package tar.gz or NF Descriptor YAML), it is onboarded.
+              If FILENAME is an NF Package folder, it is built and then onboarded.
     """
     """
-    vnfd_create(ctx, filename, overwrite)
+    logger.debug("")
+    vnfd_create(ctx, filename, overwrite=overwrite, skip_charm_build=skip_charm_build,
+                override_epa=override_epa, override_nonepa=override_nonepa, override_paravirt=override_paravirt,
+                repo=repo, vendor=vendor, version=version)
 
 
 
 
-@cli.command(name='ns-create', short_help='creates a new Network Service instance')
+@cli_osm.command(name='ns-create', short_help='creates a new Network Service instance')
 @click.option('--ns_name',
               prompt=True, help='name of the NS instance')
 @click.option('--nsd_name',
 @click.option('--ns_name',
               prompt=True, help='name of the NS instance')
 @click.option('--nsd_name',
@@ -1144,8 +1848,8 @@ def nfpkg_create(ctx, filename, overwrite):
               required=False,
               default=False,
               is_flag=True,
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def ns_create(ctx,
               nsd_name,
 @click.pass_context
 def ns_create(ctx,
               nsd_name,
@@ -1157,77 +1861,88 @@ def ns_create(ctx,
               config_file,
               wait):
     """creates a new NS instance"""
               config_file,
               wait):
     """creates a new NS instance"""
-    try:
-        if config_file:
-            check_client_version(ctx.obj, '--config_file')
-            if config:
-                raise ClientException('"--config" option is incompatible with "--config_file" option')
-            with open(config_file, 'r') as cf:
-                config=cf.read()
-        ctx.obj.ns.create(
-            nsd_name,
-            ns_name,
-            config=config,
-            ssh_keys=ssh_keys,
-            account=vim_account,
-            wait=wait)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    if config_file:
+        check_client_version(ctx.obj, '--config_file')
+        if config:
+            raise ClientException('"--config" option is incompatible with "--config_file" option')
+        with open(config_file, 'r') as cf:
+            config=cf.read()
+    ctx.obj.ns.create(
+        nsd_name,
+        ns_name,
+        config=config,
+        ssh_keys=ssh_keys,
+        account=vim_account,
+        wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 def nst_create(ctx, filename, overwrite):
 
 
 def nst_create(ctx, filename, overwrite):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.nst.create(filename, overwrite)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.nst.create(filename, overwrite)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nst-create', short_help='creates a new Network Slice Template (NST)')
+@cli_osm.command(name='nst-create', short_help='creates a new Network Slice Template (NST)')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in NST')
+@click.option('--overwrite', 'overwrite', default=None,  # hidden=True,
+              help='Deprecated. Use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
 @click.pass_context
 def nst_create1(ctx, filename, overwrite):
     """creates a new Network Slice Template (NST)
 
 @click.pass_context
 def nst_create1(ctx, filename, overwrite):
     """creates a new Network Slice Template (NST)
 
-    FILENAME: NST yaml file or NSTpkg tar.gz file
+    FILENAME: NST package folder, NST yaml file or NSTpkg tar.gz file
     """
     """
+    logger.debug("")
     nst_create(ctx, filename, overwrite)
 
 
     nst_create(ctx, filename, overwrite)
 
 
-@cli.command(name='netslice-template-create', short_help='creates a new Network Slice Template (NST)')
+@cli_osm.command(name='netslice-template-create', short_help='creates a new Network Slice Template (NST)')
 @click.argument('filename')
 @click.argument('filename')
-@click.option('--overwrite', default=None,
-              help='overwrites some fields in NST')
+@click.option('--overwrite', 'overwrite', default=None,  # hidden=True,
+              help='Deprecated. Use override')
+@click.option('--override', 'overwrite', default=None,
+              help='overrides fields in descriptor, format: '
+                   '"key1.key2...=value[;key3...=value;...]"')
 @click.pass_context
 def nst_create2(ctx, filename, overwrite):
     """creates a new Network Slice Template (NST)
 
     FILENAME: NST yaml file or NSTpkg tar.gz file
     """
 @click.pass_context
 def nst_create2(ctx, filename, overwrite):
     """creates a new Network Slice Template (NST)
 
     FILENAME: NST yaml file or NSTpkg tar.gz file
     """
+    logger.debug("")
     nst_create(ctx, filename, overwrite)
 
 
 def nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
     """creates a new Network Slice Instance (NSI)"""
     nst_create(ctx, filename, overwrite)
 
 
 def nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
     """creates a new Network Slice Instance (NSI)"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        if config_file:
-            if config:
-                raise ClientException('"--config" option is incompatible with "--config_file" option')
-            with open(config_file, 'r') as cf:
-                config=cf.read()
-        ctx.obj.nsi.create(nst_name, nsi_name, config=config, ssh_keys=ssh_keys,
-                           account=vim_account, wait=wait)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
-
-
-@cli.command(name='nsi-create', short_help='creates a new Network Slice Instance')
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if config_file:
+        if config:
+            raise ClientException('"--config" option is incompatible with "--config_file" option')
+        with open(config_file, 'r') as cf:
+            config=cf.read()
+    ctx.obj.nsi.create(nst_name, nsi_name, config=config, ssh_keys=ssh_keys,
+                       account=vim_account, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='nsi-create', short_help='creates a new Network Slice Instance')
 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
@@ -1251,15 +1966,16 @@ def nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_fi
               required=False,
               default=False,
               is_flag=True,
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def nsi_create1(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
     """creates a new Network Slice Instance (NSI)"""
 @click.pass_context
 def nsi_create1(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
     """creates a new Network Slice Instance (NSI)"""
+    logger.debug("")
     nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait)
 
 
     nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait)
 
 
-@cli.command(name='netslice-instance-create', short_help='creates a new Network Slice Instance')
+@cli_osm.command(name='netslice-instance-create', short_help='creates a new Network Slice Instance')
 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
 @click.option('--nsi_name', prompt=True, help='name of the Network Slice Instance')
 @click.option('--nst_name', prompt=True, help='name of the Network Slice Template')
 @click.option('--vim_account', prompt=True, help='default VIM account id or name for the deployment')
@@ -1281,15 +1997,16 @@ def nsi_create1(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_f
               required=False,
               default=False,
               is_flag=True,
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def nsi_create2(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
     """creates a new Network Slice Instance (NSI)"""
 @click.pass_context
 def nsi_create2(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait):
     """creates a new Network Slice Instance (NSI)"""
+    logger.debug("")
     nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait)
 
 
     nsi_create(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_file, wait=wait)
 
 
-@cli.command(name='pdu-create', short_help='adds a new Physical Deployment Unit to the catalog')
+@cli_osm.command(name='pdu-create', short_help='adds a new Physical Deployment Unit to the catalog')
 @click.option('--name', help='name of the Physical Deployment Unit')
 @click.option('--pdu_type', help='type of PDU (e.g. router, firewall, FW001)')
 @click.option('--interface',
 @click.option('--name', help='name of the Physical Deployment Unit')
 @click.option('--pdu_type', help='type of PDU (e.g. router, firewall, FW001)')
 @click.option('--interface',
@@ -1298,55 +2015,59 @@ def nsi_create2(ctx, nst_name, nsi_name, vim_account, ssh_keys, config, config_f
               multiple=True)
 @click.option('--description', help='human readable description')
 @click.option('--vim_account', help='list of VIM accounts (in the same VIM) that can reach this PDU', multiple=True)
               multiple=True)
 @click.option('--description', help='human readable description')
 @click.option('--vim_account', help='list of VIM accounts (in the same VIM) that can reach this PDU', multiple=True)
-@click.option('--descriptor_file', default=None, help='PDU descriptor file (as an alternative to using the other arguments')
+@click.option('--descriptor_file', default=None,
+              help='PDU descriptor file (as an alternative to using the other arguments')
 @click.pass_context
 def pdu_create(ctx, name, pdu_type, interface, description, vim_account, descriptor_file):
     """creates a new Physical Deployment Unit (PDU)"""
 @click.pass_context
 def pdu_create(ctx, name, pdu_type, interface, description, vim_account, descriptor_file):
     """creates a new Physical Deployment Unit (PDU)"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        pdu = {}
-        if not descriptor_file:
-            if not name:
-                raise ClientException('in absence of descriptor file, option "--name" is mandatory')
-            if not pdu_type:
-                raise ClientException('in absence of descriptor file, option "--pdu_type" is mandatory')
-            if not interface:
-                raise ClientException('in absence of descriptor file, option "--interface" is mandatory (at least once)')
-            if not vim_account:
-                raise ClientException('in absence of descriptor file, option "--vim_account" is mandatory (at least once)')
-        else:
-            with open(descriptor_file, 'r') as df:
-                pdu = yaml.load(df.read())
-        if name: pdu["name"] = name
-        if pdu_type: pdu["type"] = pdu_type
-        if description: pdu["description"] = description
-        if vim_account: pdu["vim_accounts"] = vim_account
-        if interface:
-            ifaces_list = []
-            for iface in interface:
-                new_iface={k:v for k,v in [i.split('=') for i in iface.split(',')]}
-                new_iface["mgmt"] = (new_iface.get("mgmt","false").lower() == "true")
-                ifaces_list.append(new_iface)
-            pdu["interfaces"] = ifaces_list
-        ctx.obj.pdu.create(pdu)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    pdu = {}
+    if not descriptor_file:
+        if not name:
+            raise ClientException('in absence of descriptor file, option "--name" is mandatory')
+        if not pdu_type:
+            raise ClientException('in absence of descriptor file, option "--pdu_type" is mandatory')
+        if not interface:
+            raise ClientException('in absence of descriptor file, option "--interface" is mandatory (at least once)')
+        if not vim_account:
+            raise ClientException('in absence of descriptor file, option "--vim_account" is mandatory (at least once)')
+    else:
+        with open(descriptor_file, 'r') as df:
+            pdu = yaml.safe_load(df.read())
+    if name: pdu["name"] = name
+    if pdu_type: pdu["type"] = pdu_type
+    if description: pdu["description"] = description
+    if vim_account: pdu["vim_accounts"] = vim_account
+    if interface:
+        ifaces_list = []
+        for iface in interface:
+            new_iface={k:v for k,v in [i.split('=') for i in iface.split(',')]}
+            new_iface["mgmt"] = (new_iface.get("mgmt","false").lower() == "true")
+            ifaces_list.append(new_iface)
+        pdu["interfaces"] = ifaces_list
+    ctx.obj.pdu.create(pdu)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
 
 ####################
 # UPDATE operations
 ####################
 
 def nsd_update(ctx, name, content):
 
 ####################
 # UPDATE operations
 ####################
 
 def nsd_update(ctx, name, content):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.nsd.update(name, content)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.nsd.update(name, content)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nsd-update', short_help='updates a NSD/NSpkg')
+@cli_osm.command(name='nsd-update', short_help='updates a NSD/NSpkg')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NSD/NSpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NSD/NSpkg replacing the current one')
@@ -1356,10 +2077,11 @@ def nsd_update1(ctx, name, content):
 
     NAME: name or ID of the NSD/NSpkg
     """
 
     NAME: name or ID of the NSD/NSpkg
     """
+    logger.debug("")
     nsd_update(ctx, name, content)
 
 
     nsd_update(ctx, name, content)
 
 
-@cli.command(name='nspkg-update', short_help='updates a NSD/NSpkg')
+@cli_osm.command(name='nspkg-update', short_help='updates a NSD/NSpkg')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NSD/NSpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NSD/NSpkg replacing the current one')
@@ -1369,19 +2091,21 @@ def nsd_update2(ctx, name, content):
 
     NAME: name or ID of the NSD/NSpkg
     """
 
     NAME: name or ID of the NSD/NSpkg
     """
+    logger.debug("")
     nsd_update(ctx, name, content)
 
 
 def vnfd_update(ctx, name, content):
     nsd_update(ctx, name, content)
 
 
 def vnfd_update(ctx, name, content):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.vnfd.update(name, content)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.vnfd.update(name, content)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='vnfd-update', short_help='updates a new VNFD/VNFpkg')
+@cli_osm.command(name='vnfd-update', short_help='updates a new VNFD/VNFpkg')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the VNFD/VNFpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the VNFD/VNFpkg replacing the current one')
@@ -1391,10 +2115,11 @@ def vnfd_update1(ctx, name, content):
 
     NAME: name or ID of the VNFD/VNFpkg
     """
 
     NAME: name or ID of the VNFD/VNFpkg
     """
+    logger.debug("")
     vnfd_update(ctx, name, content)
 
 
     vnfd_update(ctx, name, content)
 
 
-@cli.command(name='vnfpkg-update', short_help='updates a VNFD/VNFpkg')
+@cli_osm.command(name='vnfpkg-update', short_help='updates a VNFD/VNFpkg')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the VNFD/VNFpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the VNFD/VNFpkg replacing the current one')
@@ -1404,10 +2129,11 @@ def vnfd_update2(ctx, name, content):
 
     NAME: VNFD yaml file or VNFpkg tar.gz file
     """
 
     NAME: VNFD yaml file or VNFpkg tar.gz file
     """
+    logger.debug("")
     vnfd_update(ctx, name, content)
 
 
     vnfd_update(ctx, name, content)
 
 
-@cli.command(name='nfpkg-update', short_help='updates a NFpkg')
+@cli_osm.command(name='nfpkg-update', short_help='updates a NFpkg')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NFpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NFpkg replacing the current one')
@@ -1417,19 +2143,21 @@ def nfpkg_update(ctx, name, content):
 
     NAME: NF Descriptor yaml file or NFpkg tar.gz file
     """
 
     NAME: NF Descriptor yaml file or NFpkg tar.gz file
     """
+    logger.debug("")
     vnfd_update(ctx, name, content)
 
 
 def nst_update(ctx, name, content):
     vnfd_update(ctx, name, content)
 
 
 def nst_update(ctx, name, content):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.nst.update(name, content)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.nst.update(name, content)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nst-update', short_help='updates a Network Slice Template (NST)')
+@cli_osm.command(name='nst-update', short_help='updates a Network Slice Template (NST)')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NST/NSTpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NST/NSTpkg replacing the current one')
@@ -1439,10 +2167,11 @@ def nst_update1(ctx, name, content):
 
     NAME: name or ID of the NSD/NSpkg
     """
 
     NAME: name or ID of the NSD/NSpkg
     """
+    logger.debug("")
     nst_update(ctx, name, content)
 
 
     nst_update(ctx, name, content)
 
 
-@cli.command(name='netslice-template-update', short_help='updates a Network Slice Template (NST)')
+@cli_osm.command(name='netslice-template-update', short_help='updates a Network Slice Template (NST)')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NST/NSTpkg replacing the current one')
 @click.argument('name')
 @click.option('--content', default=None,
               help='filename with the NST/NSTpkg replacing the current one')
@@ -1452,6 +2181,7 @@ def nst_update2(ctx, name, content):
 
     NAME: name or ID of the NSD/NSpkg
     """
 
     NAME: name or ID of the NSD/NSpkg
     """
+    logger.debug("")
     nst_update(ctx, name, content)
 
 
     nst_update(ctx, name, content)
 
 
@@ -1460,18 +2190,19 @@ def nst_update2(ctx, name, content):
 ####################
 
 def nsd_delete(ctx, name, force):
 ####################
 
 def nsd_delete(ctx, name, force):
-    try:
-        if not force:
-            ctx.obj.nsd.delete(name)
-        else:
-            check_client_version(ctx.obj, '--force')
-            ctx.obj.nsd.delete(name, force)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    if not force:
+        ctx.obj.nsd.delete(name)
+    else:
+        check_client_version(ctx.obj, '--force')
+        ctx.obj.nsd.delete(name, force)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nsd-delete', short_help='deletes a NSD/NSpkg')
+@cli_osm.command(name='nsd-delete', short_help='deletes a NSD/NSpkg')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1480,10 +2211,11 @@ def nsd_delete1(ctx, name, force):
 
     NAME: name or ID of the NSD/NSpkg to be deleted
     """
 
     NAME: name or ID of the NSD/NSpkg to be deleted
     """
+    logger.debug("")
     nsd_delete(ctx, name, force)
 
 
     nsd_delete(ctx, name, force)
 
 
-@cli.command(name='nspkg-delete', short_help='deletes a NSD/NSpkg')
+@cli_osm.command(name='nspkg-delete', short_help='deletes a NSD/NSpkg')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1492,22 +2224,24 @@ def nsd_delete2(ctx, name, force):
 
     NAME: name or ID of the NSD/NSpkg to be deleted
     """
 
     NAME: name or ID of the NSD/NSpkg to be deleted
     """
+    logger.debug("")
     nsd_delete(ctx, name, force)
 
 
 def vnfd_delete(ctx, name, force):
     nsd_delete(ctx, name, force)
 
 
 def vnfd_delete(ctx, name, force):
-    try:
-        if not force:
-            ctx.obj.vnfd.delete(name)
-        else:
-            check_client_version(ctx.obj, '--force')
-            ctx.obj.vnfd.delete(name, force)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    if not force:
+        ctx.obj.vnfd.delete(name)
+    else:
+        check_client_version(ctx.obj, '--force')
+        ctx.obj.vnfd.delete(name, force)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='vnfd-delete', short_help='deletes a VNFD/VNFpkg')
+@cli_osm.command(name='vnfd-delete', short_help='deletes a VNFD/VNFpkg')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1516,10 +2250,11 @@ def vnfd_delete1(ctx, name, force):
 
     NAME: name or ID of the VNFD/VNFpkg to be deleted
     """
 
     NAME: name or ID of the VNFD/VNFpkg to be deleted
     """
+    logger.debug("")
     vnfd_delete(ctx, name, force)
 
 
     vnfd_delete(ctx, name, force)
 
 
-@cli.command(name='vnfpkg-delete', short_help='deletes a VNFD/VNFpkg')
+@cli_osm.command(name='vnfpkg-delete', short_help='deletes a VNFD/VNFpkg')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1528,10 +2263,11 @@ def vnfd_delete2(ctx, name, force):
 
     NAME: name or ID of the VNFD/VNFpkg to be deleted
     """
 
     NAME: name or ID of the VNFD/VNFpkg to be deleted
     """
+    logger.debug("")
     vnfd_delete(ctx, name, force)
 
 
     vnfd_delete(ctx, name, force)
 
 
-@cli.command(name='nfpkg-delete', short_help='deletes a NFpkg')
+@cli_osm.command(name='nfpkg-delete', short_help='deletes a NFpkg')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1540,45 +2276,51 @@ def nfpkg_delete(ctx, name, force):
 
     NAME: name or ID of the NFpkg to be deleted
     """
 
     NAME: name or ID of the NFpkg to be deleted
     """
+    logger.debug("")
     vnfd_delete(ctx, name, force)
 
 
     vnfd_delete(ctx, name, force)
 
 
-@cli.command(name='ns-delete', short_help='deletes a NS instance')
+@cli_osm.command(name='ns-delete', short_help='deletes a NS instance')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
+@click.option('--config', default=None,
+              help="specific yaml configuration for the termination, e.g. '{autoremove: False, timeout_ns_terminate: "
+                   "600, skip_terminate_primitives: True}'")
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 @click.pass_context
-def ns_delete(ctx, name, force, wait):
+def ns_delete(ctx, name, force, config, wait):
     """deletes a NS instance
 
     NAME: name or ID of the NS instance to be deleted
     """
     """deletes a NS instance
 
     NAME: name or ID of the NS instance to be deleted
     """
-    try:
-        if not force:
-            ctx.obj.ns.delete(name, wait=wait)
-        else:
-            check_client_version(ctx.obj, '--force')
-            ctx.obj.ns.delete(name, force, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    if not force:
+        ctx.obj.ns.delete(name, config=config, wait=wait)
+    else:
+        check_client_version(ctx.obj, '--force')
+        ctx.obj.ns.delete(name, force, config=config, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 def nst_delete(ctx, name, force):
 
 
 def nst_delete(ctx, name, force):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.nst.delete(name, force)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.nst.delete(name, force)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nst-delete', short_help='deletes a Network Slice Template (NST)')
+@cli_osm.command(name='nst-delete', short_help='deletes a Network Slice Template (NST)')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1587,10 +2329,11 @@ def nst_delete1(ctx, name, force):
 
     NAME: name or ID of the NST/NSTpkg to be deleted
     """
 
     NAME: name or ID of the NST/NSTpkg to be deleted
     """
+    logger.debug("")
     nst_delete(ctx, name, force)
 
 
     nst_delete(ctx, name, force)
 
 
-@cli.command(name='netslice-template-delete', short_help='deletes a Network Slice Template (NST)')
+@cli_osm.command(name='netslice-template-delete', short_help='deletes a Network Slice Template (NST)')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1599,37 +2342,40 @@ def nst_delete2(ctx, name, force):
 
     NAME: name or ID of the NST/NSTpkg to be deleted
     """
 
     NAME: name or ID of the NST/NSTpkg to be deleted
     """
+    logger.debug("")
     nst_delete(ctx, name, force)
 
 
 def nsi_delete(ctx, name, force, wait):
     nst_delete(ctx, name, force)
 
 
 def nsi_delete(ctx, name, force, wait):
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.nsi.delete(name, force, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.nsi.delete(name, force, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='nsi-delete', short_help='deletes a Network Slice Instance (NSI)')
+@cli_osm.command(name='nsi-delete', short_help='deletes a Network Slice Instance (NSI)')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def nsi_delete1(ctx, name, force, wait):
     """deletes a Network Slice Instance (NSI)
 
     NAME: name or ID of the Network Slice instance to be deleted
     """
 @click.pass_context
 def nsi_delete1(ctx, name, force, wait):
     """deletes a Network Slice Instance (NSI)
 
     NAME: name or ID of the Network Slice instance to be deleted
     """
+    logger.debug("")
     nsi_delete(ctx, name, force, wait=wait)
 
 
     nsi_delete(ctx, name, force, wait=wait)
 
 
-@cli.command(name='netslice-instance-delete', short_help='deletes a Network Slice Instance (NSI)')
+@cli_osm.command(name='netslice-instance-delete', short_help='deletes a Network Slice Instance (NSI)')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1638,10 +2384,11 @@ def nsi_delete2(ctx, name, force, wait):
 
     NAME: name or ID of the Network Slice instance to be deleted
     """
 
     NAME: name or ID of the Network Slice instance to be deleted
     """
+    logger.debug("")
     nsi_delete(ctx, name, force, wait=wait)
 
 
     nsi_delete(ctx, name, force, wait=wait)
 
 
-@cli.command(name='pdu-delete', short_help='deletes a Physical Deployment Unit (PDU)')
+@cli_osm.command(name='pdu-delete', short_help='deletes a Physical Deployment Unit (PDU)')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -1650,19 +2397,20 @@ def pdu_delete(ctx, name, force):
 
     NAME: name or ID of the PDU to be deleted
     """
 
     NAME: name or ID of the PDU to be deleted
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.pdu.delete(name, force)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.pdu.delete(name, force)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 #################
 # VIM operations
 #################
 
 
 
 #################
 # VIM operations
 #################
 
-@cli.command(name='vim-create', short_help='creates a new VIM account')
+@cli_osm.command(name='vim-create', short_help='creates a new VIM account')
 @click.option('--name',
               prompt=True,
               help='Name to create datacenter')
 @click.option('--name',
               prompt=True,
               help='Name to create datacenter')
@@ -1687,7 +2435,7 @@ def pdu_delete(ctx, name, force):
               default='openstack',
               help='VIM type')
 @click.option('--description',
               default='openstack',
               help='VIM type')
 @click.option('--description',
-              default='no description',
+              default=None,
               help='human readable description')
 @click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
               help='human readable description')
 @click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
@@ -1695,8 +2443,8 @@ def pdu_delete(ctx, name, force):
               required=False,
               default=False,
               is_flag=True,
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def vim_create(ctx,
                name,
 @click.pass_context
 def vim_create(ctx,
                name,
@@ -1711,29 +2459,30 @@ def vim_create(ctx,
                sdn_port_mapping,
                wait):
     """creates a new VIM account"""
                sdn_port_mapping,
                wait):
     """creates a new VIM account"""
-    try:
-        if sdn_controller:
-            check_client_version(ctx.obj, '--sdn_controller')
-        if sdn_port_mapping:
-            check_client_version(ctx.obj, '--sdn_port_mapping')
-        vim = {}
-        vim['vim-username'] = user
-        vim['vim-password'] = password
-        vim['vim-url'] = auth_url
-        vim['vim-tenant-name'] = tenant
-        vim['vim-type'] = account_type
-        vim['description'] = description
-        vim['config'] = config
-        if sdn_controller or sdn_port_mapping:
-            ctx.obj.vim.create(name, vim, sdn_controller, sdn_port_mapping, wait=wait)
-        else:
-            ctx.obj.vim.create(name, vim, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    if sdn_controller:
+        check_client_version(ctx.obj, '--sdn_controller')
+    if sdn_port_mapping:
+        check_client_version(ctx.obj, '--sdn_port_mapping')
+    vim = {}
+    vim['vim-username'] = user
+    vim['vim-password'] = password
+    vim['vim-url'] = auth_url
+    vim['vim-tenant-name'] = tenant
+    vim['vim-type'] = account_type
+    vim['description'] = description
+    vim['config'] = config
+    if sdn_controller or sdn_port_mapping:
+        ctx.obj.vim.create(name, vim, sdn_controller, sdn_port_mapping, wait=wait)
+    else:
+        ctx.obj.vim.create(name, vim, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='vim-update', short_help='updates a VIM account')
+@cli_osm.command(name='vim-update', short_help='updates a VIM account')
 @click.argument('name')
 @click.option('--newname', help='New name for the VIM account')
 @click.option('--user', help='VIM username')
 @click.argument('name')
 @click.option('--newname', help='New name for the VIM account')
 @click.option('--user', help='VIM username')
@@ -1743,14 +2492,15 @@ def vim_create(ctx,
 @click.option('--config', help='VIM specific config parameters')
 @click.option('--account_type', help='VIM type')
 @click.option('--description', help='human readable description')
 @click.option('--config', help='VIM specific config parameters')
 @click.option('--account_type', help='VIM type')
 @click.option('--description', help='human readable description')
-@click.option('--sdn_controller', default=None, help='Name or id of the SDN controller associated to this VIM account')
+@click.option('--sdn_controller', default=None, help='Name or id of the SDN controller to be associated with this VIM'
+                                                     'account. Use empty string to disassociate')
 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.option('--sdn_port_mapping', default=None, help="File describing the port mapping between compute nodes' ports and switch ports")
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def vim_update(ctx,
                name,
 @click.pass_context
 def vim_update(ctx,
                name,
@@ -1769,59 +2519,65 @@ def vim_update(ctx,
 
     NAME: name or ID of the VIM account
     """
 
     NAME: name or ID of the VIM account
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        vim = {}
-        if newname: vim['name'] = newname
-        if user: vim['vim_user'] = user
-        if password: vim['vim_password'] = password
-        if auth_url: vim['vim_url'] = auth_url
-        if tenant: vim['vim-tenant-name'] = tenant
-        if account_type: vim['vim_type'] = account_type
-        if description: vim['description'] = description
-        if config: vim['config'] = config
-        ctx.obj.vim.update(name, vim, sdn_controller, sdn_port_mapping, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-@cli.command(name='vim-delete', short_help='deletes a VIM account')
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    vim = {}
+    if newname: vim['name'] = newname
+    if user: vim['vim_user'] = user
+    if password: vim['vim_password'] = password
+    if auth_url: vim['vim_url'] = auth_url
+    if tenant: vim['vim-tenant-name'] = tenant
+    if account_type: vim['vim_type'] = account_type
+    if description: vim['description'] = description
+    if config: vim['config'] = config
+    ctx.obj.vim.update(name, vim, sdn_controller, sdn_port_mapping, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='vim-delete', short_help='deletes a VIM account')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def vim_delete(ctx, name, force, wait):
     """deletes a VIM account
 
     NAME: name or ID of the VIM account to be deleted
     """
 @click.pass_context
 def vim_delete(ctx, name, force, wait):
     """deletes a VIM account
 
     NAME: name or ID of the VIM account to be deleted
     """
-    try:
-        if not force:
-            ctx.obj.vim.delete(name, wait=wait)
-        else:
-            check_client_version(ctx.obj, '--force')
-            ctx.obj.vim.delete(name, force, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    if not force:
+        ctx.obj.vim.delete(name, wait=wait)
+    else:
+        check_client_version(ctx.obj, '--force')
+        ctx.obj.vim.delete(name, force, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='vim-list', short_help='list all VIM accounts')
+@cli_osm.command(name='vim-list', short_help='list all VIM accounts')
 #@click.option('--ro_update/--no_ro_update',
 #              default=False,
 #              help='update list from RO')
 #@click.option('--ro_update/--no_ro_update',
 #              default=False,
 #              help='update list from RO')
-@click.option('--filter', default=None,
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the VIM accounts matching the filter')
               help='restricts the list to the VIM accounts matching the filter')
+@click.option('--long', is_flag=True,
+              help='get more details of the NS (project, vim, deployment status, configuration status.')
 @click.pass_context
 @click.pass_context
-def vim_list(ctx, filter):
+def vim_list(ctx, filter, long):
     """list all VIM accounts"""
     """list all VIM accounts"""
+    logger.debug("")
     if filter:
     if filter:
+        filter='&'.join(filter)
         check_client_version(ctx.obj, '--filter')
 #    if ro_update:
 #        check_client_version(ctx.obj, '--ro_update', 'v1')
         check_client_version(ctx.obj, '--filter')
 #    if ro_update:
 #        check_client_version(ctx.obj, '--ro_update', 'v1')
@@ -1830,32 +2586,54 @@ def vim_list(ctx, filter):
         resp = ctx.obj.vim.list(filter)
 #    else:
 #        resp = ctx.obj.vim.list(ro_update)
         resp = ctx.obj.vim.list(filter)
 #    else:
 #        resp = ctx.obj.vim.list(ro_update)
-    table = PrettyTable(['vim name', 'uuid'])
+    if long:
+        table = PrettyTable(['vim name', 'uuid', 'project', 'operational state', 'error details'])
+        project_list = ctx.obj.project.list()
+    else:
+        table = PrettyTable(['vim name', 'uuid', 'operational state'])
     for vim in resp:
     for vim in resp:
-        table.add_row([vim['name'], vim['uuid']])
+        if long:
+            if 'vim_password' in vim:
+                vim['vim_password']='********'
+            logger.debug('VIM details: {}'.format(yaml.safe_dump(vim)))
+            vim_state = vim['_admin'].get('operationalState', '-')
+            error_details = 'N/A'
+            if vim_state == 'ERROR':
+                error_details = vim['_admin'].get('detailed-status', 'Not found')
+            project_id, project_name = get_project(project_list, vim)
+            #project_info = '{} ({})'.format(project_name, project_id)
+            project_info = project_name
+            table.add_row([vim['name'], vim['uuid'], project_info,
+                          vim_state, wrap_text(text=error_details, width=80)])
+        else:
+            table.add_row([vim['name'], vim['uuid'], vim['_admin'].get('operationalState', '-')])
     table.align = 'l'
     print(table)
 
 
     table.align = 'l'
     print(table)
 
 
-@cli.command(name='vim-show', short_help='shows the details of a VIM account')
+@cli_osm.command(name='vim-show', short_help='shows the details of a VIM account')
 @click.argument('name')
 @click.argument('name')
+@click.option('--filter', multiple=True,
+              help='restricts the information to the fields in the filter')
 @click.pass_context
 @click.pass_context
-def vim_show(ctx, name):
+def vim_show(ctx, name, filter):
     """shows the details of a VIM account
 
     NAME: name or ID of the VIM account
     """
     """shows the details of a VIM account
 
     NAME: name or ID of the VIM account
     """
-    try:
-        resp = ctx.obj.vim.get(name)
-        if 'vim_password' in resp:
-            resp['vim_password']='********'
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    resp = ctx.obj.vim.get(name)
+    if 'vim_password' in resp:
+        resp['vim_password']='********'
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in list(resp.items()):
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in list(resp.items()):
-        table.add_row([k, json.dumps(v, indent=2)])
+        if not filter or k in filter:
+            table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
     table.align = 'l'
     print(table)
 
     table.align = 'l'
     print(table)
 
@@ -1864,7 +2642,7 @@ def vim_show(ctx, name):
 # WIM operations
 ####################
 
 # WIM operations
 ####################
 
-@cli.command(name='wim-create', short_help='creates a new WIM account')
+@cli_osm.command(name='wim-create', short_help='creates a new WIM account')
 @click.option('--name',
               prompt=True,
               help='Name for the WIM account')
 @click.option('--name',
               prompt=True,
               help='Name for the WIM account')
@@ -1883,15 +2661,17 @@ def vim_show(ctx, name):
 @click.option('--wim_type',
               help='WIM type')
 @click.option('--description',
 @click.option('--wim_type',
               help='WIM type')
 @click.option('--description',
-              default='no description',
+              default=None,
               help='human readable description')
               help='human readable description')
-@click.option('--wim_port_mapping', default=None, help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge (WAN service endpoint id and info)")
+@click.option('--wim_port_mapping', default=None,
+              help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
+                   "(WAN service endpoint id and info)")
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it '
+                   'until the operation is completed, or timeout')
 @click.pass_context
 def wim_create(ctx,
                name,
 @click.pass_context
 def wim_create(ctx,
                name,
@@ -1905,27 +2685,28 @@ def wim_create(ctx,
                wim_port_mapping,
                wait):
     """creates a new WIM account"""
                wim_port_mapping,
                wait):
     """creates a new WIM account"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        # if sdn_controller:
-        #     check_client_version(ctx.obj, '--sdn_controller')
-        # if sdn_port_mapping:
-        #     check_client_version(ctx.obj, '--sdn_port_mapping')
-        wim = {}
-        if user: wim['user'] = user
-        if password: wim['password'] = password
-        if url: wim['wim_url'] = url
-        # if tenant: wim['tenant'] = tenant
-        wim['wim_type'] = wim_type
-        if description: wim['description'] = description
-        if config: wim['config'] = config
-        ctx.obj.wim.create(name, wim, wim_port_mapping, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-@cli.command(name='wim-update', short_help='updates a WIM account')
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    # if sdn_controller:
+    #     check_client_version(ctx.obj, '--sdn_controller')
+    # if sdn_port_mapping:
+    #     check_client_version(ctx.obj, '--sdn_port_mapping')
+    wim = {}
+    if user: wim['user'] = user
+    if password: wim['password'] = password
+    if url: wim['wim_url'] = url
+    # if tenant: wim['tenant'] = tenant
+    wim['wim_type'] = wim_type
+    if description: wim['description'] = description
+    if config: wim['config'] = config
+    ctx.obj.wim.create(name, wim, wim_port_mapping, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='wim-update', short_help='updates a WIM account')
 @click.argument('name')
 @click.option('--newname', help='New name for the WIM account')
 @click.option('--user', help='WIM username')
 @click.argument('name')
 @click.option('--newname', help='New name for the WIM account')
 @click.option('--user', help='WIM username')
@@ -1934,13 +2715,14 @@ def wim_create(ctx,
 @click.option('--config', help='WIM specific config parameters')
 @click.option('--wim_type', help='WIM type')
 @click.option('--description', help='human readable description')
 @click.option('--config', help='WIM specific config parameters')
 @click.option('--wim_type', help='WIM type')
 @click.option('--description', help='human readable description')
-@click.option('--wim_port_mapping', default=None, help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge (WAN service endpoint id and info)")
+@click.option('--wim_port_mapping', default=None,
+              help="File describing the port mapping between DC edge (datacenters, switches, ports) and WAN edge "
+                   "(WAN service endpoint id and info)")
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
 @click.pass_context
 def wim_update(ctx,
                name,
 @click.pass_context
 def wim_update(ctx,
                name,
@@ -1957,66 +2739,70 @@ def wim_update(ctx,
 
     NAME: name or ID of the WIM account
     """
 
     NAME: name or ID of the WIM account
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        wim = {}
-        if newname: wim['name'] = newname
-        if user: wim['user'] = user
-        if password: wim['password'] = password
-        if url: wim['url'] = url
-        # if tenant: wim['tenant'] = tenant
-        if wim_type: wim['wim_type'] = wim_type
-        if description: wim['description'] = description
-        if config: wim['config'] = config
-        ctx.obj.wim.update(name, wim, wim_port_mapping, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-@cli.command(name='wim-delete', short_help='deletes a WIM account')
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    wim = {}
+    if newname: wim['name'] = newname
+    if user: wim['user'] = user
+    if password: wim['password'] = password
+    if url: wim['url'] = url
+    # if tenant: wim['tenant'] = tenant
+    if wim_type: wim['wim_type'] = wim_type
+    if description: wim['description'] = description
+    if config: wim['config'] = config
+    ctx.obj.wim.update(name, wim, wim_port_mapping, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='wim-delete', short_help='deletes a WIM account')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
 @click.pass_context
 def wim_delete(ctx, name, force, wait):
     """deletes a WIM account
 
     NAME: name or ID of the WIM account to be deleted
     """
 @click.pass_context
 def wim_delete(ctx, name, force, wait):
     """deletes a WIM account
 
     NAME: name or ID of the WIM account to be deleted
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.wim.delete(name, force, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.wim.delete(name, force, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='wim-list', short_help='list all WIM accounts')
-@click.option('--filter', default=None,
+@cli_osm.command(name='wim-list', short_help='list all WIM accounts')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the WIM accounts matching the filter')
 @click.pass_context
 def wim_list(ctx, filter):
     """list all WIM accounts"""
               help='restricts the list to the WIM accounts matching the filter')
 @click.pass_context
 def wim_list(ctx, filter):
     """list all WIM accounts"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.wim.list(filter)
-        table = PrettyTable(['wim name', 'uuid'])
-        for wim in resp:
-            table.add_row([wim['name'], wim['uuid']])
-        table.align = 'l'
-        print(table)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.wim.list(filter)
+    table = PrettyTable(['wim name', 'uuid'])
+    for wim in resp:
+        table.add_row([wim['name'], wim['uuid']])
+    table.align = 'l'
+    print(table)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='wim-show', short_help='shows the details of a WIM account')
+@cli_osm.command(name='wim-show', short_help='shows the details of a WIM account')
 @click.argument('name')
 @click.pass_context
 def wim_show(ctx, name):
 @click.argument('name')
 @click.pass_context
 def wim_show(ctx, name):
@@ -2024,14 +2810,15 @@ def wim_show(ctx, name):
 
     NAME: name or ID of the WIM account
     """
 
     NAME: name or ID of the WIM account
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.wim.get(name)
-        if 'password' in resp:
-            resp['wim_password']='********'
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.wim.get(name)
+    if 'password' in resp:
+        resp['wim_password']='********'
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in list(resp.items()):
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in list(resp.items()):
@@ -2044,169 +2831,147 @@ def wim_show(ctx, name):
 # SDN controller operations
 ####################
 
 # SDN controller operations
 ####################
 
-@cli.command(name='sdnc-create', short_help='creates a new SDN controller')
+@cli_osm.command(name='sdnc-create', short_help='creates a new SDN controller')
 @click.option('--name',
               prompt=True,
               help='Name to create sdn controller')
 @click.option('--type',
               prompt=True,
               help='SDN controller type')
 @click.option('--name',
               prompt=True,
               help='Name to create sdn controller')
 @click.option('--type',
               prompt=True,
               help='SDN controller type')
-@click.option('--sdn_controller_version',
-              help='SDN controller version')
-@click.option('--ip_address',
-              prompt=True,
-              help='SDN controller IP address')
-@click.option('--port',
-              prompt=True,
-              help='SDN controller port')
-@click.option('--switch_dpid',
-              prompt=True,
-              help='Switch DPID (Openflow Datapath ID)')
+@click.option('--sdn_controller_version',  # hidden=True,
+              help='Deprecated. Use --config {version: sdn_controller_version}')
+@click.option('--url',
+              help='URL in format http[s]://HOST:IP/')
+@click.option('--ip_address',  # hidden=True,
+              help='Deprecated. Use --url')
+@click.option('--port',  # hidden=True,
+              help='Deprecated. Use --url')
+@click.option('--switch_dpid',  # hidden=True,
+              help='Deprecated. Use --config {switch_id: DPID}')
+@click.option('--config',
+              help='Extra information for SDN in yaml format, as {switch_id: identity used for the plugin (e.g. DPID: '
+             'Openflow Datapath ID), version: version}')
 @click.option('--user',
               help='SDN controller username')
 @click.option('--password',
               hide_input=True,
               confirmation_prompt=True,
               help='SDN controller password')
 @click.option('--user',
               help='SDN controller username')
 @click.option('--password',
               hide_input=True,
               confirmation_prompt=True,
               help='SDN controller password')
-#@click.option('--description',
-#              default='no description',
-#              help='human readable description')
+@click.option('--description', default=None, help='human readable description')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
-@click.pass_context
-def sdnc_create(ctx,
-                name,
-                type,
-                sdn_controller_version,
-                ip_address,
-                port,
-                switch_dpid,
-                user,
-                password,
-                wait):
+              help="do not return the control immediately, but keep it until the operation is completed, or timeout")
+@click.pass_context
+def sdnc_create(ctx, **kwargs):
     """creates a new SDN controller"""
     """creates a new SDN controller"""
-    sdncontroller = {}
-    sdncontroller['name'] = name
-    sdncontroller['type'] = type
-    sdncontroller['ip'] = ip_address
-    sdncontroller['port'] = int(port)
-    sdncontroller['dpid'] = switch_dpid
-    if sdn_controller_version:
-        sdncontroller['version'] = sdn_controller_version
-    if user:
-        sdncontroller['user'] = user
-    if password:
-        sdncontroller['password'] = password
-#    sdncontroller['description'] = description
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.sdnc.create(name, sdncontroller, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-@cli.command(name='sdnc-update', short_help='updates an SDN controller')
+    logger.debug("")
+    sdncontroller = {x: kwargs[x] for x in kwargs if kwargs[x] and
+                     x not in ("wait", "ip_address", "port", "switch_dpid")}
+    if kwargs.get("port"):
+        print("option '--port' is deprecated, use '--url' instead")
+        sdncontroller["port"] = int(kwargs["port"])
+    if kwargs.get("ip_address"):
+        print("option '--ip_address' is deprecated, use '--url' instead")
+        sdncontroller["ip"] = kwargs["ip_address"]
+    if kwargs.get("switch_dpid"):
+        print("option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead")
+        sdncontroller["dpid"] = kwargs["switch_dpid"]
+    if kwargs.get("sdn_controller_version"):
+        print("option '--sdn_controller_version' is deprecated, use '--config={version: SDN_CONTROLLER_VERSION}'"
+              " instead")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.sdnc.create(kwargs["name"], sdncontroller, wait=kwargs["wait"])
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+@cli_osm.command(name='sdnc-update', short_help='updates an SDN controller')
 @click.argument('name')
 @click.option('--newname', help='New name for the SDN controller')
 @click.argument('name')
 @click.option('--newname', help='New name for the SDN controller')
+@click.option('--description',  default=None, help='human readable description')
 @click.option('--type', help='SDN controller type')
 @click.option('--type', help='SDN controller type')
-@click.option('--sdn_controller_version', help='SDN controller username')
-@click.option('--ip_address', help='SDN controller IP address')
-@click.option('--port', help='SDN controller port')
-@click.option('--switch_dpid', help='Switch DPID (Openflow Datapath ID)')
+@click.option('--url', help='URL in format http[s]://HOST:IP/')
+@click.option('--config', help='Extra information for SDN in yaml format, as '
+                               '{switch_id: identity used for the plugin (e.g. DPID: '
+                               'Openflow Datapath ID), version: version}')
 @click.option('--user', help='SDN controller username')
 @click.option('--password', help='SDN controller password')
 @click.option('--user', help='SDN controller username')
 @click.option('--password', help='SDN controller password')
-#@click.option('--description',  default=None, help='human readable description')
-@click.option('--wait',
-              required=False,
-              default=False,
-              is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
-@click.pass_context
-def sdnc_update(ctx,
-                name,
-                newname,
-                type,
-                sdn_controller_version,
-                ip_address,
-                port,
-                switch_dpid,
-                user,
-                password,
-                wait):
+@click.option('--ip_address', help='Deprecated. Use --url')  # hidden=True
+@click.option('--port', help='Deprecated. Use --url')  # hidden=True
+@click.option('--switch_dpid', help='Deprecated. Use --config {switch_dpid: DPID}')  # hidden=True
+@click.option('--sdn_controller_version', help='Deprecated. Use --config {version: VERSION}')  # hidden=True
+@click.option('--wait', required=False, default=False, is_flag=True,
+              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
+@click.pass_context
+def sdnc_update(ctx, **kwargs):
     """updates an SDN controller
 
     NAME: name or ID of the SDN controller
     """
     """updates an SDN controller
 
     NAME: name or ID of the SDN controller
     """
-    sdncontroller = {}
-    if newname: sdncontroller['name'] = newname
-    if type: sdncontroller['type'] = type
-    if ip_address: sdncontroller['ip'] = ip_address
-    if port: sdncontroller['port'] = int(port)
-    if switch_dpid: sdncontroller['dpid'] = switch_dpid
-#    sdncontroller['description'] = description
-    if sdn_controller_version is not None:
-        if sdn_controller_version=="":
-            sdncontroller['version'] = None
-        else:
-            sdncontroller['version'] = sdn_controller_version
-    if user is not None:
-        if user=="":
-            sdncontroller['user'] = None
-        else:
-            sdncontroller['user'] = user
-    if password is not None:
-        if password=="":
-            sdncontroller['password'] = None
-        else:
-            sdncontroller['password'] = user
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.sdnc.update(name, sdncontroller, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-@cli.command(name='sdnc-delete', short_help='deletes an SDN controller')
+    logger.debug("")
+    sdncontroller = {x: kwargs[x] for x in kwargs if kwargs[x] and
+                     x not in ("wait", "ip_address", "port", "switch_dpid", "new_name")}
+    if kwargs.get("newname"):
+        sdncontroller["name"] = kwargs["newname"]
+    if kwargs.get("port"):
+        print("option '--port' is deprecated, use '--url' instead")
+        sdncontroller["port"] = int(kwargs["port"])
+    if kwargs.get("ip_address"):
+        print("option '--ip_address' is deprecated, use '--url' instead")
+        sdncontroller["ip"] = kwargs["ip_address"]
+    if kwargs.get("switch_dpid"):
+        print("option '--switch_dpid' is deprecated, use '--config={switch_id: id|DPID}' instead")
+        sdncontroller["dpid"] = kwargs["switch_dpid"]
+    if kwargs.get("sdn_controller_version"):
+        print("option '--sdn_controller_version' is deprecated, use '---config={version: SDN_CONTROLLER_VERSION}'"
+              " instead")
+
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.sdnc.update(kwargs["name"], sdncontroller, wait=kwargs["wait"])
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='sdnc-delete', short_help='deletes an SDN controller')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.argument('name')
 @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
-@click.option('--wait',
-              required=False,
-              default=False,
-              is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+@click.option('--wait', required=False, default=False, is_flag=True,
+              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
 @click.pass_context
 def sdnc_delete(ctx, name, force, wait):
     """deletes an SDN controller
 
     NAME: name or ID of the SDN controller to be deleted
     """
 @click.pass_context
 def sdnc_delete(ctx, name, force, wait):
     """deletes an SDN controller
 
     NAME: name or ID of the SDN controller to be deleted
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.sdnc.delete(name, force, wait=wait)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-@cli.command(name='sdnc-list', short_help='list all SDN controllers')
-@click.option('--filter', default=None,
-              help='restricts the list to the SDN controllers matching the filter')
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.sdnc.delete(name, force, wait=wait)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='sdnc-list', short_help='list all SDN controllers')
+@click.option('--filter', default=None, multiple=True,
+              help="restricts the list to the SDN controllers matching the filter with format: 'k[.k..]=v[&k[.k]=v2]'")
 @click.pass_context
 def sdnc_list(ctx, filter):
     """list all SDN controllers"""
 @click.pass_context
 def sdnc_list(ctx, filter):
     """list all SDN controllers"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.sdnc.list(filter)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.sdnc.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(['sdnc name', 'id'])
     for sdnc in resp:
         table.add_row([sdnc['name'], sdnc['_id']])
     table = PrettyTable(['sdnc name', 'id'])
     for sdnc in resp:
         table.add_row([sdnc['name'], sdnc['_id']])
@@ -2214,7 +2979,7 @@ def sdnc_list(ctx, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='sdnc-show', short_help='shows the details of an SDN controller')
+@cli_osm.command(name='sdnc-show', short_help='shows the details of an SDN controller')
 @click.argument('name')
 @click.pass_context
 def sdnc_show(ctx, name):
 @click.argument('name')
 @click.pass_context
 def sdnc_show(ctx, name):
@@ -2222,12 +2987,13 @@ def sdnc_show(ctx, name):
 
     NAME: name or ID of the SDN controller
     """
 
     NAME: name or ID of the SDN controller
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.sdnc.get(name)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.sdnc.get(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in list(resp.items()):
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in list(resp.items()):
@@ -2236,32 +3002,440 @@ def sdnc_show(ctx, name):
     print(table)
 
 
     print(table)
 
 
+###########################
+# K8s cluster operations
+###########################
+
+@cli_osm.command(name='k8scluster-add', short_help='adds a K8s cluster to OSM')
+@click.argument('name')
+@click.option('--creds',
+              prompt=True,
+              help='credentials file, i.e. a valid `.kube/config` file')
+@click.option('--version',
+              prompt=True,
+              help='Kubernetes version')
+@click.option('--vim',
+              prompt=True,
+              help='VIM target, the VIM where the cluster resides')
+@click.option('--k8s-nets',
+              prompt=True,
+              help='list of VIM networks, in JSON inline format, where the cluster is accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"')
+@click.option('--description',
+              default=None,
+              help='human readable description')
+@click.option('--namespace',
+              default='kube-system',
+              help='namespace to be used for its operation, defaults to `kube-system`')
+@click.option('--cni',
+              default=None,
+              help='list of CNI plugins, in JSON inline format, used in the cluster')
+#@click.option('--skip-init',
+#              is_flag=True,
+#              help='If set, K8s cluster is assumed to be ready for its use with OSM')
+#@click.option('--wait',
+#              is_flag=True,
+#              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
+@click.pass_context
+def k8scluster_add(ctx,
+               name,
+               creds,
+               version,
+               vim,
+               k8s_nets,
+               description,
+               namespace,
+               cni):
+    """adds a K8s cluster to OSM
+
+    NAME: name of the K8s cluster
+    """
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    cluster = {}
+    cluster['name'] = name
+    with open(creds, 'r') as cf:
+        cluster['credentials'] = yaml.safe_load(cf.read())
+    cluster['k8s_version'] = version
+    cluster['vim_account'] = vim
+    cluster['nets'] = yaml.safe_load(k8s_nets)
+    if description:
+        cluster['description'] = description
+    if namespace: cluster['namespace'] = namespace
+    if cni: cluster['cni'] = yaml.safe_load(cni)
+    ctx.obj.k8scluster.create(name, cluster)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='k8scluster-update', short_help='updates a K8s cluster')
+@click.argument('name')
+@click.option('--newname', help='New name for the K8s cluster')
+@click.option('--creds', help='credentials file, i.e. a valid `.kube/config` file')
+@click.option('--version', help='Kubernetes version')
+@click.option('--vim', help='VIM target, the VIM where the cluster resides')
+@click.option('--k8s-nets', help='list of VIM networks, in JSON inline format, where the cluster is accessible via L3 routing, e.g. "{(k8s_net1:vim_network1) [,(k8s_net2:vim_network2) ...]}"')
+@click.option('--description', help='human readable description')
+@click.option('--namespace', help='namespace to be used for its operation, defaults to `kube-system`')
+@click.option('--cni', help='list of CNI plugins, in JSON inline format, used in the cluster')
+@click.pass_context
+def k8scluster_update(ctx,
+               name,
+               newname,
+               creds,
+               version,
+               vim,
+               k8s_nets,
+               description,
+               namespace,
+               cni):
+    """updates a K8s cluster
+
+    NAME: name or ID of the K8s cluster
+    """
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    cluster = {}
+    if newname: cluster['name'] = newname
+    if creds:
+        with open(creds, 'r') as cf:
+            cluster['credentials'] = yaml.safe_load(cf.read())
+    if version: cluster['k8s_version'] = version
+    if vim: cluster['vim_account'] = vim
+    if k8s_nets: cluster['nets'] = yaml.safe_load(k8s_nets)
+    if description: cluster['description'] = description
+    if namespace: cluster['namespace'] = namespace
+    if cni: cluster['cni'] = yaml.safe_load(cni)
+    ctx.obj.k8scluster.update(name, cluster)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='k8scluster-delete', short_help='deletes a K8s cluster')
+@click.argument('name')
+@click.option('--force', is_flag=True, help='forces the deletion from the DB (not recommended)')
+#@click.option('--wait',
+#              is_flag=True,
+#              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
+@click.pass_context
+def k8scluster_delete(ctx, name, force):
+    """deletes a K8s cluster
+
+    NAME: name or ID of the K8s cluster to be deleted
+    """
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.k8scluster.delete(name, force=force)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='k8scluster-list')
+@click.option('--filter', default=None, multiple=True,
+              help='restricts the list to the K8s clusters matching the filter')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.option('--long', is_flag=True, help='get more details')
+@click.pass_context
+def k8scluster_list(ctx, filter, literal, long):
+    """list all K8s clusters"""
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.k8scluster.list(filter)
+    if literal:
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+        return
+    if long:
+        table = PrettyTable(['Name', 'Id', 'Project', 'Version', 'VIM', 'K8s-nets',
+                             'Operational State', 'Op. state (details)', 'Description', 'Detailed status'])
+        project_list = ctx.obj.project.list()
+    else:
+        table = PrettyTable(['Name', 'Id', 'VIM', 'Operational State', 'Op. state details'])
+    for cluster in resp:
+        op_state_details = "Helm: {}\nJuju: {}".format(
+                           cluster["_admin"].get("helm-chart", "-").get("operationalState", "-"),
+                           cluster["_admin"].get("juju-bundle", "-").get("operationalState", "-"))
+        if long:
+            logger.debug('Cluster details: {}'.format(yaml.safe_dump(cluster)))
+            project_id, project_name = get_project(project_list, cluster)
+            #project_info = '{} ({})'.format(project_name, project_id)
+            project_info = project_name
+            detailed_status = cluster["_admin"].get("detailed-status","-")
+            table.add_row([cluster['name'], cluster['_id'], project_info,
+                           cluster['k8s_version'], cluster['vim_account'],
+                           json.dumps(cluster['nets']), cluster["_admin"]["operationalState"],
+                           op_state_details, trunc_text(cluster.get('description') or '', 40),
+                           wrap_text(text=detailed_status, width=40)])
+        else:
+            table.add_row([cluster['name'], cluster['_id'], cluster['vim_account'],
+                           cluster["_admin"]["operationalState"], op_state_details])
+    table.align = 'l'
+    print(table)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='k8scluster-show', short_help='shows the details of a K8s cluster')
+@click.argument('name')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.pass_context
+def k8scluster_show(ctx, name, literal):
+    """shows the details of a K8s cluster
+
+    NAME: name or ID of the K8s cluster
+    """
+    # try:
+    resp = ctx.obj.k8scluster.get(name)
+    if literal:
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+        return
+    table = PrettyTable(['key', 'attribute'])
+    for k, v in list(resp.items()):
+        table.add_row([k, wrap_text(text=json.dumps(v, indent=2),width=100)])
+    table.align = 'l'
+    print(table)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+
+###########################
+# Repo operations
+###########################
+
+@cli_osm.command(name='repo-add', short_help='adds a repo to OSM')
+@click.argument('name')
+@click.argument('uri')
+@click.option('--type',
+              type=click.Choice(['helm-chart', 'juju-bundle', 'osm']),
+              default='osm',
+              help='type of repo (helm-chart for Helm Charts, juju-bundle for Juju Bundles, osm for OSM Repositories)')
+@click.option('--description',
+              default=None,
+              help='human readable description')
+@click.option('--user',
+              default=None,
+              help='OSM repository: The username of the OSM repository')
+@click.option('--password',
+              default=None,
+              help='OSM repository: The password of the OSM repository')
+#@click.option('--wait',
+#              is_flag=True,
+#              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
+@click.pass_context
+def repo_add(ctx, **kwargs):
+    """adds a repo to OSM
+
+    NAME: name of the repo
+    URI: URI of the repo
+    """
+    # try:
+    kwargs = {k: v for k, v in kwargs.items() if v is not None}
+    repo = kwargs
+    repo["url"] = repo.pop("uri")
+    if repo["type"] in ['helm-chart', 'juju-bundle']:
+        ctx.obj.repo.create(repo['name'], repo)
+    else:
+        ctx.obj.osmrepo.create(repo['name'], repo)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='repo-update', short_help='updates a repo in OSM')
+@click.argument('name')
+@click.option('--newname', help='New name for the repo')
+@click.option('--uri', help='URI of the repo')
+@click.option('--description', help='human readable description')
+#@click.option('--wait',
+#              is_flag=True,
+#              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
+@click.pass_context
+def repo_update(ctx,
+             name,
+             newname,
+             uri,
+             description):
+    """updates a repo in OSM
+
+    NAME: name of the repo
+    """
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    repo = {}
+    if newname:
+        repo['name'] = newname
+    if uri:
+        repo['uri'] = uri
+    if description: repo['description'] = description
+    try:
+        ctx.obj.repo.update(name, repo)
+    except NotFound:
+        ctx.obj.osmrepo.update(name, repo)
+
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='repo-index', short_help='Index a repository from a folder with artifacts')
+@click.option('--origin', default='.', help='origin path where the artifacts are located')
+@click.option('--destination', default='.', help='destination path where the index is deployed')
+@click.pass_context
+def repo_index(ctx, origin, destination):
+    """Index a repository
+
+    NAME: name or ID of the repo to be deleted
+    """
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.osmrepo.repo_index(origin, destination)
+
+
+@cli_osm.command(name='repo-delete', short_help='deletes a repo')
+@click.argument('name')
+@click.option('--force', is_flag=True, help='forces the deletion from the DB (not recommended)')
+#@click.option('--wait',
+#              is_flag=True,
+#              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
+@click.pass_context
+def repo_delete(ctx, name, force):
+    """deletes a repo
+
+    NAME: name or ID of the repo to be deleted
+    """
+    logger.debug("")
+    try:
+        ctx.obj.repo.delete(name, force=force)
+    except NotFound:
+        ctx.obj.osmrepo.delete(name, force=force)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='repo-list')
+@click.option('--filter', default=None, multiple=True,
+              help='restricts the list to the repos matching the filter')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.pass_context
+def repo_list(ctx, filter, literal):
+    """list all repos"""
+    # try:
+    # K8s Repositories
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.repo.list(filter)
+    resp += ctx.obj.osmrepo.list(filter)
+    if literal:
+        print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+        return
+    table = PrettyTable(['Name', 'Id', 'Type', 'URI', 'Description'])
+    for repo in resp:
+        #cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets']))
+        table.add_row([repo['name'], repo['_id'], repo['type'], repo['url'], trunc_text(repo.get('description') or '',40)])
+    table.align = 'l'
+    print(table)
+
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+@cli_osm.command(name='repo-show', short_help='shows the details of a repo')
+@click.argument('name')
+@click.option('--literal', is_flag=True,
+              help='print literally, no pretty table')
+@click.pass_context
+def repo_show(ctx, name, literal):
+    """shows the details of a repo
+
+    NAME: name or ID of the repo
+    """
+    try:
+        resp = ctx.obj.repo.get(name)
+    except NotFound:
+        resp = ctx.obj.osmrepo.get(name)
+
+    if literal:
+        if resp:
+            print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
+        return
+    table = PrettyTable(['key', 'attribute'])
+    if resp:
+        for k, v in list(resp.items()):
+            table.add_row([k, json.dumps(v, indent=2)])
+
+    table.align = 'l'
+    print(table)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+
 ####################
 # Project mgmt operations
 ####################
 
 ####################
 # Project mgmt operations
 ####################
 
-@cli.command(name='project-create', short_help='creates a new project')
+@cli_osm.command(name='project-create', short_help='creates a new project')
 @click.argument('name')
 #@click.option('--description',
 #              default='no description',
 #              help='human readable description')
 @click.argument('name')
 #@click.option('--description',
 #              default='no description',
 #              help='human readable description')
+@click.option('--domain-name', 'domain_name',
+              default=None,
+              help='assign to a domain')
+@click.option('--quotas', 'quotas', multiple=True, default=None,
+              help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one "
+                   "of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos")
 @click.pass_context
 @click.pass_context
-def project_create(ctx, name):
+def project_create(ctx, name, domain_name, quotas):
     """Creates a new project
 
     NAME: name of the project
     """Creates a new project
 
     NAME: name of the project
+    DOMAIN_NAME: optional domain name for the project when keystone authentication is used
+    QUOTAS: set quotas for the project
     """
     """
-    project = {}
-    project['name'] = name
+    logger.debug("")
+    project = {'name': name}
+    if domain_name:
+        project['domain_name'] = domain_name
+    quotas_dict = _process_project_quotas(quotas)
+    if quotas_dict:
+        project['quotas'] = quotas_dict
+
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.project.create(name, project)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+def _process_project_quotas(quota_list):
+    quotas_dict = {}
+    if not quota_list:
+        return quotas_dict
     try:
     try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.project.create(name, project)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+        for quota in quota_list:
+            for single_quota in quota.split(","):
+                k, v = single_quota.split("=")
+                quotas_dict[k] = None if v in ('None', 'null', '') else int(v)
+    except (ValueError, TypeError):
+        raise ClientException("invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null")
+    return quotas_dict
 
 
 
 
-@cli.command(name='project-delete', short_help='deletes a project')
+@cli_osm.command(name='project-delete', short_help='deletes a project')
 @click.argument('name')
 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -2270,26 +3444,30 @@ def project_delete(ctx, name):
 
     NAME: name or ID of the project to be deleted
     """
 
     NAME: name or ID of the project to be deleted
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.project.delete(name)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.project.delete(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='project-list', short_help='list all projects')
-@click.option('--filter', default=None,
+@cli_osm.command(name='project-list', short_help='list all projects')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the projects matching the filter')
 @click.pass_context
 def project_list(ctx, filter):
     """list all projects"""
               help='restricts the list to the projects matching the filter')
 @click.pass_context
 def project_list(ctx, filter):
     """list all projects"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.project.list(filter)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.project.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(['name', 'id'])
     for proj in resp:
         table.add_row([proj['name'], proj['_id']])
     table = PrettyTable(['name', 'id'])
     for proj in resp:
         table.add_row([proj['name'], proj['_id']])
@@ -2297,7 +3475,7 @@ def project_list(ctx, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='project-show', short_help='shows the details of a project')
+@cli_osm.command(name='project-show', short_help='shows the details of a project')
 @click.argument('name')
 @click.pass_context
 def project_show(ctx, name):
 @click.argument('name')
 @click.pass_context
 def project_show(ctx, name):
@@ -2305,12 +3483,13 @@ def project_show(ctx, name):
 
     NAME: name or ID of the project
     """
 
     NAME: name or ID of the project
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.project.get(name)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.project.get(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in resp.items():
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in resp.items():
@@ -2319,38 +3498,44 @@ def project_show(ctx, name):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='project-update', short_help='updates a project (only the name can be updated)')
+@cli_osm.command(name='project-update', short_help='updates a project (only the name can be updated)')
 @click.argument('project')
 @click.argument('project')
-@click.option('--name',
-              prompt=True,
+@click.option('--name', default=None,
               help='new name for the project')
               help='new name for the project')
-
+@click.option('--quotas', 'quotas', multiple=True, default=None,
+              help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' "
+                   "(use empty to reset quota to default")
 @click.pass_context
 @click.pass_context
-def project_update(ctx, project, name):
+def project_update(ctx, project, name, quotas):
     """
     Update a project name
 
     :param ctx:
     :param project: id or name of the project to modify
     :param name:  new name for the project
     """
     Update a project name
 
     :param ctx:
     :param project: id or name of the project to modify
     :param name:  new name for the project
+    :param quotas:  change quotas of the project
     :return:
     """
     :return:
     """
-
+    logger.debug("")
     project_changes = {}
     project_changes = {}
-    project_changes['name'] = name
+    if name:
+        project_changes['name'] = name
+    quotas_dict = _process_project_quotas(quotas)
+    if quotas_dict:
+        project_changes['quotas'] = quotas_dict
 
 
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.project.update(project, project_changes)
-    except ClientException as inst:
-        print(inst.message)
+    try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.project.update(project, project_changes)
+    # except ClientException as e:
+    #     print(str(e))
 
 
 ####################
 # User mgmt operations
 ####################
 
 
 
 ####################
 # User mgmt operations
 ####################
 
-@cli.command(name='user-create', short_help='creates a new user')
+@cli_osm.command(name='user-create', short_help='creates a new user')
 @click.argument('username')
 @click.option('--password',
               prompt=True,
 @click.argument('username')
 @click.option('--password',
               prompt=True,
@@ -2364,9 +3549,12 @@ def project_update(ctx, project, name):
               help='list of project ids that the user belongs to')
 @click.option('--project-role-mappings', 'project_role_mappings',
               default=None, multiple=True,
               help='list of project ids that the user belongs to')
 @click.option('--project-role-mappings', 'project_role_mappings',
               default=None, multiple=True,
-              help='creating user project/role(s) mapping')
+              help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'")
+@click.option('--domain-name', 'domain_name',
+              default=None,
+              help='assign to a domain')
 @click.pass_context
 @click.pass_context
-def user_create(ctx, username, password, projects, project_role_mappings):
+def user_create(ctx, username, password, projects, project_role_mappings, domain_name):
     """Creates a new user
 
     \b
     """Creates a new user
 
     \b
@@ -2374,22 +3562,26 @@ def user_create(ctx, username, password, projects, project_role_mappings):
     PASSWORD: password of the user
     PROJECTS: projects assigned to user (internal only)
     PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
     PASSWORD: password of the user
     PROJECTS: projects assigned to user (internal only)
     PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
+    DOMAIN_NAME: optional domain name for the user when keystone authentication is used
     """
     """
+    logger.debug("")
     user = {}
     user['username'] = username
     user['password'] = password
     user['projects'] = projects
     user['project_role_mappings'] = project_role_mappings
     user = {}
     user['username'] = username
     user['password'] = password
     user['projects'] = projects
     user['project_role_mappings'] = project_role_mappings
-    
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.user.create(username, user)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    if domain_name:
+        user['domain_name'] = domain_name
 
 
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.user.create(username, user)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
-@cli.command(name='user-update', short_help='updates user information')
+
+@cli_osm.command(name='user-update', short_help='updates user information')
 @click.argument('username')
 @click.option('--password',
               # prompt=True,
 @click.argument('username')
 @click.option('--password',
               # prompt=True,
@@ -2401,16 +3593,16 @@ def user_create(ctx, username, password, projects, project_role_mappings):
               help='change username')
 @click.option('--set-project', 'set_project',
               default=None, multiple=True,
               help='change username')
 @click.option('--set-project', 'set_project',
               default=None, multiple=True,
-              help='create/replace the project,role(s) mapping for this project: \'project,role1,role2,...\'')
+              help="create/replace the roles for this project: 'project,role1[,role2,...]'")
 @click.option('--remove-project', 'remove_project',
               default=None, multiple=True,
 @click.option('--remove-project', 'remove_project',
               default=None, multiple=True,
-              help='removes project from user: \'project\'')
+              help="removes project from user: 'project'")
 @click.option('--add-project-role', 'add_project_role',
               default=None, multiple=True,
 @click.option('--add-project-role', 'add_project_role',
               default=None, multiple=True,
-              help='adds project,role(s) mapping: \'project,role1,role2,...\'')
+              help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'")
 @click.option('--remove-project-role', 'remove_project_role',
               default=None, multiple=True,
 @click.option('--remove-project-role', 'remove_project_role',
               default=None, multiple=True,
-              help='removes project,role(s) mapping: \'project,role1,role2,...\'')
+              help="remove role(s) in a project. Can be used several times: 'project,role1[,role2,...]'")
 @click.pass_context
 def user_update(ctx, username, password, set_username, set_project, remove_project,
                 add_project_role, remove_project_role):
 @click.pass_context
 def user_update(ctx, username, password, set_username, set_project, remove_project,
                 add_project_role, remove_project_role):
@@ -2425,6 +3617,7 @@ def user_update(ctx, username, password, set_username, set_project, remove_proje
     ADD_PROJECT_ROLE: adding mappings for project/role(s)
     REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
     """
     ADD_PROJECT_ROLE: adding mappings for project/role(s)
     REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
     """
+    logger.debug("")
     user = {}
     user['password'] = password
     user['username'] = set_username
     user = {}
     user['password'] = password
     user['username'] = set_username
@@ -2433,15 +3626,15 @@ def user_update(ctx, username, password, set_username, set_project, remove_proje
     user['add-project-role'] = add_project_role
     user['remove-project-role'] = remove_project_role
     
     user['add-project-role'] = add_project_role
     user['remove-project-role'] = remove_project_role
     
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.user.update(username, user)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.user.update(username, user)
+    # except ClientException as e:
+    #     print(str(e))
+        exit(1)
 
 
 
 
-@cli.command(name='user-delete', short_help='deletes a user')
+@cli_osm.command(name='user-delete', short_help='deletes a user')
 @click.argument('name')
 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 #@click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -2451,26 +3644,29 @@ def user_delete(ctx, name):
     \b
     NAME: name or ID of the user to be deleted
     """
     \b
     NAME: name or ID of the user to be deleted
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.user.delete(name)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.user.delete(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='user-list', short_help='list all users')
-@click.option('--filter', default=None,
+@cli_osm.command(name='user-list', short_help='list all users')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the users matching the filter')
 @click.pass_context
 def user_list(ctx, filter):
     """list all users"""
               help='restricts the list to the users matching the filter')
 @click.pass_context
 def user_list(ctx, filter):
     """list all users"""
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.user.list(filter)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.user.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(['name', 'id'])
     for user in resp:
         table.add_row([user['username'], user['_id']])
     table = PrettyTable(['name', 'id'])
     for user in resp:
         table.add_row([user['username'], user['_id']])
@@ -2478,7 +3674,7 @@ def user_list(ctx, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='user-show', short_help='shows the details of a user')
+@cli_osm.command(name='user-show', short_help='shows the details of a user')
 @click.argument('name')
 @click.pass_context
 def user_show(ctx, name):
 @click.argument('name')
 @click.pass_context
 def user_show(ctx, name):
@@ -2486,14 +3682,15 @@ def user_show(ctx, name):
 
     NAME: name or ID of the user
     """
 
     NAME: name or ID of the user
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.user.get(name)
-        if 'password' in resp:
-            resp['password']='********'
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.user.get(name)
+    if 'password' in resp:
+        resp['password']='********'
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in resp.items():
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in resp.items():
@@ -2506,7 +3703,7 @@ def user_show(ctx, name):
 # Fault Management operations
 ####################
 
 # Fault Management operations
 ####################
 
-@cli.command(name='ns-alarm-create')
+@cli_osm.command(name='ns-alarm-create')
 @click.argument('name')
 @click.option('--ns', prompt=True, help='NS instance id or name')
 @click.option('--vnf', prompt=True,
 @click.argument('name')
 @click.option('--ns', prompt=True, help='NS instance id or name')
 @click.option('--vnf', prompt=True,
@@ -2529,27 +3726,28 @@ def ns_alarm_create(ctx, name, ns, vnf, vdu, metric, severity,
     """creates a new alarm for a NS instance"""
     # TODO: Check how to validate threshold_value.
     # Should it be an integer (1-100), percentage, or decimal (0.01-1.00)?
     """creates a new alarm for a NS instance"""
     # TODO: Check how to validate threshold_value.
     # Should it be an integer (1-100), percentage, or decimal (0.01-1.00)?
-    try:
-        ns_instance = ctx.obj.ns.get(ns)
-        alarm = {}
-        alarm['alarm_name'] = name
-        alarm['ns_id'] = ns_instance['_id']
-        alarm['correlation_id'] = ns_instance['_id']
-        alarm['vnf_member_index'] = vnf
-        alarm['vdu_name'] = vdu
-        alarm['metric_name'] = metric
-        alarm['severity'] = severity
-        alarm['threshold_value'] = int(threshold_value)
-        alarm['operation'] = threshold_operator
-        alarm['statistic'] = statistic
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.ns.create_alarm(alarm)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
-
-
-#@cli.command(name='ns-alarm-delete')
+    logger.debug("")
+    # try:
+    ns_instance = ctx.obj.ns.get(ns)
+    alarm = {}
+    alarm['alarm_name'] = name
+    alarm['ns_id'] = ns_instance['_id']
+    alarm['correlation_id'] = ns_instance['_id']
+    alarm['vnf_member_index'] = vnf
+    alarm['vdu_name'] = vdu
+    alarm['metric_name'] = metric
+    alarm['severity'] = severity
+    alarm['threshold_value'] = int(threshold_value)
+    alarm['operation'] = threshold_operator
+    alarm['statistic'] = statistic
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.ns.create_alarm(alarm)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+
+#@cli_osm.command(name='ns-alarm-delete')
 #@click.argument('name')
 #@click.pass_context
 #def ns_alarm_delete(ctx, name):
 #@click.argument('name')
 #@click.pass_context
 #def ns_alarm_delete(ctx, name):
@@ -2560,8 +3758,8 @@ def ns_alarm_create(ctx, name, ns, vnf, vdu, metric, severity,
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name)
 #        ctx.obj.ns.delete_alarm(name)
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name)
 #        ctx.obj.ns.delete_alarm(name)
-#    except ClientException as inst:
-#        print(inst.message)
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 
 
 #        exit(1)
 
 
@@ -2569,7 +3767,7 @@ def ns_alarm_create(ctx, name, ns, vnf, vdu, metric, severity,
 # Performance Management operations
 ####################
 
 # Performance Management operations
 ####################
 
-@cli.command(name='ns-metric-export', short_help='exports a metric to the internal OSM bus, which can be read by other apps')
+@cli_osm.command(name='ns-metric-export', short_help='exports a metric to the internal OSM bus, which can be read by other apps')
 @click.option('--ns', prompt=True, help='NS instance id or name')
 @click.option('--vnf', prompt=True,
               help='VNF name (VNF member index as declared in the NSD)')
 @click.option('--ns', prompt=True, help='NS instance id or name')
 @click.option('--vnf', prompt=True,
               help='VNF name (VNF member index as declared in the NSD)')
@@ -2585,53 +3783,69 @@ def ns_metric_export(ctx, ns, vnf, vdu, metric, interval):
     """exports a metric to the internal OSM bus, which can be read by other apps"""
     # TODO: Check how to validate interval.
     # Should it be an integer (seconds), or should a suffix (s,m,h,d,w) also be permitted?
     """exports a metric to the internal OSM bus, which can be read by other apps"""
     # TODO: Check how to validate interval.
     # Should it be an integer (seconds), or should a suffix (s,m,h,d,w) also be permitted?
-    try:
-        ns_instance = ctx.obj.ns.get(ns)
-        metric_data = {}
-        metric_data['ns_id'] = ns_instance['_id']
-        metric_data['correlation_id'] = ns_instance['_id']
-        metric_data['vnf_member_index'] = vnf
-        metric_data['vdu_name'] = vdu
-        metric_data['metric_name'] = metric
-        metric_data['collection_unit'] = 'WEEK'
-        metric_data['collection_period'] = 1
-        check_client_version(ctx.obj, ctx.command.name)
-        if not interval:
-            print('{}'.format(ctx.obj.ns.export_metric(metric_data)))
-        else:
-            i = 1
-            while True:
-                print('{} {}'.format(ctx.obj.ns.export_metric(metric_data),i))
-                time.sleep(int(interval))
-                i+=1
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    ns_instance = ctx.obj.ns.get(ns)
+    metric_data = {}
+    metric_data['ns_id'] = ns_instance['_id']
+    metric_data['correlation_id'] = ns_instance['_id']
+    metric_data['vnf_member_index'] = vnf
+    metric_data['vdu_name'] = vdu
+    metric_data['metric_name'] = metric
+    metric_data['collection_unit'] = 'WEEK'
+    metric_data['collection_period'] = 1
+    check_client_version(ctx.obj, ctx.command.name)
+    if not interval:
+        print('{}'.format(ctx.obj.ns.export_metric(metric_data)))
+    else:
+        i = 1
+        while True:
+            print('{} {}'.format(ctx.obj.ns.export_metric(metric_data),i))
+            time.sleep(int(interval))
+            i+=1
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 ####################
 # Other operations
 ####################
 
 
 
 ####################
 # Other operations
 ####################
 
-@cli.command(name='upload-package', short_help='uploads a VNF package or NS package')
+@cli_osm.command(name='version', short_help='shows client and server versions')
+@click.pass_context
+def get_version(ctx):
+    """shows client and server versions"""
+    # try:
+    check_client_version(ctx.obj, "version")
+    print ("Server version: {}".format(ctx.obj.get_version()))
+    print ("Client version: {}".format(pkg_resources.get_distribution("osmclient").version))
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
+
+@cli_osm.command(name='upload-package', short_help='uploads a VNF package or NS package')
 @click.argument('filename')
 @click.argument('filename')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='the charm will not be compiled, it is assumed to already exist')
 @click.pass_context
 @click.pass_context
-def upload_package(ctx, filename):
-    """uploads a VNF package or NS package
+def upload_package(ctx, filename, skip_charm_build):
+    """uploads a vnf package or ns package
 
 
-    FILENAME: VNF or NS package file (tar.gz)
+    filename: vnf or ns package folder, or vnf or ns package file (tar.gz)
     """
     """
-    try:
-        ctx.obj.package.upload(filename)
-        fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
-        if fullclassname != 'osmclient.sol005.client.Client':
-            ctx.obj.package.wait_for_upload(filename)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    ctx.obj.package.upload(filename, skip_charm_build=skip_charm_build)
+    fullclassname = ctx.obj.__module__ + "." + ctx.obj.__class__.__name__
+    if fullclassname != 'osmclient.sol005.client.Client':
+        ctx.obj.package.wait_for_upload(filename)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-#@cli.command(name='ns-scaling-show')
+#@cli_osm.command(name='ns-scaling-show')
 #@click.argument('ns_name')
 #@click.pass_context
 #def show_ns_scaling(ctx, ns_name):
 #@click.argument('ns_name')
 #@click.pass_context
 #def show_ns_scaling(ctx, ns_name):
@@ -2642,8 +3856,8 @@ def upload_package(ctx, filename):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        resp = ctx.obj.ns.list()
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        resp = ctx.obj.ns.list()
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 #
 #    table = PrettyTable(
 #        exit(1)
 #
 #    table = PrettyTable(
@@ -2673,7 +3887,7 @@ def upload_package(ctx, filename):
 #    print(table)
 
 
 #    print(table)
 
 
-#@cli.command(name='ns-scale')
+#@cli_osm.command(name='ns-scale')
 #@click.argument('ns_name')
 #@click.option('--ns_scale_group', prompt=True)
 #@click.option('--index', prompt=True)
 #@click.argument('ns_name')
 #@click.option('--ns_scale_group', prompt=True)
 #@click.option('--index', prompt=True)
@@ -2692,19 +3906,19 @@ def upload_package(ctx, filename):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        ctx.obj.ns.scale(ns_name, ns_scale_group, index, wait=wait)
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        ctx.obj.ns.scale(ns_name, ns_scale_group, index, wait=wait)
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 
 
 #        exit(1)
 
 
-#@cli.command(name='config-agent-list')
+#@cli_osm.command(name='config-agent-list')
 #@click.pass_context
 #def config_agent_list(ctx):
 #    """list config agents"""
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #@click.pass_context
 #def config_agent_list(ctx):
 #    """list config agents"""
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 #    table = PrettyTable(['name', 'account-type', 'details'])
 #    for account in ctx.obj.vca.list():
 #        exit(1)
 #    table = PrettyTable(['name', 'account-type', 'details'])
 #    for account in ctx.obj.vca.list():
@@ -2716,7 +3930,7 @@ def upload_package(ctx, filename):
 #    print(table)
 
 
 #    print(table)
 
 
-#@cli.command(name='config-agent-delete')
+#@cli_osm.command(name='config-agent-delete')
 #@click.argument('name')
 #@click.pass_context
 #def config_agent_delete(ctx, name):
 #@click.argument('name')
 #@click.pass_context
 #def config_agent_delete(ctx, name):
@@ -2727,12 +3941,12 @@ def upload_package(ctx, filename):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        ctx.obj.vca.delete(name)
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        ctx.obj.vca.delete(name)
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 
 
 #        exit(1)
 
 
-#@cli.command(name='config-agent-add')
+#@cli_osm.command(name='config-agent-add')
 #@click.option('--name',
 #              prompt=True)
 #@click.option('--account_type',
 #@click.option('--name',
 #              prompt=True)
 #@click.option('--account_type',
@@ -2751,12 +3965,12 @@ def upload_package(ctx, filename):
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        ctx.obj.vca.create(name, account_type, server, user, secret)
 #    try:
 #        check_client_version(ctx.obj, ctx.command.name, 'v1')
 #        ctx.obj.vca.create(name, account_type, server, user, secret)
-#    except ClientException as inst:
-#        print((inst.message))
+#    except ClientException as e:
+#        print(str(e))
 #        exit(1)
 
 
 #        exit(1)
 
 
-#@cli.command(name='ro-dump')
+#@cli_osm.command(name='ro-dump')
 #@click.pass_context
 #def ro_dump(ctx):
 #    """shows RO agent information"""
 #@click.pass_context
 #def ro_dump(ctx):
 #    """shows RO agent information"""
@@ -2769,7 +3983,7 @@ def upload_package(ctx, filename):
 #    print(table)
 
 
 #    print(table)
 
 
-#@cli.command(name='vcs-list')
+#@cli_osm.command(name='vcs-list')
 #@click.pass_context
 #def vcs_list(ctx):
 #    check_client_version(ctx.obj, ctx.command.name, 'v1')
 #@click.pass_context
 #def vcs_list(ctx):
 #    check_client_version(ctx.obj, ctx.command.name, 'v1')
@@ -2781,66 +3995,84 @@ def upload_package(ctx, filename):
 #    print(table)
 
 
 #    print(table)
 
 
-@cli.command(name='ns-action', short_help='executes an action/primitive over a NS instance')
+@cli_osm.command(name='ns-action', short_help='executes an action/primitive over a NS instance')
 @click.argument('ns_name')
 @click.option('--vnf_name', default=None, help='member-vnf-index if the target is a vnf instead of a ns)')
 @click.argument('ns_name')
 @click.option('--vnf_name', default=None, help='member-vnf-index if the target is a vnf instead of a ns)')
-@click.option('--vdu_id', default=None, help='vdu-id if the target is a vdu o a vnf')
-@click.option('--vdu_count', default=None, help='number of vdu instance of this vdu_id')
-@click.option('--action_name', prompt=True)
-@click.option('--params', default=None)
+@click.option('--kdu_name', default=None, help='kdu-name if the target is a kdu)')
+@click.option('--vdu_id', default=None, help='vdu-id if the target is a vdu')
+@click.option('--vdu_count', default=None, type=int, help='number of vdu instance of this vdu_id')
+@click.option('--action_name', prompt=True, help='action name')
+@click.option('--params', default=None, help='action params in YAML/JSON inline string')
+@click.option('--params_file', default=None, help='YAML/JSON file with action params')
+@click.option('--timeout', required=False, default=None, type=int, help='timeout in seconds')
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
 @click.option('--wait',
               required=False,
               default=False,
               is_flag=True,
-              help='do not return the control immediately, but keep it \
-              until the operation is completed, or timeout')
+              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
 @click.pass_context
 def ns_action(ctx,
               ns_name,
               vnf_name,
 @click.pass_context
 def ns_action(ctx,
               ns_name,
               vnf_name,
+              kdu_name,
               vdu_id,
               vdu_count,
               action_name,
               params,
               vdu_id,
               vdu_count,
               action_name,
               params,
+              params_file,
+              timeout,
               wait):
     """executes an action/primitive over a NS instance
 
     NS_NAME: name or ID of the NS instance
     """
               wait):
     """executes an action/primitive over a NS instance
 
     NS_NAME: name or ID of the NS instance
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        op_data = {}
-        if vnf_name:
-            op_data['member_vnf_index'] = vnf_name
-        if vdu_id:
-            op_data['vdu_id'] = vdu_id
-        if vdu_count:
-            op_data['vdu_count_index'] = vdu_count
-        op_data['primitive'] = action_name
-        if params:
-            op_data['primitive_params'] = yaml.load(params)
-        else:
-            op_data['primitive_params'] = {}
-        ctx.obj.ns.exec_op(ns_name, op_name='action', op_data=op_data, wait=wait)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    op_data = {}
+    if vnf_name:
+        op_data['member_vnf_index'] = vnf_name
+    if kdu_name:
+        op_data['kdu_name'] = kdu_name
+    if vdu_id:
+        op_data['vdu_id'] = vdu_id
+    if vdu_count is not None:
+        op_data['vdu_count_index'] = vdu_count
+    if timeout:
+        op_data['timeout_ns_action'] = timeout
+    op_data['primitive'] = action_name
+    if params_file:
+        with open(params_file, 'r') as pf:
+            params = pf.read()
+    if params:
+        op_data['primitive_params'] = yaml.safe_load(params)
+    else:
+        op_data['primitive_params'] = {}
+    print(ctx.obj.ns.exec_op(ns_name, op_name='action', op_data=op_data, wait=wait))
 
 
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    # except ClientException as e:
+    #     print(str(e))
+        exit(1)
 
 
 
 
-@cli.command(name='vnf-scale', short_help='executes a VNF scale (adding/removing VDUs)')
+@cli_osm.command(name='vnf-scale', short_help='executes a VNF scale (adding/removing VDUs)')
 @click.argument('ns_name')
 @click.argument('vnf_name')
 @click.option('--scaling-group', prompt=True, help="scaling-group-descriptor name to use")
 @click.option('--scale-in', default=False, is_flag=True, help="performs a scale in operation")
 @click.option('--scale-out', default=False, is_flag=True, help="performs a scale out operation (by default)")
 @click.argument('ns_name')
 @click.argument('vnf_name')
 @click.option('--scaling-group', prompt=True, help="scaling-group-descriptor name to use")
 @click.option('--scale-in', default=False, is_flag=True, help="performs a scale in operation")
 @click.option('--scale-out', default=False, is_flag=True, help="performs a scale out operation (by default)")
+@click.option('--timeout', required=False, default=None, type=int, help='timeout in seconds')
+@click.option('--wait', required=False, default=False, is_flag=True,
+              help='do not return the control immediately, but keep it until the operation is completed, or timeout')
 @click.pass_context
 def vnf_scale(ctx,
               ns_name,
               vnf_name,
               scaling_group,
               scale_in,
 @click.pass_context
 def vnf_scale(ctx,
               ns_name,
               vnf_name,
               scaling_group,
               scale_in,
-              scale_out):
+              scale_out,
+              timeout,
+              wait):
     """
     Executes a VNF scale (adding/removing VDUs)
 
     """
     Executes a VNF scale (adding/removing VDUs)
 
@@ -2848,21 +4080,22 @@ def vnf_scale(ctx,
     NS_NAME: name or ID of the NS instance.
     VNF_NAME: member-vnf-index in the NS to be scaled.
     """
     NS_NAME: name or ID of the NS instance.
     VNF_NAME: member-vnf-index in the NS to be scaled.
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        if not scale_in and not scale_out:
-            scale_out = True
-        ctx.obj.ns.scale_vnf(ns_name, vnf_name, scaling_group, scale_in, scale_out)
-    except ClientException as inst:
-        print((inst.message))
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if not scale_in and not scale_out:
+        scale_out = True
+    ctx.obj.ns.scale_vnf(ns_name, vnf_name, scaling_group, scale_in, scale_out, wait, timeout)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 ##############################
 # Role Management Operations #
 ##############################
 
 
 
 ##############################
 # Role Management Operations #
 ##############################
 
-@cli.command(name='role-create', short_help='creates a new role')
+@cli_osm.command(name='role-create', short_help='creates a new role')
 @click.argument('name')
 @click.option('--permissions',
               default=None,
 @click.argument('name')
 @click.option('--permissions',
               default=None,
@@ -2876,15 +4109,16 @@ def role_create(ctx, name, permissions):
     NAME: Name or ID of the role.
     DEFINITION: Definition of grant/denial of access to resources.
     """
     NAME: Name or ID of the role.
     DEFINITION: Definition of grant/denial of access to resources.
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.role.create(name, permissions)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.role.create(name, permissions)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='role-update', short_help='updates a role')
+@cli_osm.command(name='role-update', short_help='updates a role')
 @click.argument('name')
 @click.option('--set-name',
               default=None,
 @click.argument('name')
 @click.option('--set-name',
               default=None,
@@ -2909,15 +4143,16 @@ def role_update(ctx, name, set_name, add, remove):
     ADD: Grant/denial of access to resource to add.
     REMOVE: Grant/denial of access to resource to remove.
     """
     ADD: Grant/denial of access to resource to add.
     REMOVE: Grant/denial of access to resource to remove.
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.role.update(name, set_name, None, add, remove)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.role.update(name, set_name, None, add, remove)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='role-delete', short_help='deletes a role')
+@cli_osm.command(name='role-delete', short_help='deletes a role')
 @click.argument('name')
 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
 @click.argument('name')
 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
 @click.pass_context
@@ -2928,28 +4163,32 @@ def role_delete(ctx, name):
     \b
     NAME: Name or ID of the role.
     """
     \b
     NAME: Name or ID of the role.
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        ctx.obj.role.delete(name)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    ctx.obj.role.delete(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
 
 
 
-@cli.command(name='role-list', short_help='list all roles')
-@click.option('--filter', default=None,
+@cli_osm.command(name='role-list', short_help='list all roles')
+@click.option('--filter', default=None, multiple=True,
               help='restricts the list to the projects matching the filter')
 @click.pass_context
 def role_list(ctx, filter):
     """
     List all roles.
     """
               help='restricts the list to the projects matching the filter')
 @click.pass_context
 def role_list(ctx, filter):
     """
     List all roles.
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.role.list(filter)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    if filter:
+        filter='&'.join(filter)
+    resp = ctx.obj.role.list(filter)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
     table = PrettyTable(['name', 'id'])
     for role in resp:
         table.add_row([role['name'], role['_id']])
     table = PrettyTable(['name', 'id'])
     for role in resp:
         table.add_row([role['name'], role['_id']])
@@ -2957,7 +4196,7 @@ def role_list(ctx, filter):
     print(table)
 
 
     print(table)
 
 
-@cli.command(name='role-show', short_help='show specific role')
+@cli_osm.command(name='role-show', short_help='show specific role')
 @click.argument('name')
 @click.pass_context
 def role_show(ctx, name):
 @click.argument('name')
 @click.pass_context
 def role_show(ctx, name):
@@ -2967,12 +4206,13 @@ def role_show(ctx, name):
     \b
     NAME: Name or ID of the role.
     """
     \b
     NAME: Name or ID of the role.
     """
-    try:
-        check_client_version(ctx.obj, ctx.command.name)
-        resp = ctx.obj.role.get(name)
-    except ClientException as inst:
-        print(inst.message)
-        exit(1)
+    logger.debug("")
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    resp = ctx.obj.role.get(name)
+    # except ClientException as e:
+    #     print(str(e))
+    #     exit(1)
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in resp.items():
 
     table = PrettyTable(['key', 'attribute'])
     for k, v in resp.items():
@@ -2981,11 +4221,179 @@ def role_show(ctx, name):
     print(table)
 
 
     print(table)
 
 
-if __name__ == '__main__':
+@cli_osm.command(name='package-create',
+             short_help='Create a package descriptor')
+@click.argument('package-type')
+@click.argument('package-name')
+@click.option('--base-directory',
+              default='.',
+              help=('(NS/VNF/NST) Set the location for package creation. Default: "."'))
+@click.option('--image',
+              default="image-name",
+              help='(VNF) Set the name of the vdu image. Default "image-name"')
+@click.option('--vdus',
+              default=1,
+              help='(VNF) Set the number of vdus in a VNF. Default 1')
+@click.option('--vcpu',
+              default=1,
+              help='(VNF) Set the number of virtual CPUs in a vdu. Default 1')
+@click.option('--memory',
+              default=1024,
+              help='(VNF) Set the memory size (MB) of the vdu. Default 1024')
+@click.option('--storage',
+              default=10,
+              help='(VNF) Set the disk size (GB) of the vdu. Default 10')
+@click.option('--interfaces',
+              default=0,
+              help='(VNF) Set the number of additional interfaces apart from the management interface. Default 0')
+@click.option('--vendor',
+              default="OSM",
+              help='(NS/VNF) Set the descriptor vendor. Default "OSM"')
+@click.option('--override',
+              default=False,
+              is_flag=True,
+              help='(NS/VNF/NST) Flag for overriding the package if exists.')
+@click.option('--detailed',
+              is_flag=True,
+              default=False,
+              help='(NS/VNF/NST) Flag for generating descriptor .yaml with all possible commented options')
+@click.option('--netslice-subnets',
+              default=1,
+              help='(NST) Number of netslice subnets. Default 1')
+@click.option('--netslice-vlds',
+              default=1,
+              help='(NST) Number of netslice vlds. Default 1')
+@click.pass_context
+def package_create(ctx,
+                   package_type,
+                   base_directory,
+                   package_name,
+                   override,
+                   image,
+                   vdus,
+                   vcpu,
+                   memory,
+                   storage,
+                   interfaces,
+                   vendor,
+                   detailed,
+                   netslice_subnets,
+                   netslice_vlds):
+    """
+    Creates an OSM NS, VNF, NST package
+
+    \b
+    PACKAGE_TYPE: Package to be created: NS, VNF or NST.
+    PACKAGE_NAME: Name of the package to create the folder with the content.
+    """
+
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    print("Creating the {} structure: {}/{}".format(package_type.upper(), base_directory, package_name))
+    resp = ctx.obj.package_tool.create(package_type,
+                                       base_directory,
+                                       package_name,
+                                       override=override,
+                                       image=image,
+                                       vdus=vdus,
+                                       vcpu=vcpu,
+                                       memory=memory,
+                                       storage=storage,
+                                       interfaces=interfaces,
+                                       vendor=vendor,
+                                       detailed=detailed,
+                                       netslice_subnets=netslice_subnets,
+                                       netslice_vlds=netslice_vlds)
+    print(resp)
+    # except ClientException as inst:
+    #     print("ERROR: {}".format(inst))
+    #     exit(1)
+
+@cli_osm.command(name='package-validate',
+             short_help='Validate a package descriptor')
+@click.argument('base-directory',
+                default=".",
+                required=False)
+@click.option('--recursive/--no-recursive',
+              default=True,
+              help='The activated recursive option will validate the yaml files'
+                   ' within the indicated directory and in its subdirectories')
+@click.pass_context
+def package_validate(ctx,
+                     base_directory,
+                     recursive):
+    """
+    Validate descriptors given a base directory.
+
+    \b
+    BASE_DIRECTORY: Stub folder for NS, VNF or NST package.
+    """
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    results = ctx.obj.package_tool.validate(base_directory, recursive)
+    table = PrettyTable()
+    table.field_names = ["TYPE", "PATH", "VALID", "ERROR"]
+    # Print the dictionary generated by the validation function
+    for result in results:
+        table.add_row([result["type"], result["path"], result["valid"], result["error"]])
+    table.sortby = "VALID"
+    table.align["PATH"] = "l"
+    table.align["TYPE"] = "l"
+    table.align["ERROR"] = "l"
+    print(table)
+    # except ClientException as inst:
+    #     print("ERROR: {}".format(inst))
+    #     exit(1)
+
+@cli_osm.command(name='package-build',
+             short_help='Build the tar.gz of the package')
+@click.argument('package-folder')
+@click.option('--skip-validation',
+              default=False,
+              is_flag=True,
+              help='skip package validation')
+@click.option('--skip-charm-build', default=False, is_flag=True,
+              help='the charm will not be compiled, it is assumed to already exist')
+@click.pass_context
+def package_build(ctx,
+                  package_folder,
+                  skip_validation,
+                  skip_charm_build):
+    """
+    Build the package NS, VNF given the package_folder.
+
+    \b
+    PACKAGE_FOLDER: Folder of the NS, VNF or NST to be packaged
+    """
+    # try:
+    check_client_version(ctx.obj, ctx.command.name)
+    results = ctx.obj.package_tool.build(package_folder,
+                                         skip_validation=skip_validation,
+                                         skip_charm_build=skip_charm_build)
+    print(results)
+    # except ClientException as inst:
+    #     print("ERROR: {}".format(inst))
+    #     exit(1)
+
+
+def cli():
     try:
     try:
-        cli()
-    except pycurl.error as e:
-        print(e)
-        print('Maybe "--hostname" option or OSM_HOSTNAME' +
-              'environment variable needs to be specified')
-        exit(1)
+        cli_osm()
+        exit(0)
+    except pycurl.error as exc:
+        print(exc)
+        print('Maybe "--hostname" option or OSM_HOSTNAME environment variable needs to be specified')
+    except ClientException as exc:
+        print("ERROR: {}".format(exc))
+    except (FileNotFoundError, PermissionError) as exc:
+        print("Cannot open file: {}".format(exc))
+    except yaml.YAMLError as exc:
+        print("Invalid YAML format: {}".format(exc))
+    exit(1)
+    # TODO capture other controlled exceptions here
+    # TODO remove the ClientException captures from all places, unless they do something different
+
+
+if __name__ == '__main__':
+    cli()
+