You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by av...@apache.org on 2017/04/19 11:21:25 UTC

[07/12] incubator-ariatosca git commit: reviewed helptexts module; renamed cli.cli package to cli.core

reviewed helptexts module; renamed cli.cli package to cli.core


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/79f5d78e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/79f5d78e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/79f5d78e

Branch: refs/heads/cli-tests
Commit: 79f5d78ebe31327e7635ae75551ba636ac5fd1dc
Parents: 3bff159
Author: Ran Ziv <ra...@gigaspaces.com>
Authored: Tue Apr 18 16:55:52 2017 +0300
Committer: Ran Ziv <ra...@gigaspaces.com>
Committed: Tue Apr 18 16:55:52 2017 +0300

----------------------------------------------------------------------
 aria/cli/cli/__init__.py               |  14 -
 aria/cli/cli/aria.py                   | 439 ----------------------------
 aria/cli/cli/helptexts.py              |  57 ----
 aria/cli/commands/executions.py        |   5 +-
 aria/cli/commands/logs.py              |   2 +-
 aria/cli/commands/node_templates.py    |   4 +-
 aria/cli/commands/nodes.py             |   2 +-
 aria/cli/commands/plugins.py           |   4 +-
 aria/cli/commands/reset.py             |   4 +-
 aria/cli/commands/service_templates.py |   2 +-
 aria/cli/commands/services.py          |   5 +-
 aria/cli/commands/workflows.py         |   2 +-
 aria/cli/core/__init__.py              |  14 +
 aria/cli/core/aria.py                  | 429 +++++++++++++++++++++++++++
 aria/cli/helptexts.py                  |  49 ++++
 aria/cli/main.py                       |   2 +-
 16 files changed, 509 insertions(+), 525 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/cli/__init__.py
----------------------------------------------------------------------
diff --git a/aria/cli/cli/__init__.py b/aria/cli/cli/__init__.py
deleted file mode 100644
index ae1e83e..0000000
--- a/aria/cli/cli/__init__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/cli/aria.py
----------------------------------------------------------------------
diff --git a/aria/cli/cli/aria.py b/aria/cli/cli/aria.py
deleted file mode 100644
index 548be23..0000000
--- a/aria/cli/cli/aria.py
+++ /dev/null
@@ -1,439 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-import os
-import sys
-import difflib
-import StringIO
-import traceback
-from functools import wraps
-
-import click
-
-from ..env import (
-    env,
-    logger
-)
-from ..cli import helptexts
-from ..inputs import inputs_to_dict
-from ..constants import DEFAULT_SERVICE_TEMPLATE_FILENAME
-from ...utils.exceptions import get_exception_as_string
-from ... import __version__
-
-
-CLICK_CONTEXT_SETTINGS = dict(
-    help_option_names=['-h', '--help'],
-    token_normalize_func=lambda param: param.lower())
-
-
-class MutuallyExclusiveOption(click.Option):
-    """Makes options mutually exclusive. The option must pass a `cls` argument
-    with this class name and a `mutually_exclusive` argument with a list of
-    argument names it is mutually exclusive with.
-
-    NOTE: All mutually exclusive options must use this. It's not enough to
-    use it in just one of the options.
-    """
-
-    def __init__(self, *args, **kwargs):
-        self.mutually_exclusive = set(kwargs.pop('mutually_exclusive', []))
-        self.mutuality_error_message = \
-            kwargs.pop('mutuality_error_message',
-                       helptexts.DEFAULT_MUTUALITY_MESSAGE)
-        self.mutuality_string = ', '.join(self.mutually_exclusive)
-        if self.mutually_exclusive:
-            help = kwargs.get('help', '')
-            kwargs['help'] = (
-                '{0}. This argument is mutually exclusive with '
-                'arguments: [{1}] ({2})'.format(
-                    help,
-                    self.mutuality_string,
-                    self.mutuality_error_message))
-        super(MutuallyExclusiveOption, self).__init__(*args, **kwargs)
-
-    def handle_parse_result(self, ctx, opts, args):
-        if self.mutually_exclusive.intersection(opts) and self.name in opts:
-            raise click.UsageError(
-                'Illegal usage: `{0}` is mutually exclusive with '
-                'arguments: [{1}] ({2}).'.format(
-                    self.name,
-                    self.mutuality_string,
-                    self.mutuality_error_message))
-        return super(MutuallyExclusiveOption, self).handle_parse_result(
-            ctx, opts, args)
-
-
-def _format_version_data(version,
-                         prefix=None,
-                         suffix=None,
-                         infix=None):
-    all_data = dict(version=version)
-    all_data['prefix'] = prefix or ''
-    all_data['suffix'] = suffix or ''
-    all_data['infix'] = infix or ''
-    output = StringIO.StringIO()
-    output.write('{prefix}{version}'.format(**all_data))
-    output.write('{suffix}'.format(**all_data))
-    return output.getvalue()
-
-
-def show_version(ctx, param, value):
-    if not value:
-        return
-
-    cli_version = _format_version_data(
-        __version__,
-        prefix='ARIA CLI ',
-        infix=' ' * 5,
-        suffix='')
-
-    logger.info(cli_version)
-    ctx.exit()
-
-
-def inputs_callback(ctx, param, value):
-    """Allow to pass any inputs we provide to a command as
-    processed inputs instead of having to call `inputs_to_dict`
-    inside the command.
-
-    `@aria.options.inputs` already calls this callback so that
-    every time you use the option it returns the inputs as a
-    dictionary.
-    """
-    if not value:
-        return {}
-
-    return inputs_to_dict(value)
-
-
-def set_verbosity_level(ctx, param, value):
-    if not value:
-        return
-
-    env.logging.verbosity_level = value
-
-
-def set_cli_except_hook():
-
-    def recommend(possible_solutions):
-        logger.info('Possible solutions:')
-        for solution in possible_solutions:
-            logger.info('  - {0}'.format(solution))
-
-    def new_excepthook(tpe, value, trace):
-        if env.logging.is_high_verbose_level():
-            # log error including traceback
-            logger.error(get_exception_as_string(tpe, value, trace))
-        else:
-            # write the full error to the log file
-            with open(env.logging.log_file, 'a') as log_file:
-                traceback.print_exception(
-                    etype=tpe,
-                    value=value,
-                    tb=trace,
-                    file=log_file)
-            # print only the error message
-            print value
-
-        if hasattr(value, 'possible_solutions'):
-            recommend(getattr(value, 'possible_solutions'))
-
-    sys.excepthook = new_excepthook
-
-
-def pass_logger(func):
-    """Simply passes the logger to a command.
-    """
-    # Wraps here makes sure the original docstring propagates to click
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        return func(logger=logger, *args, **kwargs)
-
-    return wrapper
-
-
-def pass_plugin_manager(func):
-    """Simply passes the plugin manager to a command.
-    """
-    # Wraps here makes sure the original docstring propagates to click
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        return func(plugin_manager=env.plugin_manager, *args, **kwargs)
-
-    return wrapper
-
-
-def pass_model_storage(func):
-    """Simply passes the model storage to a command.
-    """
-    # Wraps here makes sure the original docstring propagates to click
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        return func(model_storage=env.model_storage, *args, **kwargs)
-
-    return wrapper
-
-
-def pass_resource_storage(func):
-    """Simply passes the resource storage to a command.
-    """
-    # Wraps here makes sure the original docstring propagates to click
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        return func(resource_storage=env.resource_storage, *args, **kwargs)
-
-    return wrapper
-
-
-def pass_context(func):
-    """Make click context ARIA specific
-
-    This exists purely for aesthetic reasons, otherwise
-    Some decorators are called `@click.something` instead of
-    `@aria.something`
-    """
-    return click.pass_context(func)
-
-
-class AliasedGroup(click.Group):
-    def __init__(self, *args, **kwargs):
-        self.max_suggestions = kwargs.pop("max_suggestions", 3)
-        self.cutoff = kwargs.pop("cutoff", 0.5)
-        super(AliasedGroup, self).__init__(*args, **kwargs)
-
-    def get_command(self, ctx, cmd_name):
-        cmd = click.Group.get_command(self, ctx, cmd_name)
-        if cmd is not None:
-            return cmd
-        matches = \
-            [x for x in self.list_commands(ctx) if x.startswith(cmd_name)]
-        if not matches:
-            return None
-        elif len(matches) == 1:
-            return click.Group.get_command(self, ctx, matches[0])
-        ctx.fail('Too many matches: {0}'.format(', '.join(sorted(matches))))
-
-    def resolve_command(self, ctx, args):
-        """Override clicks ``resolve_command`` method
-        and appends *Did you mean ...* suggestions
-        to the raised exception message.
-        """
-        try:
-            return super(AliasedGroup, self).resolve_command(ctx, args)
-        except click.exceptions.UsageError as error:
-            error_msg = str(error)
-            original_cmd_name = click.utils.make_str(args[0])
-            matches = difflib.get_close_matches(
-                original_cmd_name,
-                self.list_commands(ctx),
-                self.max_suggestions,
-                self.cutoff)
-            if matches:
-                error_msg += '{0}{0}Did you mean one of these?{0}    {1}'.format(
-                    os.linesep,
-                    '{0}    '.format(os.linesep).join(matches, ))
-            raise click.exceptions.UsageError(error_msg, error.ctx)
-
-
-def group(name):
-    """Allow to create a group with a default click context
-    and a cls for click's `didyoueamn` without having to repeat
-    it for every group.
-    """
-    return click.group(
-        name=name,
-        context_settings=CLICK_CONTEXT_SETTINGS,
-        cls=AliasedGroup)
-
-
-def command(*args, **kwargs):
-    """Make Click commands ARIA specific
-
-    This exists purely for aesthetical reasons, otherwise
-    Some decorators are called `@click.something` instead of
-    `@aria.something`
-    """
-    return click.command(*args, **kwargs)
-
-
-def argument(*args, **kwargs):
-    """Make Click arguments ARIA specific
-
-    This exists purely for aesthetic reasons, otherwise
-    Some decorators are called `@click.something` instead of
-    `@aria.something`
-    """
-    return click.argument(*args, **kwargs)
-
-
-class Options(object):
-    def __init__(self):
-        """The options api is nicer when you use each option by calling
-        `@aria.options.some_option` instead of `@aria.some_option`.
-
-        Note that some options are attributes and some are static methods.
-        The reason for that is that we want to be explicit regarding how
-        a developer sees an option. It it can receive arguments, it's a
-        method - if not, it's an attribute.
-        """
-        self.version = click.option(
-            '--version',
-            is_flag=True,
-            callback=show_version,
-            expose_value=False,
-            is_eager=True,
-            help=helptexts.VERSION)
-
-        self.inputs = click.option(
-            '-i',
-            '--inputs',
-            multiple=True,
-            callback=inputs_callback,
-            help=helptexts.INPUTS)
-
-        self.json_output = click.option(
-            '--json-output',
-            is_flag=True,
-            help=helptexts.JSON_OUTPUT)
-
-        self.dry_execution = click.option(
-            '--dry',
-            is_flag=True,
-            help=helptexts.DRY_EXECUTION)
-
-        self.reset_config = click.option(
-            '--reset-config',
-            is_flag=True,
-            help=helptexts.RESET_CONFIG)
-
-        self.enable_colors = click.option(
-            '--enable-colors',
-            is_flag=True,
-            default=False,
-            help=helptexts.ENABLE_COLORS)
-
-        self.node_name = click.option(
-            '-n',
-            '--node-name',
-            required=False,
-            help=helptexts.NODE_NAME)
-
-        self.descending = click.option(
-            '--descending',
-            required=False,
-            is_flag=True,
-            default=False,
-            help=helptexts.DESCENDING)
-
-        self.service_template_filename = click.option(
-            '-n',
-            '--service-template-filename',
-            default=DEFAULT_SERVICE_TEMPLATE_FILENAME,
-            help=helptexts.SERVICE_TEMPLATE_FILENAME)
-
-    @staticmethod
-    def verbose(expose_value=False):
-        return click.option(
-            '-v',
-            '--verbose',
-            count=True,
-            callback=set_verbosity_level,
-            expose_value=expose_value,
-            is_eager=True,
-            help=helptexts.VERBOSE)
-
-    @staticmethod
-    def force(help):
-        return click.option(
-            '-f',
-            '--force',
-            is_flag=True,
-            help=help)
-
-    @staticmethod
-    def task_max_attempts(default=1):
-        return click.option(
-            '--task-max-attempts',
-            type=int,
-            default=default,
-            help=helptexts.TASK_MAX_ATTEMPTS.format(default))
-
-    @staticmethod
-    def sort_by(default='created_at'):
-        return click.option(
-            '--sort-by',
-            required=False,
-            default=default,
-            help=helptexts.SORT_BY)
-
-    @staticmethod
-    def task_retry_interval(default=1):
-        return click.option(
-            '--task-retry-interval',
-            type=int,
-            default=default,
-            help=helptexts.TASK_RETRY_INTERVAL.format(default))
-
-    @staticmethod
-    def service_id(required=False):
-        return click.option(
-            '-s',
-            '--service-id',
-            required=required,
-            help=helptexts.SERVICE_ID)
-
-    @staticmethod
-    def execution_id(required=False):
-        return click.option(
-            '-e',
-            '--execution-id',
-            required=required,
-            help=helptexts.EXECUTION_ID)
-
-    @staticmethod
-    def service_template_id(required=False):
-        return click.option(
-            '-t',
-            '--service-template-id',
-            required=required,
-            help=helptexts.SERVICE_TEMPLATE_ID)
-
-    @staticmethod
-    def service_template_path(required=False):
-        return click.option(
-            '-p',
-            '--service-template-path',
-            required=required,
-            type=click.Path(exists=True))
-
-    @staticmethod
-    def service_name(required=False):
-        return click.option(
-            '-s',
-            '--service-name',
-            required=required,
-            help=helptexts.SERVICE_ID)
-
-    @staticmethod
-    def service_template_name(required=False):
-        return click.option(
-            '-t',
-            '--service-template-name',
-            required=required,
-            help=helptexts.SERVICE_ID)
-
-
-options = Options()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/cli/helptexts.py
----------------------------------------------------------------------
diff --git a/aria/cli/cli/helptexts.py b/aria/cli/cli/helptexts.py
deleted file mode 100644
index f8b315c..0000000
--- a/aria/cli/cli/helptexts.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-VERBOSE = \
-    "Show verbose output. You can supply this up to three times (i.e. -vvv)"
-VERSION = "Display the version and exit"
-
-INPUTS_PARAMS_USAGE = (
-    '(Can be provided as wildcard based paths '
-    '(*.yaml, /my_inputs/, etc..) to YAML files, a JSON string or as '
-    'key1=value1;key2=value2). This argument can be used multiple times'
-)
-
-SERVICE_TEMPLATE_PATH = "The path to the application's service template file"
-SERVICE_TEMPLATE_ID = "The unique identifier for the service template"
-
-FORCE_RESET = "Confirmation for resetting ARIA's working directory"
-RESET_CONFIG = "Reset ARIA's user configuration"
-ENABLE_COLORS = "Enable colors in logger (use --hard when working with" \
-                " an initialized environment) [default: False]"
-
-DRY_EXECUTION = "Execute a workflow dry run (prints operations information without causing side " \
-                "effects)"
-SERVICE_TEMPLATE_FILENAME = (
-    "The name of the archive's main service template file. "
-    "This is only relevant if uploading an archive")
-INPUTS = "Inputs for the service {0}".format(INPUTS_PARAMS_USAGE)
-PARAMETERS = "Parameters for the workflow {0}".format(INPUTS_PARAMS_USAGE)
-TASK_RETRY_INTERVAL = \
-    "How long of a minimal interval should occur between task retry attempts [default: {0}]"
-TASK_MAX_ATTEMPTS = \
-    "How many times should a task be attempted in case of failures [default: {0}]"
-
-JSON_OUTPUT = "Output events in a consumable JSON format"
-
-SERVICE_ID = "The unique identifier for the service"
-EXECUTION_ID = "The unique identifier for the execution"
-IGNORE_AVAILABLE_NODES = "Delete the service even if it has available nodes"
-
-NODE_NAME = "The node's name"
-
-DEFAULT_MUTUALITY_MESSAGE = 'Cannot be used simultaneously'
-
-SORT_BY = "Key for sorting the list"
-DESCENDING = "Sort list in descending order [default: False]"

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/executions.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/executions.py b/aria/cli/commands/executions.py
index 730fd29..cd12ead 100644
--- a/aria/cli/commands/executions.py
+++ b/aria/cli/commands/executions.py
@@ -15,9 +15,10 @@
 
 import os
 
+from .. import helptexts
 from .. import utils
+from ..core import aria
 from ..table import print_data
-from ..cli import aria
 from ...modeling.models import Execution
 from ...orchestrator.workflow_runner import WorkflowRunner
 from ...orchestrator.workflows.executor.dry import DryExecutor
@@ -103,7 +104,7 @@ def list(service_name,
                     short_help='Execute a workflow')
 @aria.argument('workflow-name')
 @aria.options.service_name(required=True)
-@aria.options.inputs
+@aria.options.inputs(help=helptexts.EXECUTION_INPUTS)
 @aria.options.dry_execution
 @aria.options.task_max_attempts()
 @aria.options.task_retry_interval()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/logs.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/logs.py b/aria/cli/commands/logs.py
index f8873cd..8888fef 100644
--- a/aria/cli/commands/logs.py
+++ b/aria/cli/commands/logs.py
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 from .. import utils
-from ..cli import aria
+from ..core import aria
 
 
 @aria.group(name='logs')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/node_templates.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/node_templates.py b/aria/cli/commands/node_templates.py
index cf50ceb..b63b630 100644
--- a/aria/cli/commands/node_templates.py
+++ b/aria/cli/commands/node_templates.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ..table import print_data
 from .. import utils
-from ..cli import aria
+from ..core import aria
+from ..table import print_data
 
 
 NODE_TEMPLATE_COLUMNS = ['id', 'name', 'description', 'service_template_name', 'type_name']

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/nodes.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/nodes.py b/aria/cli/commands/nodes.py
index fd65e24..b1f2acc 100644
--- a/aria/cli/commands/nodes.py
+++ b/aria/cli/commands/nodes.py
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 from .. import utils
-from ..cli import aria
+from ..core import aria
 from ..table import print_data
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/plugins.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/plugins.py b/aria/cli/commands/plugins.py
index 9e7d449..680284f 100644
--- a/aria/cli/commands/plugins.py
+++ b/aria/cli/commands/plugins.py
@@ -15,9 +15,9 @@
 
 import zipfile
 
-from ..table import print_data
-from ..cli import aria
+from ..core import aria
 from ..exceptions import AriaCliError
+from ..table import print_data
 from ..utils import storage_sort_param
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/reset.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/reset.py b/aria/cli/commands/reset.py
index 775f555..1fe0714 100644
--- a/aria/cli/commands/reset.py
+++ b/aria/cli/commands/reset.py
@@ -13,8 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ..cli import aria
-from ..cli import helptexts
+from .. import helptexts
+from ..core import aria
 from ..env import env
 from ..exceptions import AriaCliError
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/service_templates.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/service_templates.py b/aria/cli/commands/service_templates.py
index 8e0e91c..93dc188 100644
--- a/aria/cli/commands/service_templates.py
+++ b/aria/cli/commands/service_templates.py
@@ -19,7 +19,7 @@ import os
 from .. import utils
 from .. import csar
 from .. import service_template_utils
-from ..cli import aria
+from ..core import aria
 from ..table import print_data
 from ..exceptions import AriaCliError
 from ..utils import handle_storage_exception

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/services.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/services.py b/aria/cli/commands/services.py
index 78899c5..4728509 100644
--- a/aria/cli/commands/services.py
+++ b/aria/cli/commands/services.py
@@ -18,7 +18,8 @@ import os
 from StringIO import StringIO
 
 from . import service_templates
-from ..cli import aria, helptexts
+from .. import helptexts
+from ..core import aria
 from ..exceptions import AriaCliError
 from ..table import print_data
 from ..utils import storage_sort_param, handle_storage_exception
@@ -74,7 +75,7 @@ def list(service_template_name,
                   short_help='Create a services')
 @aria.argument('service-name', required=False)
 @aria.options.service_template_name(required=True)
-@aria.options.inputs
+@aria.options.inputs(help=helptexts.SERVICE_INPUTS)
 @aria.options.verbose()
 @aria.pass_model_storage
 @aria.pass_resource_storage

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/commands/workflows.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/workflows.py b/aria/cli/commands/workflows.py
index 72dea5b..d380fac 100644
--- a/aria/cli/commands/workflows.py
+++ b/aria/cli/commands/workflows.py
@@ -13,8 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from ..core import aria
 from ..table import print_data
-from ..cli import aria
 from ..exceptions import AriaCliError
 
 WORKFLOW_COLUMNS = ['name', 'service_template_name', 'service_name']

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/core/__init__.py
----------------------------------------------------------------------
diff --git a/aria/cli/core/__init__.py b/aria/cli/core/__init__.py
new file mode 100644
index 0000000..ae1e83e
--- /dev/null
+++ b/aria/cli/core/__init__.py
@@ -0,0 +1,14 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/core/aria.py
----------------------------------------------------------------------
diff --git a/aria/cli/core/aria.py b/aria/cli/core/aria.py
new file mode 100644
index 0000000..cd1036e
--- /dev/null
+++ b/aria/cli/core/aria.py
@@ -0,0 +1,429 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import os
+import sys
+import difflib
+import StringIO
+import traceback
+from functools import wraps
+
+import click
+
+from ..env import (
+    env,
+    logger
+)
+from .. import helptexts
+from ..inputs import inputs_to_dict
+from ..constants import DEFAULT_SERVICE_TEMPLATE_FILENAME
+from ...utils.exceptions import get_exception_as_string
+from ... import __version__
+
+
+CLICK_CONTEXT_SETTINGS = dict(
+    help_option_names=['-h', '--help'],
+    token_normalize_func=lambda param: param.lower())
+
+
+class MutuallyExclusiveOption(click.Option):
+    """Makes options mutually exclusive. The option must pass a `cls` argument
+    with this class name and a `mutually_exclusive` argument with a list of
+    argument names it is mutually exclusive with.
+
+    NOTE: All mutually exclusive options must use this. It's not enough to
+    use it in just one of the options.
+    """
+
+    def __init__(self, *args, **kwargs):
+        self.mutually_exclusive = set(kwargs.pop('mutually_exclusive', []))
+        self.mutuality_error_message = \
+            kwargs.pop('mutuality_error_message',
+                       helptexts.DEFAULT_MUTUALITY_MESSAGE)
+        self.mutuality_string = ', '.join(self.mutually_exclusive)
+        if self.mutually_exclusive:
+            help = kwargs.get('help', '')
+            kwargs['help'] = (
+                '{0}. This argument is mutually exclusive with '
+                'arguments: [{1}] ({2})'.format(
+                    help,
+                    self.mutuality_string,
+                    self.mutuality_error_message))
+        super(MutuallyExclusiveOption, self).__init__(*args, **kwargs)
+
+    def handle_parse_result(self, ctx, opts, args):
+        if self.mutually_exclusive.intersection(opts) and self.name in opts:
+            raise click.UsageError(
+                'Illegal usage: `{0}` is mutually exclusive with '
+                'arguments: [{1}] ({2}).'.format(
+                    self.name,
+                    self.mutuality_string,
+                    self.mutuality_error_message))
+        return super(MutuallyExclusiveOption, self).handle_parse_result(
+            ctx, opts, args)
+
+
+def _format_version_data(version,
+                         prefix=None,
+                         suffix=None,
+                         infix=None):
+    all_data = dict(version=version)
+    all_data['prefix'] = prefix or ''
+    all_data['suffix'] = suffix or ''
+    all_data['infix'] = infix or ''
+    output = StringIO.StringIO()
+    output.write('{prefix}{version}'.format(**all_data))
+    output.write('{suffix}'.format(**all_data))
+    return output.getvalue()
+
+
+def show_version(ctx, param, value):
+    if not value:
+        return
+
+    cli_version = _format_version_data(
+        __version__,
+        prefix='ARIA CLI ',
+        infix=' ' * 5,
+        suffix='')
+
+    logger.info(cli_version)
+    ctx.exit()
+
+
+def inputs_callback(ctx, param, value):
+    """Allow to pass any inputs we provide to a command as
+    processed inputs instead of having to call `inputs_to_dict`
+    inside the command.
+
+    `@aria.options.inputs` already calls this callback so that
+    every time you use the option it returns the inputs as a
+    dictionary.
+    """
+    if not value:
+        return {}
+
+    return inputs_to_dict(value)
+
+
+def set_verbosity_level(ctx, param, value):
+    if not value:
+        return
+
+    env.logging.verbosity_level = value
+
+
+def set_cli_except_hook():
+
+    def recommend(possible_solutions):
+        logger.info('Possible solutions:')
+        for solution in possible_solutions:
+            logger.info('  - {0}'.format(solution))
+
+    def new_excepthook(tpe, value, trace):
+        if env.logging.is_high_verbose_level():
+            # log error including traceback
+            logger.error(get_exception_as_string(tpe, value, trace))
+        else:
+            # write the full error to the log file
+            with open(env.logging.log_file, 'a') as log_file:
+                traceback.print_exception(
+                    etype=tpe,
+                    value=value,
+                    tb=trace,
+                    file=log_file)
+            # print only the error message
+            print value
+
+        if hasattr(value, 'possible_solutions'):
+            recommend(getattr(value, 'possible_solutions'))
+
+    sys.excepthook = new_excepthook
+
+
+def pass_logger(func):
+    """Simply passes the logger to a command.
+    """
+    # Wraps here makes sure the original docstring propagates to click
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        return func(logger=logger, *args, **kwargs)
+
+    return wrapper
+
+
+def pass_plugin_manager(func):
+    """Simply passes the plugin manager to a command.
+    """
+    # Wraps here makes sure the original docstring propagates to click
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        return func(plugin_manager=env.plugin_manager, *args, **kwargs)
+
+    return wrapper
+
+
+def pass_model_storage(func):
+    """Simply passes the model storage to a command.
+    """
+    # Wraps here makes sure the original docstring propagates to click
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        return func(model_storage=env.model_storage, *args, **kwargs)
+
+    return wrapper
+
+
+def pass_resource_storage(func):
+    """Simply passes the resource storage to a command.
+    """
+    # Wraps here makes sure the original docstring propagates to click
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        return func(resource_storage=env.resource_storage, *args, **kwargs)
+
+    return wrapper
+
+
+def pass_context(func):
+    """Make click context ARIA specific
+
+    This exists purely for aesthetic reasons, otherwise
+    Some decorators are called `@click.something` instead of
+    `@aria.something`
+    """
+    return click.pass_context(func)
+
+
+class AliasedGroup(click.Group):
+    def __init__(self, *args, **kwargs):
+        self.max_suggestions = kwargs.pop("max_suggestions", 3)
+        self.cutoff = kwargs.pop("cutoff", 0.5)
+        super(AliasedGroup, self).__init__(*args, **kwargs)
+
+    def get_command(self, ctx, cmd_name):
+        cmd = click.Group.get_command(self, ctx, cmd_name)
+        if cmd is not None:
+            return cmd
+        matches = \
+            [x for x in self.list_commands(ctx) if x.startswith(cmd_name)]
+        if not matches:
+            return None
+        elif len(matches) == 1:
+            return click.Group.get_command(self, ctx, matches[0])
+        ctx.fail('Too many matches: {0}'.format(', '.join(sorted(matches))))
+
+    def resolve_command(self, ctx, args):
+        """Override clicks ``resolve_command`` method
+        and appends *Did you mean ...* suggestions
+        to the raised exception message.
+        """
+        try:
+            return super(AliasedGroup, self).resolve_command(ctx, args)
+        except click.exceptions.UsageError as error:
+            error_msg = str(error)
+            original_cmd_name = click.utils.make_str(args[0])
+            matches = difflib.get_close_matches(
+                original_cmd_name,
+                self.list_commands(ctx),
+                self.max_suggestions,
+                self.cutoff)
+            if matches:
+                error_msg += '{0}{0}Did you mean one of these?{0}    {1}'.format(
+                    os.linesep,
+                    '{0}    '.format(os.linesep).join(matches, ))
+            raise click.exceptions.UsageError(error_msg, error.ctx)
+
+
+def group(name):
+    """Allow to create a group with a default click context
+    and a cls for click's `didyoueamn` without having to repeat
+    it for every group.
+    """
+    return click.group(
+        name=name,
+        context_settings=CLICK_CONTEXT_SETTINGS,
+        cls=AliasedGroup)
+
+
+def command(*args, **kwargs):
+    """Make Click commands ARIA specific
+
+    This exists purely for aesthetical reasons, otherwise
+    Some decorators are called `@click.something` instead of
+    `@aria.something`
+    """
+    return click.command(*args, **kwargs)
+
+
+def argument(*args, **kwargs):
+    """Make Click arguments ARIA specific
+
+    This exists purely for aesthetic reasons, otherwise
+    Some decorators are called `@click.something` instead of
+    `@aria.something`
+    """
+    return click.argument(*args, **kwargs)
+
+
+class Options(object):
+    def __init__(self):
+        """The options api is nicer when you use each option by calling
+        `@aria.options.some_option` instead of `@aria.some_option`.
+
+        Note that some options are attributes and some are static methods.
+        The reason for that is that we want to be explicit regarding how
+        a developer sees an option. It it can receive arguments, it's a
+        method - if not, it's an attribute.
+        """
+        self.version = click.option(
+            '--version',
+            is_flag=True,
+            callback=show_version,
+            expose_value=False,
+            is_eager=True,
+            help=helptexts.VERSION)
+
+        self.json_output = click.option(
+            '--json-output',
+            is_flag=True,
+            help=helptexts.JSON_OUTPUT)
+
+        self.dry_execution = click.option(
+            '--dry',
+            is_flag=True,
+            help=helptexts.DRY_EXECUTION)
+
+        self.reset_config = click.option(
+            '--reset-config',
+            is_flag=True,
+            help=helptexts.RESET_CONFIG)
+
+        self.descending = click.option(
+            '--descending',
+            required=False,
+            is_flag=True,
+            default=False,
+            help=helptexts.DESCENDING)
+
+        self.service_template_filename = click.option(
+            '-n',
+            '--service-template-filename',
+            default=DEFAULT_SERVICE_TEMPLATE_FILENAME,
+            help=helptexts.SERVICE_TEMPLATE_FILENAME)
+
+    @staticmethod
+    def verbose(expose_value=False):
+        return click.option(
+            '-v',
+            '--verbose',
+            count=True,
+            callback=set_verbosity_level,
+            expose_value=expose_value,
+            is_eager=True,
+            help=helptexts.VERBOSE)
+
+    @staticmethod
+    def inputs(help):
+        return click.option(
+            '-i',
+            '--inputs',
+            multiple=True,
+            callback=inputs_callback,
+            help=help)
+
+    @staticmethod
+    def force(help):
+        return click.option(
+            '-f',
+            '--force',
+            is_flag=True,
+            help=help)
+
+    @staticmethod
+    def task_max_attempts(default=1):
+        return click.option(
+            '--task-max-attempts',
+            type=int,
+            default=default,
+            help=helptexts.TASK_MAX_ATTEMPTS.format(default))
+
+    @staticmethod
+    def sort_by(default='created_at'):
+        return click.option(
+            '--sort-by',
+            required=False,
+            default=default,
+            help=helptexts.SORT_BY)
+
+    @staticmethod
+    def task_retry_interval(default=1):
+        return click.option(
+            '--task-retry-interval',
+            type=int,
+            default=default,
+            help=helptexts.TASK_RETRY_INTERVAL.format(default))
+
+    @staticmethod
+    def service_id(required=False):
+        return click.option(
+            '-s',
+            '--service-id',
+            required=required,
+            help=helptexts.SERVICE_ID)
+
+    @staticmethod
+    def execution_id(required=False):
+        return click.option(
+            '-e',
+            '--execution-id',
+            required=required,
+            help=helptexts.EXECUTION_ID)
+
+    @staticmethod
+    def service_template_id(required=False):
+        return click.option(
+            '-t',
+            '--service-template-id',
+            required=required,
+            help=helptexts.SERVICE_TEMPLATE_ID)
+
+    @staticmethod
+    def service_template_path(required=False):
+        return click.option(
+            '-p',
+            '--service-template-path',
+            required=required,
+            type=click.Path(exists=True))
+
+    @staticmethod
+    def service_name(required=False):
+        return click.option(
+            '-s',
+            '--service-name',
+            required=required,
+            help=helptexts.SERVICE_ID)
+
+    @staticmethod
+    def service_template_name(required=False):
+        return click.option(
+            '-t',
+            '--service-template-name',
+            required=required,
+            help=helptexts.SERVICE_ID)
+
+
+options = Options()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/helptexts.py
----------------------------------------------------------------------
diff --git a/aria/cli/helptexts.py b/aria/cli/helptexts.py
new file mode 100644
index 0000000..6e31f47
--- /dev/null
+++ b/aria/cli/helptexts.py
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+DEFAULT_MUTUALITY_MESSAGE = 'Cannot be used simultaneously'
+VERBOSE = \
+    "Show verbose output. You can supply this up to three times (i.e. -vvv)"
+
+VERSION = "Display the version and exit"
+FORCE_RESET = "Confirmation for resetting ARIA's working directory"
+RESET_CONFIG = "Reset ARIA's user configuration"
+
+SERVICE_TEMPLATE_ID = "The unique identifier for the service template"
+SERVICE_ID = "The unique identifier for the service"
+EXECUTION_ID = "The unique identifier for the execution"
+
+SERVICE_TEMPLATE_PATH = "The path to the application's service template file"
+SERVICE_TEMPLATE_FILENAME = (
+    "The name of the archive's main service template file. "
+    "This is only relevant if uploading a non-csar archive")
+INPUTS_PARAMS_USAGE = (
+    '(Can be provided as wildcard based paths '
+    '(*.yaml, /my_inputs/, etc..) to YAML files, a JSON string or as '
+    'key1=value1;key2=value2). This argument can be used multiple times')
+SERVICE_INPUTS = "Inputs for the service {0}".format(INPUTS_PARAMS_USAGE)
+EXECUTION_INPUTS = "Inputs for the execution {0}".format(INPUTS_PARAMS_USAGE)
+
+TASK_RETRY_INTERVAL = \
+    "How long of a minimal interval should occur between task retry attempts [default: {0}]"
+TASK_MAX_ATTEMPTS = \
+    "How many times should a task be attempted in case of failures [default: {0}]"
+DRY_EXECUTION = "Execute a workflow dry run (prints operations information without causing side " \
+                "effects)"
+IGNORE_AVAILABLE_NODES = "Delete the service even if it has available nodes"
+SORT_BY = "Key for sorting the list"
+DESCENDING = "Sort list in descending order [default: False]"
+JSON_OUTPUT = "Output logs in a consumable JSON format"

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79f5d78e/aria/cli/main.py
----------------------------------------------------------------------
diff --git a/aria/cli/main.py b/aria/cli/main.py
index 01d224c..02cf095 100644
--- a/aria/cli/main.py
+++ b/aria/cli/main.py
@@ -15,7 +15,7 @@
 
 from aria import install_aria_extensions
 from aria.cli import commands
-from aria.cli.cli import aria
+from aria.cli.core import aria
 
 
 @aria.group(name='aria')