From 0717dfb97fd7d126cf162960af599622b0c5ce85 Mon Sep 17 00:00:00 2001 From: tengqm Date: Wed, 13 Dec 2017 04:49:57 -0500 Subject: [PATCH] Retire senlin command line tool With upstream osc-lib bug fixed, the --profile argument now works. We have no need of the senlin command line. This patch retires the command line. Change-Id: I6bfc8f6f97ad7a77624ba5da1004a7a8f0924740 --- README.rst | 7 +- doc/source/cli/index.rst | 1723 ------------------- doc/source/conf.py | 6 +- doc/source/index.rst | 1 - doc/source/man/senlin.rst | 82 - senlinclient/cliargs.py | 180 -- senlinclient/client.py | 23 - senlinclient/common/utils.py | 148 -- senlinclient/shell.py | 312 ---- senlinclient/tests/unit/test_cliargs.py | 74 - senlinclient/tests/unit/test_client.py | 39 - senlinclient/tests/unit/test_shell.py | 356 ---- senlinclient/tests/unit/test_utils.py | 153 +- senlinclient/tests/unit/v1/test_shell.py | 1952 ---------------------- senlinclient/v1/shell.py | 1883 --------------------- setup.cfg | 3 - tools/senlin.bash_completion | 27 - 17 files changed, 5 insertions(+), 6964 deletions(-) delete mode 100644 doc/source/cli/index.rst delete mode 100644 doc/source/man/senlin.rst delete mode 100644 senlinclient/cliargs.py delete mode 100644 senlinclient/client.py delete mode 100644 senlinclient/shell.py delete mode 100644 senlinclient/tests/unit/test_cliargs.py delete mode 100644 senlinclient/tests/unit/test_client.py delete mode 100644 senlinclient/tests/unit/test_shell.py delete mode 100644 senlinclient/tests/unit/v1/test_shell.py delete mode 100644 senlinclient/v1/shell.py delete mode 100644 tools/senlin.bash_completion diff --git a/README.rst b/README.rst index f3d4a9af..f62d9010 100644 --- a/README.rst +++ b/README.rst @@ -7,12 +7,11 @@ Team and repository tags .. Change things from this point on -Python bindings to the Senlin Clustering API -============================================ +OpenStackClient Plugin for Senlin Clustering Service +==================================================== This is a client library for Senlin built on the Senlin clustering API. It -provides a Python API (the ``senlinclient`` module) and a command-line tool -(``senlin``). +provides a plugin for the openstackclient command-line tool. Development takes place via the usual OpenStack processes as outlined in the `developer guide `_. diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst deleted file mode 100644 index dfdcb88c..00000000 --- a/doc/source/cli/index.rst +++ /dev/null @@ -1,1723 +0,0 @@ -.. ################################################### -.. ## WARNING ###################################### -.. ############## WARNING ########################## -.. ########################## WARNING ############## -.. ###################################### WARNING ## -.. ################################################### -.. ################################################### -.. ## -.. This file is tool-generated. Do not edit manually. -.. https://docs.openstack.org/doc-contrib-guide/ -.. doc-tools/cli-reference.html -.. ## -.. ## WARNING ###################################### -.. ############## WARNING ########################## -.. ########################## WARNING ############## -.. ###################################### WARNING ## -.. ################################################### - -=============================================== -Clustering service (senlin) command-line client -=============================================== - -The senlin client is the command-line interface (CLI) for -the Clustering service (senlin) API and its extensions. - -This chapter documents :command:`senlin` version ``1.3.0``. - -For help on a specific :command:`senlin` command, enter: - -.. code-block:: console - - $ senlin help COMMAND - -.. _senlin_command_usage: - -senlin usage -~~~~~~~~~~~~ - -.. code-block:: console - - usage: senlin [--version] [-d] [-v] [--api-timeout API_TIMEOUT] - [--senlin-api-version SENLIN_API_VERSION] - [--os-auth-plugin AUTH_PLUGIN] [--os-auth-url AUTH_URL] - [--os-project-id PROJECT_ID] [--os-project-name PROJECT_NAME] - [--os-tenant-id TENANT_ID] [--os-tenant-name TENANT_NAME] - [--os-domain-id DOMAIN_ID] [--os-domain-name DOMAIN_NAME] - [--os-project-domain-id PROJECT_DOMAIN_ID] - [--os-project-domain-name PROJECT_DOMAIN_NAME] - [--os-user-domain-id USER_DOMAIN_ID] - [--os-user-domain-name USER_DOMAIN_NAME] - [--os-username USERNAME] [--os-user-id USER_ID] - [--os-password PASSWORD] [--os-trust-id TRUST_ID] - [--os-cacert CA_BUNDLE_FILE | --verify | --insecure] - [--os-token TOKEN] [--os-access-info ACCESS_INFO] - [--os-profile HMAC_KEY] - ... - -**Subcommands:** - -``action-list`` - List actions. - -``action-show`` - Show detailed info about the specified action. - -``build-info`` - Retrieve build information. - -``cluster-check`` - Check the cluster(s). - -``cluster-collect`` - Collect attributes across a cluster. - -``cluster-create`` - Create the cluster. - -``cluster-delete`` - Delete the cluster(s). - -``cluster-list`` - List the user's clusters. - -``cluster-node-add`` - Add specified nodes to cluster. - -``cluster-node-del`` - Delete specified nodes from cluster. - -``cluster-node-list`` - List nodes from cluster. - -``cluster-node-replace`` - Replace the nodes in cluster with specified nodes. - -``cluster-policy-attach`` - Attach policy to cluster. - -``cluster-policy-detach`` - Detach policy from cluster. - -``cluster-policy-list`` - List policies from cluster. - -``cluster-policy-show`` - Show a specific policy that is bound to the specified - cluster. - -``cluster-policy-update`` - Update a policy's properties on a cluster. - -``cluster-recover`` - Recover the cluster(s). - -``cluster-resize`` - Resize a cluster. - -``cluster-run`` - Run shell scripts on all nodes of a cluster. - -``cluster-scale-in`` - Scale in a cluster by the specified number of nodes. - -``cluster-scale-out`` - Scale out a cluster by the specified number of nodes. - -``cluster-show`` - Show details of the cluster. - -``cluster-update`` - Update the cluster. - -``event-list`` - List events. - -``event-show`` - Describe the event. - -``node-check`` - Check the node(s). - -``node-create`` - Create the node. - -``node-delete`` - Delete the node(s). - -``node-list`` - Show list of nodes. - -``node-recover`` - Recover the node(s). - -``node-show`` - Show detailed info about the specified node. - -``node-update`` - Update the node. - -``policy-create`` - Create a policy. - -``policy-delete`` - Delete policy(s). - -``policy-list`` - List policies that meet the criteria. - -``policy-show`` - Show the policy details. - -``policy-type-list`` - List the available policy types. - -``policy-type-show`` - Get the details about a policy type. - -``policy-update`` - Update a policy. - -``policy-validate`` - Validate a policy spec. - -``profile-create`` - Create a profile. - -``profile-delete`` - Delete profile(s). - -``profile-list`` - List profiles that meet the criteria. - -``profile-show`` - Show the profile details. - -``profile-type-list`` - List the available profile types. - -``profile-type-show`` - Get the details about a profile type. - -``profile-update`` - Update a profile. - -``profile-validate`` - Validate a profile. - -``receiver-create`` - Create a receiver. - -``receiver-delete`` - Delete receiver(s). - -``receiver-list`` - List receivers that meet the criteria. - -``receiver-show`` - Show the receiver details. - -``bash-completion`` - Prints all of the commands and options to stdout. - -``help`` - Display help about this program or one of its - subcommands. - -.. _senlin_command_options: - -senlin optional arguments -~~~~~~~~~~~~~~~~~~~~~~~~~ - -``--version`` - Shows the client version and exits. - -``-d, --debug`` - Defaults to ``env[SENLINCLIENT_DEBUG]``. - -``-v, --verbose`` - Print more verbose output. - -``--api-timeout API_TIMEOUT`` - Number of seconds to wait for an API response, - defaults to system socket timeout - -``--senlin-api-version SENLIN_API_VERSION`` - Version number for Senlin API to use, Default to "1". - -``--os-auth-plugin AUTH_PLUGIN`` - Authentication plugin, default to ``env[OS_AUTH_PLUGIN]`` - -``--os-auth-url AUTH_URL`` - Defaults to ``env[OS_AUTH_URL]`` - -``--os-project-id PROJECT_ID`` - Defaults to ``env[OS_PROJECT_ID]``. - -``--os-project-name PROJECT_NAME`` - Defaults to ``env[OS_PROJECT_NAME]``. - -``--os-tenant-id TENANT_ID`` - Defaults to ``env[OS_TENANT_ID]``. - -``--os-tenant-name TENANT_NAME`` - Defaults to ``env[OS_TENANT_NAME]``. - -``--os-domain-id DOMAIN_ID`` - Domain ID for scope of authorization, defaults to - ``env[OS_DOMAIN_ID]``. - -``--os-domain-name DOMAIN_NAME`` - Domain name for scope of authorization, defaults to - ``env[OS_DOMAIN_NAME]``. - -``--os-project-domain-id PROJECT_DOMAIN_ID`` - Project domain ID for scope of authorization, defaults - to ``env[OS_PROJECT_DOMAIN_ID]``. - -``--os-project-domain-name PROJECT_DOMAIN_NAME`` - Project domain name for scope of authorization, - defaults to ``env[OS_PROJECT_DOMAIN_NAME]``. - -``--os-user-domain-id USER_DOMAIN_ID`` - User domain ID for scope of authorization, defaults to - ``env[OS_USER_DOMAIN_ID]``. - -``--os-user-domain-name USER_DOMAIN_NAME`` - User domain name for scope of authorization, defaults - to ``env[OS_USER_DOMAIN_NAME]``. - -``--os-username USERNAME`` - Defaults to ``env[OS_USERNAME]``. - -``--os-user-id USER_ID`` - Defaults to ``env[OS_USER_ID]``. - -``--os-password PASSWORD`` - Defaults to ``env[OS_PASSWORD]`` - -``--os-trust-id TRUST_ID`` - Defaults to ``env[OS_TRUST_ID]`` - -``--os-cacert CA_BUNDLE_FILE`` - Path of CA TLS certificate(s) used to verify the - remote server's certificate. Without this option - senlin looks for the default system CA certificates. - -``--verify`` - Verify server certificate (default) - -``--insecure`` - Explicitly allow senlinclient to perform "insecure - SSL" (HTTPS) requests. The server's certificate will - not be verified against any certificate authorities. - This option should be used with caution. - -``--os-token TOKEN`` - A string token to bootstrap the Keystone database, - defaults to ``env[OS_TOKEN]`` - -``--os-access-info ACCESS_INFO`` - Access info, defaults to ``env[OS_ACCESS_INFO]`` - -``--os-profile HMAC_KEY`` - HMAC key to use for encrypting context data for - performance profiling of operation. This key should be - the value of HMAC key configured in osprofiler - middleware in senlin, it is specified in the paste - deploy configuration (/etc/senlin/api-paste.ini). - Without the key, profiling will not be triggered even - if osprofiler is enabled on server side. - -.. _senlin_action-list: - -senlin action-list ------------------- - -.. code-block:: console - - usage: senlin action-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] [-o ] - [-l ] [-m ] [-g] [-F] - -List actions. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned actions. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-l , --limit `` - Limit the number of actions returned. - -``-m , --marker `` - Only return actions that appear after the given node - ID. - -``-g, --global-project`` - Whether actions from all projects should be listed. - Default to False. Setting this to True may demand for - an admin privilege. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_action-show: - -senlin action-show ------------------- - -.. code-block:: console - - usage: senlin action-show - -Show detailed info about the specified action. - -**Positional arguments:** - -```` - Name or ID of the action to show the details for. - -.. _senlin_build-info: - -senlin build-info ------------------ - -.. code-block:: console - - usage: senlin build-info - -Retrieve build information. - -.. _senlin_cluster-check: - -senlin cluster-check --------------------- - -.. code-block:: console - - usage: senlin cluster-check [ ...] - -Check the cluster(s). - -**Positional arguments:** - -```` - ID or name of cluster(s) to operate on. - -.. _senlin_cluster-collect: - -senlin cluster-collect ----------------------- - -.. code-block:: console - - usage: senlin cluster-collect -p [-L] [-F] - -Collect attributes across a cluster. - -**Positional arguments:** - -```` - Name or ID of cluster(s) to operate on. - -**Optional arguments:** - -``-p , --path `` - A Json path string specifying the attribute to - collect. - -``-L, --list`` - Print a full list that contains both node ids and - attribute values instead of values only. Default is - False. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_cluster-create: - -senlin cluster-create ---------------------- - -.. code-block:: console - - usage: senlin cluster-create -p [-n ] [-m ] - [-c ] [-t ] - [-M <"KEY1=VALUE1;KEY2=VALUE2...">] - - -Create the cluster. - -**Positional arguments:** - -```` - Name of the cluster to create. - -**Optional arguments:** - -``-p , --profile `` - Profile Id or name used for this cluster. - -``-n , --min-size `` - Min size of the cluster. Default to 0. - -``-m , --max-size `` - Max size of the cluster. Default to -1, means - unlimited. - -``-c , --desired-capacity `` - Desired capacity of the cluster. Default to min_size - if min_size is specified else 0. - -``-t , --timeout `` - Cluster creation timeout in seconds. - -``-M <"KEY1=VALUE1;KEY2=VALUE2...">, --metadata <"KEY1=VALUE1;KEY2=VALUE2...">`` - Metadata values to be attached to the cluster. This - can - be - specified - multiple - times, - or - once - with - key-value - pairs - separated - by - a - semicolon. - -.. _senlin_cluster-delete: - -senlin cluster-delete ---------------------- - -.. code-block:: console - - usage: senlin cluster-delete [ ...] - -Delete the cluster(s). - -**Positional arguments:** - -```` - Name or ID of cluster(s) to delete. - -.. _senlin_cluster-list: - -senlin cluster-list -------------------- - -.. code-block:: console - - usage: senlin cluster-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] [-o ] - [-l ] [-m ] [-g] [-F] - -List the user's clusters. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned clusters. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-l , --limit `` - Limit the number of clusters returned. - -``-m , --marker `` - Only return clusters that appear after the given - cluster ID. - -``-g, --global-project`` - Indicate that the cluster list should include clusters - from all projects. This option is subject to access - policy checking. Default is False. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_cluster-node-add: - -senlin cluster-node-add ------------------------ - -.. code-block:: console - - usage: senlin cluster-node-add -n - -Add specified nodes to cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-n , --nodes `` - ID of nodes to be added; multiple nodes can be - separated with "," - -.. _senlin_cluster-node-del: - -senlin cluster-node-del ------------------------ - -.. code-block:: console - - usage: senlin cluster-node-del -n [-d ] - -Delete specified nodes from cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-n , --nodes `` - ID of nodes to be deleted; multiple nodes can be - separated with ",". - -``-d , --destroy-after-deletion `` - Whether nodes should be destroyed after deleted. - Default is False. - -.. _senlin_cluster-node-list: - -senlin cluster-node-list ------------------------- - -.. code-block:: console - - usage: senlin cluster-node-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] - [-l ] [-m ] [-F] - - -List nodes from cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to nodes from. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned nodes. This can - be specified multiple times, or once with parameters - separated by a semicolon. - -``-l , --limit `` - Limit the number of nodes returned. - -``-m , --marker `` - Only return nodes that appear after the given node ID. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_cluster-node-replace: - -senlin cluster-node-replace ---------------------------- - -.. code-block:: console - - usage: senlin cluster-node-replace -n - -Replace the nodes in cluster with specified nodes. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-n , --nodes `` - OLD_NODE is the name or ID of a node to be replaced, - NEW_NODE is the name or ID of a node as replacement. - This can be specified multiple times, or once with - node-pairs separated by a comma ','. - -.. _senlin_cluster-policy-attach: - -senlin cluster-policy-attach ----------------------------- - -.. code-block:: console - - usage: senlin cluster-policy-attach -p [-e ] - -Attach policy to cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-p , --policy `` - ID or name of policy to be attached. - -``-e , --enabled `` - Whether the policy should be enabled once attached. - Default to enabled. - -.. _senlin_cluster-policy-detach: - -senlin cluster-policy-detach ----------------------------- - -.. code-block:: console - - usage: senlin cluster-policy-detach -p - -Detach policy from cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-p , --policy `` - ID or name of policy to be detached. - -.. _senlin_cluster-policy-list: - -senlin cluster-policy-list --------------------------- - -.. code-block:: console - - usage: senlin cluster-policy-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] - [-o ] [-F] - - -List policies from cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to query on. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned results. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_cluster-policy-show: - -senlin cluster-policy-show --------------------------- - -.. code-block:: console - - usage: senlin cluster-policy-show -p - -Show a specific policy that is bound to the specified cluster. - -**Positional arguments:** - -```` - ID or name of the cluster to query on. - -**Optional arguments:** - -``-p , --policy `` - ID or name of the policy to query on. - -.. _senlin_cluster-policy-update: - -senlin cluster-policy-update ----------------------------- - -.. code-block:: console - - usage: senlin cluster-policy-update -p [-e ] - -Update a policy's properties on a cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-p , --policy `` - ID or name of policy to be updated. - -``-e , --enabled `` - Whether the policy should be enabled. - -.. _senlin_cluster-recover: - -senlin cluster-recover ----------------------- - -.. code-block:: console - - usage: senlin cluster-recover [ ...] - -Recover the cluster(s). - -**Positional arguments:** - -```` - ID or name of cluster(s) to operate on. - -.. _senlin_cluster-resize: - -senlin cluster-resize ---------------------- - -.. code-block:: console - - usage: senlin cluster-resize [-c ] [-a ] - [-p ] [-t ] [-s] [-n MIN] - [-m MAX] - - -Resize a cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-c , --capacity `` - The desired number of nodes of the cluster. - -``-a , --adjustment `` - A positive integer meaning the number of nodes to add, - or a negative integer indicating the number of nodes - to remove. - -``-p , --percentage `` - A value that is interpreted as the percentage of size - adjustment. This value can be positive or negative. - -``-t , --min-step `` - An integer specifying the number of nodes for - adjustment when is specified. - -``-s, --strict A`` - boolean specifying whether the resize should be - performed on a best-effort basis when the new capacity - may go beyond size constraints. - -``-n MIN, --min-size MIN`` - New lower bound of cluster size. - -``-m MAX, --max-size MAX`` - New upper bound of cluster size. A value of -1 - indicates no upper limit on cluster size. - -.. _senlin_cluster-run: - -senlin cluster-run ------------------- - -.. code-block:: console - - usage: senlin cluster-run [-p ] [-t ADDRESS_TYPE] [-n ] [-6] - [-u ] [-i IDENTITY_FILE] [-O SSH_OPTIONS] -s - - - -Run shell scripts on all nodes of a cluster. - -**Positional arguments:** - -```` - Name or ID of the cluster. - -**Optional arguments:** - -``-p , --port `` - Optional flag to indicate the port to use - (Default=22). - -``-t ADDRESS_TYPE, --address-type ADDRESS_TYPE`` - Optional flag to indicate which IP type to use. - Possible values includes 'fixed' and 'floating' (the - Default). - -``-n , --network `` - Network to use for the ssh. - -``-6, --ipv6`` - Optional flag to indicate whether to use an IPv6 - address attached to a server. (Defaults to IPv4 - address) - -``-u , --user `` - Login to use. - -``-i IDENTITY_FILE, --identity-file IDENTITY_FILE`` - Private key file, same as the '-i' option to the ssh - command. - -``-O SSH_OPTIONS, --ssh-options SSH_OPTIONS`` - Extra options to pass to ssh. see: man ssh. - -``-s , --script `` - Script file to run. - -.. _senlin_cluster-scale-in: - -senlin cluster-scale-in ------------------------ - -.. code-block:: console - - usage: senlin cluster-scale-in [-c ] - -Scale in a cluster by the specified number of nodes. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-c , --count `` - Number of nodes to be deleted from the specified - cluster. - -.. _senlin_cluster-scale-out: - -senlin cluster-scale-out ------------------------- - -.. code-block:: console - - usage: senlin cluster-scale-out [-c ] - -Scale out a cluster by the specified number of nodes. - -**Positional arguments:** - -```` - Name or ID of cluster to operate on. - -**Optional arguments:** - -``-c , --count `` - Number of nodes to be added to the specified cluster. - -.. _senlin_cluster-show: - -senlin cluster-show -------------------- - -.. code-block:: console - - usage: senlin cluster-show - -Show details of the cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to show. - -.. _senlin_cluster-update: - -senlin cluster-update ---------------------- - -.. code-block:: console - - usage: senlin cluster-update [-p ] [-P ] [-t ] - [-M <"KEY1=VALUE1;KEY2=VALUE2...">] [-n ] - - -Update the cluster. - -**Positional arguments:** - -```` - Name or ID of cluster to be updated. - -**Optional arguments:** - -``-p , --profile `` - ID or name of new profile to use. - -``-P , --profile-only `` - Whether the cluster should be updated profile only. If - false, it will be applied to all existing nodes. If - true, any newly created nodes will use the new - profile, but existing nodes will not be changed. - Default is False. - -``-t , --timeout `` - New timeout (in seconds) value for the cluster. - -``-M <"KEY1=VALUE1;KEY2=VALUE2...">, --metadata <"KEY1=VALUE1;KEY2=VALUE2...">`` - Metadata values to be attached to the cluster. This - can - be - specified - multiple - times, - or - once - with - key-value - pairs - separated - by - a - semicolon. - Use - '{}' - can - clean metadata - -``-n , --name `` - New name for the cluster to update. - -.. _senlin_event-list: - -senlin event-list ------------------ - -.. code-block:: console - - usage: senlin event-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] [-l ] - [-m ] [-o ] [-g] [-F] - -List events. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned events. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-l , --limit `` - Limit the number of events returned. - -``-m , --marker `` - Only return events that appear after the given event - ID. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-g, --global-project`` - Whether events from all projects should be listed. - Default to False. Setting this to True may demand for - an admin privilege. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_event-show: - -senlin event-show ------------------ - -.. code-block:: console - - usage: senlin event-show - -Describe the event. - -**Positional arguments:** - -```` - ID of event to display details for. - -.. _senlin_node-check: - -senlin node-check ------------------ - -.. code-block:: console - - usage: senlin node-check [ ...] - -Check the node(s). - -**Positional arguments:** - -```` - ID or name of node(s) to check. - -.. _senlin_node-create: - -senlin node-create ------------------- - -.. code-block:: console - - usage: senlin node-create -p [-c ] [-r ] - [-M <"KEY1=VALUE1;KEY2=VALUE2...">] - - -Create the node. - -**Positional arguments:** - -```` - Name of the node to create. - -**Optional arguments:** - -``-p , --profile `` - Profile Id or name used for this node. - -``-c , --cluster `` - Cluster Id for this node. - -``-r , --role `` - Role for this node in the specific cluster. - -``-M <"KEY1=VALUE1;KEY2=VALUE2...">, --metadata <"KEY1=VALUE1;KEY2=VALUE2...">`` - Metadata values to be attached to the node. This can - be specified multiple times, or once with key-value - pairs separated by a semicolon. - -.. _senlin_node-delete: - -senlin node-delete ------------------- - -.. code-block:: console - - usage: senlin node-delete [ ...] - -Delete the node(s). - -**Positional arguments:** - -```` - Name or ID of node(s) to delete. - -.. _senlin_node-list: - -senlin node-list ----------------- - -.. code-block:: console - - usage: senlin node-list [-c ] [-f <"KEY1=VALUE1;KEY2=VALUE2...">] - [-o ] [-l ] [-m ] [-g] [-F] - -Show list of nodes. - -**Optional arguments:** - -``-c , --cluster `` - ID or name of cluster from which nodes are to be - listed. - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned nodes. This can - be specified multiple times, or once with parameters - separated by a semicolon. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-l , --limit `` - Limit the number of nodes returned. - -``-m , --marker `` - Only return nodes that appear after the given node ID. - -``-g, --global-project`` - Indicate that this node list should include nodes from - all projects. This option is subject to access policy - checking. Default is False. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_node-recover: - -senlin node-recover -------------------- - -.. code-block:: console - - usage: senlin node-recover [-c ] [ ...] - -Recover the node(s). - -**Positional arguments:** - -```` - ID or name of node(s) to recover. - -**Optional arguments:** - -``-c , --check `` - Whether the node(s) should check physical resource - status before doing node recover.Default is false - -.. _senlin_node-show: - -senlin node-show ----------------- - -.. code-block:: console - - usage: senlin node-show [-D] - -Show detailed info about the specified node. - -**Positional arguments:** - -```` - Name or ID of the node to show the details for. - -**Optional arguments:** - -``-D, --details`` - Include physical object details. - -.. _senlin_node-update: - -senlin node-update ------------------- - -.. code-block:: console - - usage: senlin node-update [-n ] [-p ] [-r ] - [-M <"KEY1=VALUE1;KEY2=VALUE2...">] - - -Update the node. - -**Positional arguments:** - -```` - Name or ID of node to update. - -**Optional arguments:** - -``-n , --name `` - New name for the node. - -``-p , --profile `` - ID or name of new profile to use. - -``-r , --role `` - Role for this node in the specific cluster. - -``-M <"KEY1=VALUE1;KEY2=VALUE2...">, --metadata <"KEY1=VALUE1;KEY2=VALUE2...">`` - Metadata values to be attached to the node. This can - be specified multiple times, or once with key-value - pairs separated by a semicolon. Use '{}' can clean - metadata - -.. _senlin_policy-create: - -senlin policy-create --------------------- - -.. code-block:: console - - usage: senlin policy-create -s - -Create a policy. - -**Positional arguments:** - -```` - Name of the policy to create. - -**Optional arguments:** - -``-s , --spec-file `` - The spec file used to create the policy. - -.. _senlin_policy-delete: - -senlin policy-delete --------------------- - -.. code-block:: console - - usage: senlin policy-delete [ ...] - -Delete policy(s). - -**Positional arguments:** - -```` - Name or ID of policy(s) to delete. - -.. _senlin_policy-list: - -senlin policy-list ------------------- - -.. code-block:: console - - usage: senlin policy-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] [-l ] - [-m ] [-o ] [-g] [-F] - -List policies that meet the criteria. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned policies. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-l , --limit `` - Limit the number of policies returned. - -``-m , --marker `` - Only return policies that appear after the given ID. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-g, --global-project`` - Indicate that the list should include policies from - all projects. This option is subject to access policy - checking. Default is False. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_policy-show: - -senlin policy-show ------------------- - -.. code-block:: console - - usage: senlin policy-show - -Show the policy details. - -**Positional arguments:** - -```` - Name or ID of the policy to be shown. - -.. _senlin_policy-type-list: - -senlin policy-type-list ------------------------ - -.. code-block:: console - - usage: senlin policy-type-list - -List the available policy types. - -.. _senlin_policy-type-show: - -senlin policy-type-show ------------------------ - -.. code-block:: console - - usage: senlin policy-type-show [-F ] - -Get the details about a policy type. - -**Positional arguments:** - -```` - Policy type to retrieve. - -**Optional arguments:** - -``-F , --format `` - The template output format, one of: yaml, json. - -.. _senlin_policy-update: - -senlin policy-update --------------------- - -.. code-block:: console - - usage: senlin policy-update [-n ] - -Update a policy. - -**Positional arguments:** - -```` - Name of the policy to be updated. - -**Optional arguments:** - -``-n , --name `` - New name of the policy to be updated. - -.. _senlin_policy-validate: - -senlin policy-validate ----------------------- - -.. code-block:: console - - usage: senlin policy-validate -s - -Validate a policy spec. - -**Optional arguments:** - -``-s , --spec-file `` - The spec file of the policy to be validated. - -.. _senlin_profile-create: - -senlin profile-create ---------------------- - -.. code-block:: console - - usage: senlin profile-create -s - [-M <"KEY1=VALUE1;KEY2=VALUE2...">] - - -Create a profile. - -**Positional arguments:** - -```` - Name of the profile to create. - -**Optional arguments:** - -``-s , --spec-file `` - The spec file used to create the profile. - -``-M <"KEY1=VALUE1;KEY2=VALUE2...">, --metadata <"KEY1=VALUE1;KEY2=VALUE2...">`` - Metadata values to be attached to the profile. This - can - be - specified - multiple - times, - or - once - with - key-value - pairs - separated - by - a - semicolon. - -.. _senlin_profile-delete: - -senlin profile-delete ---------------------- - -.. code-block:: console - - usage: senlin profile-delete [ ...] - -Delete profile(s). - -**Positional arguments:** - -```` - Name or ID of profile(s) to delete. - -.. _senlin_profile-list: - -senlin profile-list -------------------- - -.. code-block:: console - - usage: senlin profile-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] [-l ] - [-m ] [-o ] [-g] [-F] - -List profiles that meet the criteria. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned profiles. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-l , --limit `` - Limit the number of profiles returned. - -``-m , --marker `` - Only return profiles that appear after the given ID. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-g, --global-project`` - Indicate that the list should include profiles from - all projects. This option is subject to access policy - checking. Default is False. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_profile-show: - -senlin profile-show -------------------- - -.. code-block:: console - - usage: senlin profile-show - -Show the profile details. - -**Positional arguments:** - -```` - Name or ID of profile to show. - -.. _senlin_profile-type-list: - -senlin profile-type-list ------------------------- - -.. code-block:: console - - usage: senlin profile-type-list - -List the available profile types. - -.. _senlin_profile-type-show: - -senlin profile-type-show ------------------------- - -.. code-block:: console - - usage: senlin profile-type-show [-F ] - -Get the details about a profile type. - -**Positional arguments:** - -```` - Profile type to retrieve. - -**Optional arguments:** - -``-F , --format `` - The template output format, one of: yaml, json. - -.. _senlin_profile-update: - -senlin profile-update ---------------------- - -.. code-block:: console - - usage: senlin profile-update [-n ] [-M <"KEY1=VALUE1;KEY2=VALUE2...">] - - -Update a profile. - -**Positional arguments:** - -```` - Name or ID of the profile to update. - -**Optional arguments:** - -``-n , --name `` - The new name for the profile. - -``-M <"KEY1=VALUE1;KEY2=VALUE2...">, --metadata <"KEY1=VALUE1;KEY2=VALUE2...">`` - Metadata values to be attached to the profile. This - can - be - specified - multiple - times, - or - once - with - key-value - pairs - separated - by - a - semicolon. - Use - '{}' - can - clean metadata - -.. _senlin_profile-validate: - -senlin profile-validate ------------------------ - -.. code-block:: console - - usage: senlin profile-validate -s - -Validate a profile. - -**Optional arguments:** - -``-s , --spec-file `` - The spec file of the profile to be validated. - -.. _senlin_receiver-create: - -senlin receiver-create ----------------------- - -.. code-block:: console - - usage: senlin receiver-create [-t ] [-c ] [-a ] - [-P <"KEY1=VALUE1;KEY2=VALUE2...">] - - -Create a receiver. - -**Positional arguments:** - -```` - Name of the receiver to create. - -**Optional arguments:** - -``-t , --type `` - Type of the receiver to create. Receiver type can be - "webhook" or "message". Default to "webhook". - -``-c , --cluster `` - Targeted cluster for this receiver. Required if - receiver type is webhook. - -``-a , --action `` - Name or ID of the targeted action to be triggered. - Required if receiver type is webhook. - -``-P <"KEY1=VALUE1;KEY2=VALUE2...">, --params <"KEY1=VALUE1;KEY2=VALUE2...">`` - A dictionary of parameters that will be passed to - target action when the receiver is triggered. - -.. _senlin_receiver-delete: - -senlin receiver-delete ----------------------- - -.. code-block:: console - - usage: senlin receiver-delete [ ...] - -Delete receiver(s). - -**Positional arguments:** - -```` - Name or ID of receiver(s) to delete. - -.. _senlin_receiver-list: - -senlin receiver-list --------------------- - -.. code-block:: console - - usage: senlin receiver-list [-f <"KEY1=VALUE1;KEY2=VALUE2...">] [-l ] - [-m ] [-o ] [-g] [-F] - -List receivers that meet the criteria. - -**Optional arguments:** - -``-f <"KEY1=VALUE1;KEY2=VALUE2...">, --filters <"KEY1=VALUE1;KEY2=VALUE2...">`` - Filter parameters to apply on returned receivers. This - can be specified multiple times, or once with - parameters separated by a semicolon. - -``-l , --limit `` - Limit the number of receivers returned. - -``-m , --marker `` - Only return receivers that appear after the given ID. - -``-o , --sort `` - Sorting option which is a string containing a list of - keys separated by commas. Each key can be optionally - appended by a sort direction (:asc or :desc) - -``-g, --global-project`` - Indicate that the list should include receivers from - all projects. This option is subject to access policy - checking. Default is False. - -``-F, --full-id`` - Print full IDs in list. - -.. _senlin_receiver-show: - -senlin receiver-show --------------------- - -.. code-block:: console - - usage: senlin receiver-show - -Show the receiver details. - -**Positional arguments:** - -```` - Name or ID of the receiver to show. - diff --git a/doc/source/conf.py b/doc/source/conf.py index 49b3e603..d29233ac 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -247,11 +247,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('man/senlin', 'senlin', - u'Command line reference for Senlin', - [u'Senlin Developers'], 1) -] +# man_pages = [] # If true, show URL addresses after external links. # man_show_urls = False diff --git a/doc/source/index.rst b/doc/source/index.rst index 2cd3d310..5b76f575 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -8,7 +8,6 @@ Contents: :maxdepth: 2 install/index - cli/index Indices and tables diff --git a/doc/source/man/senlin.rst b/doc/source/man/senlin.rst deleted file mode 100644 index 6023c15c..00000000 --- a/doc/source/man/senlin.rst +++ /dev/null @@ -1,82 +0,0 @@ -:orphan: - -====== -senlin -====== - -.. program:: senlin - -SYNOPSIS -======== - - `senlin` [options] [command-options] - - `senlin help` - - `senlin help` - - -DESCRIPTION -=========== - -`senlin` is a command line client for controlling OpenStack Senlin. - -Before the `senlin` command is issued, ensure the environment contains -the necessary variables so that the CLI can pass user credentials to -the server. -See `Getting Credentials for a CLI` section of `OpenStack CLI Guide` -for more info. - - -OPTIONS -======= - -To get a list of available commands and options run:: - - senlin help - -To get usage and options of a command run:: - - senlin help - - -EXAMPLES -======== - -Get information about profile-create command:: - - senlin help profile-create - -List available profiles:: - - senlin profile-list - -List available clusters:: - - senlin cluster-list - -Create a profile:: - - senlin profile-create -s profile.spec myprofile - -View profile information:: - - senlin profile-show myprofile - -Create a cluster:: - - senlin cluster-create -p myprofile -n 2 mycluster - -List events:: - - senlin event-list - -Delete a cluster:: - - senlin cluster-delete mycluster - -BUGS -==== - -Senlin client is hosted in Launchpad so you can view current bugs -at https://bugs.launchpad.net/python-senlinclient/. diff --git a/senlinclient/cliargs.py b/senlinclient/cliargs.py deleted file mode 100644 index e2f28dc1..00000000 --- a/senlinclient/cliargs.py +++ /dev/null @@ -1,180 +0,0 @@ -# Licensed 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 argparse - -from senlinclient.common.i18n import _ -from senlinclient.common import utils - - -def add_global_identity_args(parser): - parser.add_argument( - '--os-auth-plugin', dest='auth_plugin', metavar='AUTH_PLUGIN', - default=utils.env('OS_AUTH_PLUGIN', default=None), - help=_('Authentication plugin, default to env[OS_AUTH_PLUGIN]')) - - parser.add_argument( - '--os-auth-url', dest='auth_url', metavar='AUTH_URL', - default=utils.env('OS_AUTH_URL'), - help=_('Defaults to env[OS_AUTH_URL]')) - - parser.add_argument( - '--os-project-id', dest='project_id', metavar='PROJECT_ID', - default=utils.env('OS_PROJECT_ID'), - help=_('Defaults to env[OS_PROJECT_ID].')) - - parser.add_argument( - '--os-project-name', dest='project_name', metavar='PROJECT_NAME', - default=utils.env('OS_PROJECT_NAME'), - help=_('Defaults to env[OS_PROJECT_NAME].')) - - parser.add_argument( - '--os-tenant-id', dest='tenant_id', metavar='TENANT_ID', - default=utils.env('OS_TENANT_ID'), - help=_('Defaults to env[OS_TENANT_ID].')) - - parser.add_argument( - '--os-tenant-name', dest='tenant_name', metavar='TENANT_NAME', - default=utils.env('OS_TENANT_NAME'), - help=_('Defaults to env[OS_TENANT_NAME].')) - - parser.add_argument( - '--os-domain-id', dest='domain_id', metavar='DOMAIN_ID', - default=utils.env('OS_DOMAIN_ID'), - help=_('Domain ID for scope of authorization, defaults to ' - 'env[OS_DOMAIN_ID].')) - - parser.add_argument( - '--os-domain-name', dest='domain_name', metavar='DOMAIN_NAME', - default=utils.env('OS_DOMAIN_NAME'), - help=_('Domain name for scope of authorization, defaults to ' - 'env[OS_DOMAIN_NAME].')) - - parser.add_argument( - '--os-project-domain-id', dest='project_domain_id', - metavar='PROJECT_DOMAIN_ID', - default=utils.env('OS_PROJECT_DOMAIN_ID'), - help=_('Project domain ID for scope of authorization, defaults to ' - 'env[OS_PROJECT_DOMAIN_ID].')) - - parser.add_argument( - '--os-project-domain-name', dest='project_domain_name', - metavar='PROJECT_DOMAIN_NAME', - default=utils.env('OS_PROJECT_DOMAIN_NAME'), - help=_('Project domain name for scope of authorization, defaults to ' - 'env[OS_PROJECT_DOMAIN_NAME].')) - - parser.add_argument( - '--os-user-domain-id', dest='user_domain_id', - metavar='USER_DOMAIN_ID', - default=utils.env('OS_USER_DOMAIN_ID'), - help=_('User domain ID for scope of authorization, defaults to ' - 'env[OS_USER_DOMAIN_ID].')) - - parser.add_argument( - '--os-user-domain-name', dest='user_domain_name', - metavar='USER_DOMAIN_NAME', - default=utils.env('OS_USER_DOMAIN_NAME'), - help=_('User domain name for scope of authorization, defaults to ' - 'env[OS_USER_DOMAIN_NAME].')) - - parser.add_argument( - '--os-username', dest='username', metavar='USERNAME', - default=utils.env('OS_USERNAME'), - help=_('Defaults to env[OS_USERNAME].')) - - parser.add_argument( - '--os-user-id', dest='user_id', metavar='USER_ID', - default=utils.env('OS_USER_ID'), - help=_('Defaults to env[OS_USER_ID].')) - - parser.add_argument( - '--os-password', dest='password', metavar='PASSWORD', - default=utils.env('OS_PASSWORD'), - help=_('Defaults to env[OS_PASSWORD]')) - - parser.add_argument( - '--os-trust-id', dest='trust_id', metavar='TRUST_ID', - default=utils.env('OS_TRUST_ID'), - help=_('Defaults to env[OS_TRUST_ID]')) - - verify_group = parser.add_mutually_exclusive_group() - - verify_group.add_argument( - '--os-cacert', dest='verify', metavar='CA_BUNDLE_FILE', - default=utils.env('OS_CACERT', default=True), - help=_('Path of CA TLS certificate(s) used to verify the remote ' - 'server\'s certificate. Without this option senlin looks ' - 'for the default system CA certificates.')) - - verify_group.add_argument( - '--verify', - action='store_true', - help=_('Verify server certificate (default)')) - - verify_group.add_argument( - '--insecure', dest='verify', action='store_false', - help=_('Explicitly allow senlinclient to perform "insecure SSL" ' - '(HTTPS) requests. The server\'s certificate will not be ' - 'verified against any certificate authorities. This ' - 'option should be used with caution.')) - - parser.add_argument( - '--os-token', dest='token', metavar='TOKEN', - default=utils.env('OS_TOKEN', default=None), - help=_('A string token to bootstrap the Keystone database, defaults ' - 'to env[OS_TOKEN]')) - - parser.add_argument( - '--os-access-info', dest='access_info', metavar='ACCESS_INFO', - default=utils.env('OS_ACCESS_INFO'), - help=_('Access info, defaults to env[OS_ACCESS_INFO]')) - -# parser.add_argument( -# '--os-cert', -# help=_('Path of certificate file to use in SSL connection. This ' -# 'file can optionally be prepended with the private key.')) -# -# parser.add_argument( -# '--os-key', -# help=_('Path of client key to use in SSL connection. This option is ' -# 'not necessary if your key is prepended to your cert file.')) - - -def add_global_args(parser, version): - # GLOBAL ARGUMENTS - parser.add_argument( - '-h', '--help', action='store_true', - help=argparse.SUPPRESS) - - parser.add_argument( - '--version', action='version', version=version, - help=_("Shows the client version and exits.")) - - parser.add_argument( - '-d', '--debug', action='store_true', - default=bool(utils.env('SENLINCLIENT_DEBUG')), - help=_('Defaults to env[SENLINCLIENT_DEBUG].')) - - parser.add_argument( - '-v', '--verbose', action="store_true", default=False, - help=_("Print more verbose output.")) - - parser.add_argument( - '--api-timeout', - help=_('Number of seconds to wait for an API response, ' - 'defaults to system socket timeout')) - - parser.add_argument( - '--senlin-api-version', - default=utils.env('SENLIN_API_VERSION', default='1'), - help=_('Version number for Senlin API to use, Default to "1".')) diff --git a/senlinclient/client.py b/senlinclient/client.py deleted file mode 100644 index 3d9c751f..00000000 --- a/senlinclient/client.py +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed 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. - -from senlinclient.common import utils - - -def Client(api_ver, *args, **kwargs): - """Import versioned client module. - - :param api_ver: API version required. - """ - module = utils.import_versioned_module(api_ver, 'client') - cls = getattr(module, 'Client') - return cls(*args, **kwargs) diff --git a/senlinclient/common/utils.py b/senlinclient/common/utils.py index 39c20e25..5f51661d 100644 --- a/senlinclient/common/utils.py +++ b/senlinclient/common/utils.py @@ -12,13 +12,9 @@ from __future__ import print_function -import os -import sys from heatclient.common import template_utils from oslo_serialization import jsonutils -from oslo_utils import encodeutils -from oslo_utils import importutils import prettytable import six import yaml @@ -27,46 +23,6 @@ from senlinclient.common import exc from senlinclient.common.i18n import _ -supported_formats = { - "json": lambda x: jsonutils.dumps(x, indent=2), - "yaml": lambda x: yaml.safe_dump(x, default_flow_style=False) -} - - -def arg(*args, **kwargs): - """Decorator for CLI args.""" - - def _decorator(func): - if not hasattr(func, 'arguments'): - func.arguments = [] - - if (args, kwargs) not in func.arguments: - func.arguments.insert(0, (args, kwargs)) - - return func - - return _decorator - - -def env(*args, **kwargs): - """Returns the first environment variable set. - - If all are empty, defaults to '' or keyword arg `default`. - """ - for arg in args: - value = os.environ.get(arg) - if value: - return value - return kwargs.get('default', '') - - -def import_versioned_module(version, submodule=None): - module = 'senlinclient.v%s' % version - if submodule: - module = '.'.join((module, submodule)) - return importutils.import_module(module) - - def format_nested_dict(d, fields, column_names): if d is None: return '' @@ -99,95 +55,6 @@ def list_formatter(record): return '\n'.join(record or []) -def _print_list(objs, fields, formatters=None, sortby_index=0, - mixed_case_fields=None, field_labels=None): - """Print a list of objects as a table, one row per object. - - :param objs: iterable of :class:`Resource` - :param fields: attributes that correspond to columns, in order - :param formatters: `dict` of callables for field formatting - :param sortby_index: index of the field for sorting table rows - :param mixed_case_fields: fields corresponding to object attributes that - have mixed case names (e.g., 'serverId') - :param field_labels: Labels to use in the heading of the table, default to - fields. - """ - formatters = formatters or {} - mixed_case_fields = mixed_case_fields or [] - field_labels = field_labels or fields - if len(field_labels) != len(fields): - raise ValueError(_("Field labels list %(labels)s has different number " - "of elements than fields list %(fields)s"), - {'labels': field_labels, 'fields': fields}) - - if sortby_index is None: - kwargs = {} - else: - kwargs = {'sortby': field_labels[sortby_index]} - pt = prettytable.PrettyTable(field_labels) - pt.align = 'l' - - for o in objs: - row = [] - for field in fields: - if field in formatters: - data = formatters[field](o) - else: - if field in mixed_case_fields: - field_name = field.replace(' ', '_') - else: - field_name = field.lower().replace(' ', '_') - data = getattr(o, field_name, '') - if data is None: - data = '-' - row.append(data) - pt.add_row(row) - - if six.PY3: - return encodeutils.safe_encode(pt.get_string(**kwargs)).decode() - else: - return encodeutils.safe_encode(pt.get_string(**kwargs)) - - -def print_list(objs, fields, formatters=None, sortby_index=0, - mixed_case_fields=None, field_labels=None): - # This wrapper is needed because sdk may yield a generator that will - # escape the exception catching previously - if not objs: - objs = [] - - try: - res = _print_list(objs, fields, formatters=formatters, - sortby_index=sortby_index, - mixed_case_fields=mixed_case_fields, - field_labels=field_labels) - print(res) - except Exception as ex: - exc.parse_exception(ex) - - -def print_dict(d, formatters=None): - formatters = formatters or {} - pt = prettytable.PrettyTable(['Property', 'Value'], - caching=False, print_empty=False) - pt.align = 'l' - - for field in d.keys(): - if field in formatters: - data = formatters[field](d[field]) - else: - data = d[field] - if data is None: - data = '-' - pt.add_row([field, data]) - - content = pt.get_string(sortby='Property') - if six.PY3: - print(encodeutils.safe_encode(content).decode()) - else: - print(encodeutils.safe_encode(content)) - - def print_action_result(rid, res): if res[0] == "OK": output = _("accepted by action %s") % res[1] @@ -281,18 +148,3 @@ def process_stack_spec(spec): } return new_spec - - -def format_output(output, format='yaml'): - fmt = format.lower() - try: - return supported_formats[fmt](output) - except KeyError: - raise exc.HTTPUnsupported(_('The format(%s) is unsupported.') - % fmt) - - -def exit(msg=''): - if msg: - print(msg, file=sys.stderr) - sys.exit(1) diff --git a/senlinclient/shell.py b/senlinclient/shell.py deleted file mode 100644 index 057fb6c1..00000000 --- a/senlinclient/shell.py +++ /dev/null @@ -1,312 +0,0 @@ -# Licensed 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. - -""" -Command-line interface to the Senlin clustering API. -""" - -from __future__ import print_function - -import argparse -import logging -import sys - -from oslo_utils import encodeutils -from oslo_utils import importutils -import six - -import senlinclient -from senlinclient import cliargs -from senlinclient import client as senlin_client -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import utils - -osprofiler_profiler = importutils.try_import("osprofiler.profiler") -USER_AGENT = 'python-senlinclient' -LOG = logging.getLogger(__name__) - - -class HelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(HelpFormatter, self).start_section(heading) - - -class SenlinShell(object): - def _setup_logging(self, debug): - log_lvl = logging.DEBUG if debug else logging.WARNING - logging.basicConfig(format="%(levelname)s (%(module)s) %(message)s", - level=log_lvl) - logging.getLogger('iso8601').setLevel(logging.WARNING) - logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) - - def _setup_verbose(self, verbose): - if verbose: - exc.verbose = 1 - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - - # get callback documentation string - desc = callback.__doc__ or '' - help = desc.strip().split('\n')[0] - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser(command, - help=help, - description=desc, - add_help=False, - formatter_class=HelpFormatter) - - subparser.add_argument('-h', '--help', - action='help', - help=argparse.SUPPRESS) - - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - self.subcommands[command] = subparser - - def do_bash_completion(self, args): - """Prints all of the commands and options to stdout. - - The senlin.bash_completion script doesn't have to hard code them. - """ - commands = set() - options = set() - for sc_str, sc in self.subcommands.items(): - if sc_str == 'bash-completion': - continue - - commands.add(sc_str) - for option in list(sc._optionals._option_string_actions): - options.add(option) - - print(' '.join(commands | options)) - - def add_profiler_args(self, parser): - if osprofiler_profiler: - parser.add_argument( - '--os-profile', - metavar='HMAC_KEY', - default=utils.env('OS_PROFILE'), - help=_('HMAC key to use for encrypting context data for ' - 'performance profiling of operation. This key should ' - 'be the value of HMAC key configured in ' - 'senlin configuration (/etc/senlin/senlin.conf). ' - 'Without the key, profiling will not be triggered ' - 'even if osprofiler is enabled on server side.')) - - def get_subcommand_parser(self, base_parser, version): - parser = base_parser - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='') - submodule = utils.import_versioned_module(version, 'shell') - self._find_actions(subparsers, submodule) - self._find_actions(subparsers, self) - - return parser - - @utils.arg('command', metavar='', nargs='?', - help=_('Display help for .')) - def do_help(self, args): - """Display help about this program or one of its subcommands.""" - if getattr(args, 'command', None): - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError("'%s' is not a valid subcommand" % - args.command) - else: - self.parser.print_help() - - def _check_identity_arguments(self, args): - # TODO(Qiming): validate the token authentication path and the trust - # authentication path - - if not args.auth_url: - msg = _('You must provide an auth url via --os-auth-url (or ' - ' env[OS_AUTH_URL])') - raise exc.CommandError(msg) - - # username or user_id or token must be specified - if not (args.username or args.user_id or args.token): - msg = _('You must provide a user name, a user_id or a ' - 'token for authentication') - raise exc.CommandError(msg) - - # if both username and user_id are specified, user_id takes precedence - if (args.username and args.user_id): - msg = _('Both user name and user ID are specified, Senlin will ' - 'use user ID for authentication') - print(_('WARNING: %s') % msg) - - if 'v3' in args.auth_url: - if (args.username and not args.user_id): - if not (args.user_domain_id or args.user_domain_name): - msg = _('Either user domain ID (--user-domain-id / ' - 'env[OS_USER_DOMAIN_ID]) or user domain name ' - '(--user-domain-name / env[OS_USER_DOMAIN_NAME]) ' - 'must be specified, because user name may not be ' - 'unique.') - raise exc.CommandError(msg) - - # password is needed if username or user_id is present - if (args.username or args.user_id) and not (args.password): - msg = _('You must provide a password for user %s') % ( - args.username or args.user_id) - raise exc.CommandError(msg) - - # project name or ID is needed, or else sdk may find the wrong project - if (not (args.project_id or args.project_name or args.tenant_id or - args.tenant_name)): - if not (args.user_id): - msg = _('Either project/tenant ID or project/tenant name ' - 'must be specified, or else Senlin cannot know ' - 'which project to use.') - raise exc.CommandError(msg) - else: - msg = _('Neither project ID nor project name is specified. ' - 'Senlin will use user\'s default project which may ' - 'result in authentication error.') - print(_('WARNING: %s') % msg) - - # both project name and ID are specified, ID takes precedence - if ((args.project_id or args.tenant_id) and - (args.project_name or args.tenant_name)): - msg = _('Both project/tenant name and project/tenant ID are ' - 'specified, Senlin will use project ID for ' - 'authentication') - print(_('WARNING: %s') % msg) - - # project name may not be unique - if 'v3' in args.auth_url: - if (not (args.project_id or args.tenant_id) and - (args.project_name or args.tenant_name) and - not (args.project_domain_id or args.project_domain_name)): - msg = _('Either project domain ID (--project-domain-id / ' - 'env[OS_PROJECT_DOMAIN_ID]) orr project domain name ' - '(--project-domain-name / ' - 'env[OS_PROJECT_DOMAIN_NAME]) must be specified, ' - 'because project/tenant name may not be unique.') - raise exc.CommandError(msg) - - def _setup_senlin_client(self, api_ver, args): - """Create senlin client using given args.""" - kwargs = { - 'auth_plugin': args.auth_plugin or 'password', - 'auth_url': args.auth_url, - 'project_name': args.project_name or args.tenant_name, - 'project_id': args.project_id or args.tenant_id, - 'domain_name': args.domain_name, - 'domain_id': args.domain_id, - 'project_domain_name': args.project_domain_name, - 'project_domain_id': args.project_domain_id, - 'user_domain_name': args.user_domain_name, - 'user_domain_id': args.user_domain_id, - 'username': args.username, - 'user_id': args.user_id, - 'password': args.password, - 'verify': args.verify, - 'token': args.token, - 'trust_id': args.trust_id, - } - - return senlin_client.Client('1', user_agent=USER_AGENT, **kwargs) - - def main(self, argv): - # Parse args once to find version - parser = argparse.ArgumentParser( - prog='senlin', - description=__doc__.strip(), - epilog=_('Type "senlin help " for help on a specific ' - 'command.'), - add_help=False, - formatter_class=HelpFormatter, - ) - - cliargs.add_global_args(parser, version=senlinclient.__version__) - cliargs.add_global_identity_args(parser) - self.add_profiler_args(parser) - base_parser = parser - - (options, args) = base_parser.parse_known_args(argv) - - self._setup_logging(options.debug) - self._setup_verbose(options.verbose) - - # build available subcommands based on version - api_ver = options.senlin_api_version - LOG.info(api_ver) - subcommand_parser = self.get_subcommand_parser(base_parser, api_ver) - self.parser = subcommand_parser - - # Handle top-level --help/-h before attempting to parse - # a command off the command line - if not args and options.help or not argv: - self.do_help(options) - return 0 - - # Parse args again and call whatever callback was selected - args = subcommand_parser.parse_args(argv) - - # Short-circuit and deal with help command right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - elif args.func == self.do_bash_completion: - self.do_bash_completion(args) - return 0 - - # Check if identity information are sufficient - self._check_identity_arguments(args) - - # Setup Senlin client connection - sc = self._setup_senlin_client(api_ver, args) - - os_profile = osprofiler_profiler and options.os_profile - if os_profile: - osprofiler_profiler.init(options.os_profile) - - args.func(sc.service, args) - - if os_profile: - trace_id = osprofiler_profiler.get().get_base_id() - print(_("Trace ID: %s") % trace_id) - print(_("To display trace use next command:\n" - "osprofiler trace show --html %s ") % trace_id) - - -def main(args=None): - try: - if args is None: - args = sys.argv[1:] - - SenlinShell().main(args) - except KeyboardInterrupt: - print(_("... terminating senlin client"), file=sys.stderr) - return 130 - except Exception as e: - if '--debug' in args or '-d' in args: - raise - else: - print(encodeutils.safe_encode(six.text_type(e)), file=sys.stderr) - return 1 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/senlinclient/tests/unit/test_cliargs.py b/senlinclient/tests/unit/test_cliargs.py deleted file mode 100644 index 89833f38..00000000 --- a/senlinclient/tests/unit/test_cliargs.py +++ /dev/null @@ -1,74 +0,0 @@ -# Licensed 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 mock -import testtools - -from senlinclient import cliargs - - -class TestCliArgs(testtools.TestCase): - - def test_add_global_identity_args(self): - parser = mock.Mock() - - cliargs.add_global_identity_args(parser) - expected = [ - '--os-auth-plugin', - '--os-auth-url', - '--os-project-id', - '--os-project-name', - '--os-tenant-id', - '--os-tenant-name', - '--os-domain-id', - '--os-domain-name', - '--os-project-domain-id', - '--os-project-domain-name', - '--os-user-domain-id', - '--os-user-domain-name', - '--os-username', - '--os-user-id', - '--os-password', - '--os-trust-id', - '--os-token', - '--os-access-info', - ] - - options = [arg[0][0] for arg in parser.add_argument.call_args_list] - self.assertEqual(expected, options) - - parser.add_mutually_exclusive_group.assert_called_once_with() - group = parser.add_mutually_exclusive_group.return_value - - verify_opts = [arg[0][0] for arg in group.add_argument.call_args_list] - verify_args = [ - '--os-cacert', - '--verify', - '--insecure' - ] - self.assertEqual(verify_args, verify_opts) - - def test_add_global_args(self): - parser = mock.Mock() - - cliargs.add_global_args(parser, '1') - expected = [ - '-h', - '--version', - '-d', - '-v', - '--api-timeout', - '--senlin-api-version' - ] - - options = [arg[0][0] for arg in parser.add_argument.call_args_list] - self.assertEqual(expected, options) diff --git a/senlinclient/tests/unit/test_client.py b/senlinclient/tests/unit/test_client.py deleted file mode 100644 index 097a56c1..00000000 --- a/senlinclient/tests/unit/test_client.py +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed 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 mock -import testtools - -from senlinclient import client as sc -from senlinclient.common import utils - - -class FakeClient(object): - - def __init__(self, session): - super(FakeClient, self).__init__() - self.session = session - - -class ClientTest(testtools.TestCase): - - @mock.patch.object(utils, 'import_versioned_module') - def test_client_init(self, mock_import): - the_module = mock.Mock() - the_module.Client = FakeClient - mock_import.return_value = the_module - session = mock.Mock() - - res = sc.Client('FAKE_VER', session) - - mock_import.assert_called_once_with('FAKE_VER', 'client') - self.assertIsInstance(res, FakeClient) diff --git a/senlinclient/tests/unit/test_shell.py b/senlinclient/tests/unit/test_shell.py deleted file mode 100644 index 337e7cfb..00000000 --- a/senlinclient/tests/unit/test_shell.py +++ /dev/null @@ -1,356 +0,0 @@ -# Licensed 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 argparse -import logging -import sys - -import mock -import six -from six.moves import builtins -import testtools - -from senlinclient import client as senlin_client -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import sdk -from senlinclient.common import utils -from senlinclient import shell -from senlinclient.tests.unit import fakes - - -class HelpFormatterTest(testtools.TestCase): - - def test_start_section(self): - fmtr = shell.HelpFormatter('senlin') - res = fmtr.start_section(('heading', 'text1', 30)) - self.assertIsNone(res) - h = fmtr._current_section.heading - self.assertEqual("HEADING('text1', 30)", h) - - -class TestArgs(testtools.TestCase): - - def __init__(self): - self.auth_url = 'http://fakeurl/v3' - self.auth_plugin = 'test_plugin' - self.username = 'test_user_name' - self.user_id = 'test_user_id' - self.token = 'test_token' - self.project_id = 'test_project_id' - self.project_name = 'test_project_name' - self.tenant_id = 'test_tenant_id' - self.tenant_name = 'test_tenant_name' - self.password = 'test_password' - self.user_domain_id = 'test_user_domain_id' - self.user_domain_name = 'test_user_domain_name' - self.project_domain_id = 'test_project_domain_id' - self.project_domain_name = 'test_project_domain_name' - self.domain_name = 'test_domain_name' - self.domain_id = 'test_domain_id' - self.verify = 'test_verify' - self.user_preferences = 'test_preferences' - self.trust_id = 'test_trust' - - -class ShellTest(testtools.TestCase): - - def setUp(self): - super(ShellTest, self).setUp() - - def SHELL(self, func, *args, **kwargs): - orig_out = sys.stdout - sys.stdout = six.StringIO() - func(*args, **kwargs) - output = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig_out - - return output - - @mock.patch.object(logging, 'basicConfig') - @mock.patch.object(logging, 'getLogger') - def test_setup_logging_debug(self, x_get, x_config): - sh = shell.SenlinShell() - sh._setup_logging(True) - - x_config.assert_called_once_with( - format="%(levelname)s (%(module)s) %(message)s", - level=logging.DEBUG) - mock_calls = [ - mock.call('iso8601'), - mock.call().setLevel(logging.WARNING), - mock.call('urllib3.connectionpool'), - mock.call().setLevel(logging.WARNING), - ] - x_get.assert_has_calls(mock_calls) - - @mock.patch.object(logging, 'basicConfig') - @mock.patch.object(logging, 'getLogger') - def test_setup_logging_no_debug(self, x_get, x_config): - sh = shell.SenlinShell() - sh._setup_logging(False) - - x_config.assert_called_once_with( - format="%(levelname)s (%(module)s) %(message)s", - level=logging.WARNING) - mock_calls = [ - mock.call('iso8601'), - mock.call().setLevel(logging.WARNING), - mock.call('urllib3.connectionpool'), - mock.call().setLevel(logging.WARNING), - ] - x_get.assert_has_calls(mock_calls) - - def test_setup_verbose(self): - sh = shell.SenlinShell() - sh._setup_verbose(True) - self.assertEqual(1, exc.verbose) - - sh._setup_verbose(False) - self.assertEqual(1, exc.verbose) - - def test_find_actions(self): - sh = shell.SenlinShell() - sh.subcommands = {} - subparsers = mock.Mock() - x_subparser1 = mock.Mock() - x_subparser2 = mock.Mock() - x_add_parser = mock.Mock(side_effect=[x_subparser1, x_subparser2]) - subparsers.add_parser = x_add_parser - - # subparsers.add_parser = mock.Mock(return_value=x_subparser) - sh._find_actions(subparsers, fakes) - - self.assertEqual({'command-bar': x_subparser1, - 'command-foo': x_subparser2}, - sh.subcommands) - add_calls = [ - mock.call('command-bar', help='This is the command doc.', - description='This is the command doc.', - add_help=False, - formatter_class=shell.HelpFormatter), - mock.call('command-foo', help='Pydoc for command foo.', - description='Pydoc for command foo.', - add_help=False, - formatter_class=shell.HelpFormatter), - ] - x_add_parser.assert_has_calls(add_calls) - - calls_1 = [ - mock.call('-h', '--help', action='help', - help=argparse.SUPPRESS), - mock.call('-F', '--flag', metavar='', - help='Flag desc.'), - mock.call('arg1', metavar='', - help='Arg1 desc') - ] - x_subparser1.add_argument.assert_has_calls(calls_1) - x_subparser1.set_defaults.assert_called_once_with( - func=fakes.do_command_bar) - - calls_2 = [ - mock.call('-h', '--help', action='help', - help=argparse.SUPPRESS), - ] - x_subparser2.add_argument.assert_has_calls(calls_2) - x_subparser2.set_defaults.assert_called_once_with( - func=fakes.do_command_foo) - - def test_do_bash_completion(self): - sh = shell.SenlinShell() - sc1 = mock.Mock() - sc2 = mock.Mock() - sc1._optionals._option_string_actions = ('A1', 'A2', 'C') - sc2._optionals._option_string_actions = ('B1', 'B2', 'C') - sh.subcommands = { - 'command-foo': sc1, - 'command-bar': sc2, - 'bash-completion': None, - } - - output = self.SHELL(sh.do_bash_completion, None) - - output = output.split('\n')[0] - output_list = output.split(' ') - for option in ('A1', 'A2', 'C', 'B1', 'B2', - 'command-foo', 'command-bar'): - self.assertIn(option, output_list) - - def test_do_add_profiler_args(self): - sh = shell.SenlinShell() - parser = mock.Mock() - - sh.add_profiler_args(parser) - if shell.osprofiler_profiler: - self.assertEqual(1, parser.add_argument.call_count) - else: - self.assertEqual(0, parser.add_argument.call_count) - - @mock.patch.object(utils, 'import_versioned_module') - @mock.patch.object(shell.SenlinShell, '_find_actions') - def test_get_subcommand_parser(self, x_find, x_import): - x_base = mock.Mock() - x_module = mock.Mock() - x_import.return_value = x_module - sh = shell.SenlinShell() - - res = sh.get_subcommand_parser(x_base, 'v100') - - self.assertEqual(x_base, res) - x_base.add_subparsers.assert_called_once_with( - metavar='') - x_subparsers = x_base.add_subparsers.return_value - x_import.assert_called_once_with('v100', 'shell') - find_calls = [ - mock.call(x_subparsers, x_module), - mock.call(x_subparsers, sh) - ] - - x_find.assert_has_calls(find_calls) - - @mock.patch.object(argparse.ArgumentParser, 'print_help') - def test_do_help(self, mock_print): - sh = shell.SenlinShell() - args = mock.Mock() - args.command = mock.Mock() - sh.subcommands = {args.command: argparse.ArgumentParser} - sh.do_help(args) - self.assertTrue(mock_print.called) - - sh.subcommands = {} - ex = self.assertRaises(exc.CommandError, - sh.do_help, args) - msg = _("'%s' is not a valid subcommand") % args.command - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(builtins, 'print') - def test_check_identity_arguments(self, mock_print): - sh = shell.SenlinShell() - # auth_url is not specified. - args = TestArgs() - args.auth_url = None - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - msg = _('You must provide an auth url via --os-auth-url (or ' - ' env[OS_AUTH_URL])') - self.assertEqual(msg, six.text_type(ex)) - # username, user_id and token are not specified. - args = TestArgs() - args.username = None - args.user_id = None - args.token = None - msg = _('You must provide a user name, a user_id or a ' - 'token for authentication') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - # Both username and user_id are specified. - args = TestArgs() - args.project_id = None - args.tenant_id = None - sh._check_identity_arguments(args) - msg = _('WARNING: Both user name and user ID are specified, ' - 'Senlin will use user ID for authentication') - mock_print.assert_called_with(msg) - - # 'v3' in auth_url but neither user_domain_id nor user_domain_name - # is specified. - args = TestArgs() - args.user_id = None - args.user_domain_id = None - args.user_domain_name = None - msg = _('Either user domain ID (--user-domain-id / ' - 'env[OS_USER_DOMAIN_ID]) or user domain name ' - '(--user-domain-name / env[OS_USER_DOMAIN_NAME]) ' - 'must be specified, because user name may not be ' - 'unique.') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - # user_id, project_id, project_name, tenant_id and tenant_name are all - # not specified. - args = TestArgs() - args.project_id = None - args.project_name = None - args.tenant_id = None - args.tenant_name = None - args.user_id = None - msg = _('Either project/tenant ID or project/tenant name ' - 'must be specified, or else Senlin cannot know ' - 'which project to use.') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - args.user_id = 'test_user_id' - sh._check_identity_arguments(args) - msg = _('Neither project ID nor project name is specified. ' - 'Senlin will use user\'s default project which may ' - 'result in authentication error.') - mock_print.assert_called_with(_('WARNING: %s') % msg) - - # Both project_name and project_id are specified - args = TestArgs() - args.user_id = None - sh._check_identity_arguments(args) - msg = _('Both project/tenant name and project/tenant ID are ' - 'specified, Senlin will use project ID for ' - 'authentication') - mock_print.assert_called_with(_('WARNING: %s') % msg) - # Project name may not be unique - args = TestArgs() - args.user_id = None - args.project_id = None - args.tenant_id = None - args.project_domain_id = None - args.project_domain_name = None - msg = _('Either project domain ID (--project-domain-id / ' - 'env[OS_PROJECT_DOMAIN_ID]) orr project domain name ' - '(--project-domain-name / ' - 'env[OS_PROJECT_DOMAIN_NAME]) must be specified, ' - 'because project/tenant name may not be unique.') - ex = self.assertRaises(exc.CommandError, - sh._check_identity_arguments, args) - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sdk, 'create_connection') - def test_setup_senlinclient(self, mock_conn): - USER_AGENT = 'python-senlinclient' - args = TestArgs() - kwargs = { - 'auth_plugin': args.auth_plugin, - 'auth_url': args.auth_url, - 'project_name': args.project_name or args.tenant_name, - 'project_id': args.project_id or args.tenant_id, - 'domain_name': args.domain_name, - 'domain_id': args.domain_id, - 'project_domain_name': args.project_domain_name, - 'project_domain_id': args.project_domain_id, - 'user_domain_name': args.user_domain_name, - 'user_domain_id': args.user_domain_id, - 'username': args.username, - 'user_id': args.user_id, - 'password': args.password, - 'verify': args.verify, - 'token': args.token, - 'trust_id': args.trust_id, - } - sh = shell.SenlinShell() - conn = mock.Mock() - mock_conn.return_value = conn - conn.session = mock.Mock() - sh._setup_senlin_client('1', args) - mock_conn.assert_called_once_with(prof=None, user_agent=USER_AGENT, - **kwargs) - client = mock.Mock() - senlin_client.Client = mock.Mock(return_value=client) - self.assertEqual(client, sh._setup_senlin_client('1', args)) diff --git a/senlinclient/tests/unit/test_utils.py b/senlinclient/tests/unit/test_utils.py index f961c733..c51811ea 100644 --- a/senlinclient/tests/unit/test_utils.py +++ b/senlinclient/tests/unit/test_utils.py @@ -10,9 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import collections -import sys - from heatclient.common import template_utils import mock import six @@ -23,21 +20,7 @@ from senlinclient.common.i18n import _ from senlinclient.common import utils -class CaptureStdout(object): - """Context manager for capturing stdout from statements in its block.""" - def __enter__(self): - self.real_stdout = sys.stdout - self.stringio = six.StringIO() - sys.stdout = self.stringio - return self - - def __exit__(self, *args): - sys.stdout = self.real_stdout - self.stringio.seek(0) - self.read = self.stringio.read - - -class shellTest(testtools.TestCase): +class UtilTest(testtools.TestCase): def test_format_parameter(self): params = ['status=ACTIVE;name=cluster1'] @@ -114,137 +97,3 @@ class shellTest(testtools.TestCase): def test_list_formatter_with_empty_list(self): params = [] self.assertEqual('', utils.list_formatter(params)) - - -class PrintListTestCase(testtools.TestCase): - - def test_print_list_with_list(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - to_print = [Row(foo='fake_foo1', bar='fake_bar2'), - Row(foo='fake_foo2', bar='fake_bar1')] - with CaptureStdout() as cso: - utils.print_list(to_print, ['foo', 'bar']) - # Output should be sorted by the first key (foo) - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo1 | fake_bar2 | -| fake_foo2 | fake_bar1 | -+-----------+-----------+ -""", cso.read()) - - def test_print_list_with_None_string(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - to_print = [Row(foo='fake_foo1', bar='None'), - Row(foo='fake_foo2', bar='fake_bar1')] - with CaptureStdout() as cso: - utils.print_list(to_print, ['foo', 'bar']) - # Output should be sorted by the first key (foo) - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo1 | None | -| fake_foo2 | fake_bar1 | -+-----------+-----------+ -""", cso.read()) - - def test_print_list_with_None_data(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - to_print = [Row(foo='fake_foo1', bar=None), - Row(foo='fake_foo2', bar='fake_bar1')] - with CaptureStdout() as cso: - utils.print_list(to_print, ['foo', 'bar']) - # Output should be sorted by the first key (foo) - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo1 | - | -| fake_foo2 | fake_bar1 | -+-----------+-----------+ -""", cso.read()) - - def test_print_list_with_list_sortby(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - to_print = [Row(foo='fake_foo1', bar='fake_bar2'), - Row(foo='fake_foo2', bar='fake_bar1')] - with CaptureStdout() as cso: - utils.print_list(to_print, ['foo', 'bar'], sortby_index=1) - # Output should be sorted by the first key (bar) - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo2 | fake_bar1 | -| fake_foo1 | fake_bar2 | -+-----------+-----------+ -""", cso.read()) - - def test_print_list_with_list_no_sort(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - to_print = [Row(foo='fake_foo2', bar='fake_bar1'), - Row(foo='fake_foo1', bar='fake_bar2')] - with CaptureStdout() as cso: - utils.print_list(to_print, ['foo', 'bar'], sortby_index=None) - # Output should be in the order given - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo2 | fake_bar1 | -| fake_foo1 | fake_bar2 | -+-----------+-----------+ -""", cso.read()) - - def test_print_list_with_generator(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - - def gen_rows(): - for row in [Row(foo='fake_foo1', bar='fake_bar2'), - Row(foo='fake_foo2', bar='fake_bar1')]: - yield row - with CaptureStdout() as cso: - utils.print_list(gen_rows(), ['foo', 'bar']) - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo1 | fake_bar2 | -| fake_foo2 | fake_bar1 | -+-----------+-----------+ -""", cso.read()) - - -class PrintDictTestCase(testtools.TestCase): - - def test_print_dict(self): - data = {'foo': 'fake_foo', 'bar': 'fake_bar'} - with CaptureStdout() as cso: - utils.print_dict(data) - # Output should be sorted by the Property - self.assertEqual("""\ -+----------+----------+ -| Property | Value | -+----------+----------+ -| bar | fake_bar | -| foo | fake_foo | -+----------+----------+ -""", cso.read()) - - def test_print_dict_with_None_data(self): - Row = collections.namedtuple('Row', ['foo', 'bar']) - to_print = [Row(foo='fake_foo1', bar=None), - Row(foo='fake_foo2', bar='fake_bar1')] - with CaptureStdout() as cso: - utils.print_list(to_print, ['foo', 'bar']) - # Output should be sorted by the first key (foo) - self.assertEqual("""\ -+-----------+-----------+ -| foo | bar | -+-----------+-----------+ -| fake_foo1 | - | -| fake_foo2 | fake_bar1 | -+-----------+-----------+ -""", cso.read()) diff --git a/senlinclient/tests/unit/v1/test_shell.py b/senlinclient/tests/unit/v1/test_shell.py deleted file mode 100644 index 0e639d3e..00000000 --- a/senlinclient/tests/unit/v1/test_shell.py +++ /dev/null @@ -1,1952 +0,0 @@ -# Licensed 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 copy -import fixtures -import subprocess - -import mock -from openstack import exceptions as oexc -import six -import testtools - -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import utils -from senlinclient.v1 import shell as sh - - -class ShellTest(testtools.TestCase): - - def setUp(self): - super(ShellTest, self).setUp() - self.profile_args = { - 'spec_file': mock.Mock(), - 'name': 'stack_spec', - 'metadata': {'user': 'demo'} - } - self.profile_spec = { - 'type': 'os.heat.stack', - 'version': 1.0, - 'properties': { - 'name': 'stack1', - 'template': {"Template": "data"} - } - } - self.patch('senlinclient.v1.shell.show_deprecated') - - # NOTE(pshchelo): this overrides the testtools.TestCase.patch method - # that does simple monkey-patching in favor of mock's patching - def patch(self, target, **kwargs): - mockfixture = self.useFixture(fixtures.MockPatch(target, **kwargs)) - return mockfixture.mock - - def _make_args(self, args): - """Convert a dict to an object.""" - class Args(object): - def __init__(self, entries): - self.__dict__.update(entries) - - return Args(args) - - @mock.patch.object(utils, 'print_dict') - def test_do_build_info(self, mock_print): - service = mock.Mock() - result = mock.Mock() - service.get_build_info.return_value = result - sh.do_build_info(service) - formatters = { - 'api': utils.json_formatter, - 'engine': utils.json_formatter, - } - mock_print.assert_called_once_with( - {'api': result.api, 'engine': result.engine}, - formatters=formatters) - service.get_build_info.assert_called_once_with() - - @mock.patch.object(utils, 'print_list') - def test_do_profile_type_list(self, mock_print): - service = mock.Mock() - mock_type = mock.Mock( - support_status={ - "1.0": [ - { - "status": "SUPPORTED", - "since": "2016.10" - } - ] - } - ) - mock_type.name = "fake_type" - types = [mock_type] - service.profile_types.return_value = types - - sh.do_profile_type_list(service) - - mock_print.assert_called_once_with( - mock.ANY, - ['name', 'version', 'support_status'], - sortby_index=0) - self.assertTrue(service.profile_types.called) - - @mock.patch.object(utils, 'format_output') - def test_do_profile_type_show(self, mock_format): - service = mock.Mock() - fake_pt = mock.Mock() - fake_pt.to_dict.return_value = {'foo': 'bar'} - service.get_profile_type = mock.Mock(return_value=fake_pt) - args_dict = { - 'format': 'json', - 'type_name': 'os.nova.server' - } - args = self._make_args(args_dict) - sh.do_profile_type_show(service, args) - mock_format.assert_called_with({'foo': 'bar'}, format=args.format) - service.get_profile_type.assert_called_with('os.nova.server') - args.format = None - sh.do_profile_type_show(service, args) - mock_format.assert_called_with({'foo': 'bar'}) - - def test_do_profile_type_show_type_not_found(self): - service = mock.Mock() - args = { - 'type_name': 'wrong_type', - 'format': 'json' - } - args = self._make_args(args) - ex = oexc.ResourceNotFound - service.get_profile_type = mock.Mock(side_effect=ex) - ex = self.assertRaises(exc.CommandError, - sh.do_profile_type_show, - service, args) - self.assertEqual(_('Profile Type not found: wrong_type'), - six.text_type(ex)) - - @mock.patch.object(utils, 'format_output') - def test_do_profile_type_operations(self, mock_format): - service = mock.Mock() - fake_pto = {'foo': 'bar'} - service.list_profile_type_operations = mock.Mock(return_value=fake_pto) - args_dict = { - 'format': 'json', - 'type_name': 'os.nova.server-1.0' - } - args = self._make_args(args_dict) - sh.do_profile_type_ops(service, args) - mock_format.assert_called_with({'foo': 'bar'}, format=args.format) - service.list_profile_type_operations.assert_called_with( - 'os.nova.server-1.0') - args.format = None - sh.do_profile_type_ops(service, args) - mock_format.assert_called_with({'foo': 'bar'}) - - def test_do_profile_type_operations_type_not_found(self): - service = mock.Mock() - args = { - 'type_name': 'wrong_type', - 'format': 'json' - } - args = self._make_args(args) - ex = oexc.ResourceNotFound - service.list_profile_type_operations = mock.Mock(side_effect=ex) - ex = self.assertRaises(exc.CommandError, - sh.do_profile_type_ops, - service, args) - self.assertEqual(_('Profile Type not found: wrong_type'), - six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_profile_list(self, mock_print): - service = mock.Mock() - profiles = mock.Mock() - service.profiles.return_value = profiles - fields = ['id', 'name', 'type', 'created_at', 'project_id'] - args = { - 'limit': 20, - 'marker': 'mark_id', - 'sort': 'key:dir', - 'global_project': True, - 'filters': ['name=stack_spec'] - } - queries = copy.deepcopy(args) - del queries['filters'] - queries['name'] = 'stack_spec' - formatters = {} - args = self._make_args(args) - args.full_id = True - sh.do_profile_list(service, args) - service.profiles.assert_called_once_with(**queries) - mock_print.assert_called_with(profiles, fields, formatters=formatters, - sortby_index=None) - - args.sort = None - sh.do_profile_list(service, args) - mock_print.assert_called_with(profiles, fields, formatters=formatters, - sortby_index=1) - - @mock.patch.object(utils, 'nested_dict_formatter') - @mock.patch.object(utils, 'print_dict') - def test_show_profile(self, mock_print, mock_dict): - service = mock.Mock() - profile = mock.Mock() - profile_id = mock.Mock() - service.get_profile.return_value = profile - pro_to_dict = mock.Mock() - profile.to_dict.return_value = pro_to_dict - json_formatter = mock.Mock() - utils.json_formatter = json_formatter - dict_formatter = mock.Mock() - mock_dict.return_value = dict_formatter - formatters = { - 'metadata': json_formatter, - 'spec': dict_formatter - } - sh._show_profile(service, profile_id) - service.get_profile.assert_called_once_with(profile_id) - mock_dict.assert_called_once_with(['type', 'version', 'properties'], - ['property', 'value']) - mock_print.assert_called_once_with(pro_to_dict, formatters=formatters) - - def test_show_profile_not_found(self): - service = mock.Mock() - ex = oexc.ResourceNotFound - service.get_profile.side_effect = ex - profile_id = 'wrong_id' - ex = self.assertRaises(exc.CommandError, - sh._show_profile, - service, profile_id) - self.assertEqual(_('Profile not found: wrong_id'), six.text_type(ex)) - service.get_profile.assert_called_once_with(profile_id) - - @mock.patch.object(sh, '_show_profile') - @mock.patch.object(utils, 'format_parameters') - @mock.patch.object(utils, 'process_stack_spec') - @mock.patch.object(utils, 'get_spec_content') - def test_do_profile_create(self, mock_get, mock_proc, mock_format, - mock_show): - args = copy.deepcopy(self.profile_args) - args = self._make_args(args) - spec = copy.deepcopy(self.profile_spec) - mock_get.return_value = spec - stack_properties = mock.Mock() - mock_proc.return_value = stack_properties - mock_format.return_value = {'user': 'demo'} - params = { - 'name': 'stack_spec', - 'spec': spec, - 'metadata': {'user': 'demo'}, - } - service = mock.Mock() - profile = mock.Mock() - profile_id = mock.Mock() - profile.id = profile_id - service.create_profile.return_value = profile - - sh.do_profile_create(service, args) - - mock_get.assert_called_once_with(args.spec_file) - mock_proc.assert_called_once_with(self.profile_spec['properties']) - mock_format.assert_called_once_with(args.metadata) - service.create_profile.assert_called_once_with(**params) - mock_show.assert_called_once_with(service, profile_id) - - # Miss 'type' key in spec file - del spec['type'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_create, - service, args) - self.assertEqual(_("Missing 'type' key in spec file."), - six.text_type(ex)) - # Miss 'version' key in spec file - spec['type'] = 'os.heat.stack' - del spec['version'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_create, - service, args) - self.assertEqual(_("Missing 'version' key in spec file."), - six.text_type(ex)) - # Miss 'properties' key in spec file - spec['version'] = 1.0 - del spec['properties'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_create, - service, args) - self.assertEqual(_("Missing 'properties' key in spec file."), - six.text_type(ex)) - - @mock.patch.object(sh, '_show_profile') - def test_do_profile_show(self, mock_show): - service = mock.Mock() - args = {'id': 'profile_id'} - args = self._make_args(args) - sh.do_profile_show(service, args) - mock_show.assert_called_once_with(service, args.id) - - @mock.patch.object(sh, '_show_profile') - @mock.patch.object(utils, 'format_parameters') - def test_do_profile_update(self, mock_format, mock_show): - args = copy.deepcopy(self.profile_args) - args = self._make_args(args) - mock_format.return_value = {'user': 'demo'} - service = mock.Mock() - profile = mock.Mock() - profile_id = mock.Mock() - profile.id = profile_id - args.id = 'FAKE_ID' - service.get_profile.return_value = profile - - sh.do_profile_update(service, args) - - mock_format.assert_called_once_with(args.metadata) - service.get_profile.assert_called_once_with('FAKE_ID') - params = { - 'name': 'stack_spec', - 'metadata': {'user': 'demo'}, - } - service.update_profile.assert_called_once_with(profile, **params) - mock_show.assert_called_once_with(service, profile_id) - - @mock.patch.object(utils, 'format_parameters') - def test_do_profile_update_not_found(self, mock_format): - service = mock.Mock() - args = copy.deepcopy(self.profile_args) - args = self._make_args(args) - args.id = 'FAKE_ID' - ex = oexc.ResourceNotFound - service.get_profile.side_effect = ex - ex = self.assertRaises(exc.CommandError, - sh.do_profile_update, - service, args) - self.assertEqual(_('Profile not found: FAKE_ID'), - six.text_type(ex)) - mock_format.assert_called_once_with(args.metadata) - - def test_do_profile_delete(self): - service = mock.Mock() - args = {'id': ['profile_id']} - args = self._make_args(args) - sh.do_profile_delete(service, args) - service.delete_profile.assert_called_with('profile_id', False) - - def test_do_profile_delete_not_found(self): - service = mock.Mock() - args = {'id': ['profile1', 'profile2']} - args = self._make_args(args) - sh.do_profile_delete(service, args) - service.delete_profile.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_profile_delete, - service, args) - msg = _("Failed to delete some of the specified profile(s).") - self.assertEqual(msg, six.text_type(ex)) - service.delete_profile.assert_called_with('profile2', False) - - @mock.patch.object(utils, 'process_stack_spec') - @mock.patch.object(utils, 'get_spec_content') - def test_do_profile_validate(self, mock_get, mock_proc): - args = self._make_args({'spec_file': mock.Mock()}) - spec = copy.deepcopy(self.profile_spec) - mock_get.return_value = spec - params = { - 'spec': spec, - } - service = mock.Mock() - profile = mock.Mock() - profile.to_dict.return_value = {} - service.validate_profile.return_value = profile - - sh.do_profile_validate(service, args) - - service.validate_profile.assert_called_once_with(**params) - - # Miss 'type' key in spec file - del spec['type'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_validate, - service, args) - self.assertEqual(_("Missing 'type' key in spec file."), - six.text_type(ex)) - # Miss 'version' key in spec file - spec['type'] = 'os.heat.stack' - del spec['version'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_validate, - service, args) - self.assertEqual(_("Missing 'version' key in spec file."), - six.text_type(ex)) - # Miss 'properties' key in spec file - spec['version'] = 1.0 - del spec['properties'] - ex = self.assertRaises(exc.CommandError, - sh.do_profile_validate, - service, args) - self.assertEqual(_("Missing 'properties' key in spec file."), - six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_policy_type_list(self, mock_print): - service = mock.Mock() - args = mock.Mock() - mock_type = mock.Mock( - support_status={ - "1.0": [ - { - "status": "SUPPORTED", - "since": "2016.10" - } - ] - } - ) - mock_type.name = "fake_type" - types = [mock_type] - service.policy_types.return_value = types - - sh.do_policy_type_list(service, args) - - mock_print.assert_called_once_with( - mock.ANY, - ['name', 'version', 'support_status'], - sortby_index=0) - self.assertTrue(service.policy_types.called) - - @mock.patch.object(utils, 'format_output') - def test_do_policy_type_show(self, mock_format): - service = mock.Mock() - args = { - 'type_name': 'senlin.policy.deletion', - 'format': 'yaml' - } - args = self._make_args(args) - res = mock.Mock() - pt = mock.Mock() - res.to_dict.return_value = pt - service.get_policy_type.return_value = res - sh.do_policy_type_show(service, args) - mock_format.assert_called_with(pt, format=args.format) - - # no format attribute - args = { - 'type_name': 'senlin.policy.deletion', - 'format': None - } - args = self._make_args(args) - service.get_policy_type.return_value = res - sh.do_policy_type_show(service, args) - mock_format.assert_called_with(pt) - - def test_do_policy_type_show_not_found(self): - service = mock.Mock() - args = {'type_name': 'BAD'} - args = self._make_args(args) - - service.get_policy_type.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_policy_type_show, service, args) - msg = _('Policy type not found: BAD') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_receiver_list(self, mock_print): - service = mock.Mock() - params = { - 'limit': 10, - 'marker': 'fake_id', - 'sort': 'key:dir', - 'filters': ['filter_key=filter_value'], - 'global_project': False, - 'full_id': False, - } - fields = ['id', 'name', 'type', 'cluster_id', 'action', 'created_at'] - args = self._make_args(params) - queries = copy.deepcopy(params) - del queries['filters'] - queries['filter_key'] = 'filter_value' - r1 = mock.Mock() - r1.id = '01234567-abcd-efgh' - r1.cluster_id = 'abcdefgh-abcd-efgh' - receivers = [r1] - service.receivers.return_value = receivers - formatters = { - 'id': mock.ANY, - 'cluster_id': mock.ANY - } - sh.do_receiver_list(service, args) - mock_print.assert_called_with(receivers, fields, - formatters=formatters, - sortby_index=None) - # full_id is requested - args.full_id = True - sh.do_receiver_list(service, args) - mock_print.assert_called_with(receivers, fields, - formatters={}, - sortby_index=None) - - # default sorting - args.sort = None - sh.do_receiver_list(service, args) - mock_print.assert_called_with(receivers, fields, - formatters={}, - sortby_index=0) - - @mock.patch.object(utils, 'print_dict') - def test_show_receiver(self, mock_print): - service = mock.Mock() - receiver = mock.Mock() - receiver_id = '01234567-abcd-abcd-abcdef' - receiver.id = receiver_id - service.get_receiver.return_value = receiver - receiver_dict = mock.Mock() - receiver.to_dict.return_value = receiver_dict - sh._show_receiver(service, receiver_id) - formatters = { - 'actor': utils.json_formatter, - 'params': utils.json_formatter, - 'channel': utils.json_formatter, - } - service.get_receiver.assert_called_once_with(receiver_id) - mock_print.assert_called_once_with(receiver_dict, - formatters=formatters) - - def test_show_receiver_not_found(self): - service = mock.Mock() - receiver = mock.Mock() - receiver_id = 'wrong_id' - receiver.id = receiver_id - - service.get_receiver.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh._show_receiver, service, receiver_id) - self.assertEqual(_('Receiver not found: wrong_id'), six.text_type(ex)) - - @mock.patch.object(sh, '_show_receiver') - def test_do_receiver_show(self, mock_show): - service = mock.Mock() - args = {'id': 'receiver_id'} - args = self._make_args(args) - sh.do_receiver_show(service, args) - mock_show.assert_called_once_with(service, - receiver_id='receiver_id') - - @mock.patch.object(sh, '_show_receiver') - def test_do_receiver_create_webhook(self, mock_show): - service = mock.Mock() - args = { - 'name': 'receiver1', - 'type': 'webhook', - 'cluster': 'cluster1', - 'action': 'CLUSTER_SCALE_IN', - 'params': [] - } - args = self._make_args(args) - params = { - 'name': 'receiver1', - 'type': 'webhook', - 'cluster_id': 'cluster1', - 'action': 'CLUSTER_SCALE_IN', - 'params': {} - } - receiver = mock.Mock() - receiver.id = 'FAKE_ID' - service.create_receiver.return_value = receiver - sh.do_receiver_create(service, args) - service.create_receiver.assert_called_once_with(**params) - mock_show.assert_called_once_with(service, 'FAKE_ID') - - def test_do_receiver_create_webhook_failed(self): - service = mock.Mock() - args = { - 'name': 'receiver1', - 'type': 'webhook', - 'cluster': None, - 'action': None, - 'params': [] - } - args = self._make_args(args) - ex = self.assertRaises(exc.CommandError, - sh.do_receiver_create, service, args) - msg = _("cluster and action parameters are required to create webhook" - " type of receiver.") - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sh, '_show_receiver') - def test_do_receiver_create_non_webhook(self, mock_show): - service = mock.Mock() - args = { - 'name': 'receiver1', - 'type': 'foo', - 'cluster': None, - 'action': None, - 'params': [] - } - args = self._make_args(args) - params = { - 'name': 'receiver1', - 'type': 'foo', - 'cluster_id': None, - 'action': None, - 'params': {} - } - receiver = mock.Mock() - receiver.id = 'FAKE_ID' - service.create_receiver.return_value = receiver - sh.do_receiver_create(service, args) - service.create_receiver.assert_called_once_with(**params) - mock_show.assert_called_once_with(service, 'FAKE_ID') - - @mock.patch.object(sh, '_show_receiver') - def test_do_receiver_update(self, mock_show): - service = mock.Mock() - args = { - 'name': 'receiver2', - 'id': 'receiver_id', - 'action': 'CLUSTER_SCALE_OUT', - 'params': ['key1=value1;key2=value2'] - } - args = self._make_args(args) - params = { - 'name': 'receiver2', - 'action': 'CLUSTER_SCALE_OUT', - 'params': {'key1': 'value1', 'key2': 'value2'} - } - receiver = mock.Mock() - receiver.id = 'receiver_id' - service.get_receiver.return_value = receiver - sh.do_receiver_update(service, args) - service.get_receiver.assert_called_once_with('receiver_id') - service.update_receiver.assert_called_once_with( - receiver, **params) - mock_show(service, receiver_id=receiver.id) - - def test_do_receiver_delete(self): - service = mock.Mock() - args = {'id': ['FAKE']} - args = self._make_args(args) - service.delete_receiver = mock.Mock() - sh.do_receiver_delete(service, args) - service.delete_receiver.assert_called_once_with('FAKE', False) - - def test_do_receiver_delete_not_found(self): - service = mock.Mock() - args = {'id': ['receiver_id']} - args = self._make_args(args) - - service.delete_receiver.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_receiver_delete, service, args) - msg = _("Failed to delete some of the specified receiver(s).") - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_policy_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'type', 'created_at'] - args = { - 'limit': 20, - 'marker': 'fake_id', - 'sort': 'name', - 'global_project': False, - 'full_id': True, - 'filters': ['name=stack_spec'] - } - args = self._make_args(args) - queries = { - 'limit': 20, - 'marker': 'fake_id', - 'sort': 'name', - 'global_project': False, - 'name': 'stack_spec', - } - policies = mock.Mock() - service.policies.return_value = policies - formatters = {} - sh.do_policy_list(service, args) - service.policies.assert_called_once_with(**queries) - mock_print.assert_called_once_with( - policies, fields, formatters=formatters, sortby_index=None) - mock_print.reset_mock() - - args.sort = None - sh.do_policy_list(service, args) - mock_print.assert_called_once_with( - policies, fields, formatters=formatters, sortby_index=1) - - @mock.patch.object(utils, 'print_dict') - def test_show_policy(self, mock_print): - service = mock.Mock() - formatters = { - 'metadata': utils.json_formatter, - 'spec': utils.json_formatter, - } - policy_id = 'fake_policy_id' - policy = mock.Mock() - policy.id = policy_id - service.get_policy.return_value = policy - policy_dict = mock.Mock() - policy.to_dict.return_value = policy_dict - sh._show_policy(service, policy_id) - mock_print.assert_called_once_with(policy_dict, - formatters=formatters) - - # policy not found - ex = oexc.ResourceNotFound - service.get_policy.side_effect = ex - ex = self.assertRaises(exc.CommandError, - sh._show_policy, - service, policy_id) - msg = _('Policy not found: fake_policy_id') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(sh, '_show_policy') - @mock.patch.object(utils, 'get_spec_content') - def test_do_policy_create(self, mock_get, mock_show): - service = mock.Mock() - spec = mock.Mock() - mock_get.return_value = spec - args = { - 'name': 'new_policy', - 'spec_file': 'policy_file', - } - args = self._make_args(args) - attrs = { - 'name': 'new_policy', - 'spec': spec, - } - policy = mock.Mock() - policy.id = 'policy_id' - service.create_policy.return_value = policy - sh.do_policy_create(service, args) - mock_get.assert_called_once_with(args.spec_file) - service.create_policy.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, policy.id) - - @mock.patch.object(sh, '_show_policy') - def test_do_policy_show(self, mock_show): - service = mock.Mock() - args = {'id': 'policy_id'} - args = self._make_args(args) - sh.do_policy_show(service, args) - mock_show.assert_called_once_with(service, policy_id='policy_id') - - @mock.patch.object(sh, '_show_policy') - def test_do_policy_update(self, mock_show): - service = mock.Mock() - args = { - 'name': 'deletion_policy', - 'id': 'policy_id', - } - args = self._make_args(args) - params = { - 'name': 'deletion_policy', - } - policy = mock.Mock() - service.get_policy.return_value = policy - policy.id = 'policy_id' - sh.do_policy_update(service, args) - service.get_policy.assert_called_once_with('policy_id') - service.update_policy.assert_called_once_with( - policy, **params) - mock_show(service, policy_id=policy.id) - - def test_do_policy_delete(self): - service = mock.Mock() - args = {'id': ['policy_id']} - args = self._make_args(args) - service.delete_policy = mock.Mock() - sh.do_policy_delete(service, args) - service.delete_policy.assert_called_once_with('policy_id', False) - - def test_do_policy_delete_not_found(self): - service = mock.Mock() - args = {'id': ['policy_id']} - args = self._make_args(args) - - service.delete_policy.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_policy_delete, service, args) - msg = _("Failed to delete some of the specified policy(s).") - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'get_spec_content') - def test_do_policy_validate(self, mock_get): - service = mock.Mock() - spec = mock.Mock() - mock_get.return_value = spec - args = { - 'spec_file': 'policy_file', - } - args = self._make_args({'spec_file': 'policy_file'}) - attrs = { - 'spec': spec, - } - policy = mock.Mock() - policy.to_dict.return_value = {} - service.validate_policy.return_value = policy - sh.do_policy_validate(service, args) - mock_get.assert_called_once_with(args.spec_file) - service.validate_policy.assert_called_once_with(**attrs) - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'status', 'created_at', 'updated_at'] - args = { - 'limit': 20, - 'marker': 'fake_id', - 'sort': 'key:dir', - 'global_project': False, - 'filters': ['status=ACTIVE'], - } - queries = copy.deepcopy(args) - del queries['filters'] - queries['status'] = 'ACTIVE' - args = self._make_args(args) - clusters = mock.Mock() - service.clusters.return_value = clusters - args.full_id = True - formatters = {} - sh.do_cluster_list(service, args) - service.clusters.assert_called_once_with(**queries) - mock_print.assert_called_once_with(clusters, fields, - formatters=formatters, - sortby_index=None) - args.sort = None - sh.do_cluster_list(service, args) - mock_print.assert_called_with(clusters, fields, - formatters={}, sortby_index=3) - - @mock.patch.object(utils, 'print_dict') - def test_show_cluster(self, mock_print): - service = mock.Mock() - cluster_id = 'cluster_id' - cluster = mock.Mock() - cluster.id = cluster_id - service.get_cluster.return_value = cluster - formatters = { - 'config': utils.json_formatter, - 'metadata': utils.json_formatter, - 'node_ids': utils.list_formatter, - } - cluster_dict = mock.Mock() - cluster.to_dict.return_value = cluster_dict - sh._show_cluster(service, cluster_id) - mock_print.assert_called_once_with(cluster_dict, formatters=formatters) - - @mock.patch.object(sh, '_show_cluster') - def test_do_cluster_create(self, mock_show): - service = mock.Mock() - args = { - 'name': 'CLUSTER1', - 'profile': 'profile1', - 'min_size': 1, - 'max_size': 10, - 'desired_capacity': 5, - 'metadata': ['user=demo'], - 'config': {}, - 'timeout': 200, - } - attrs = copy.deepcopy(args) - attrs['profile_id'] = args['profile'] - args = self._make_args(args) - del attrs['profile'] - attrs['metadata'] = {'user': 'demo'} - cluster = mock.Mock() - service.create_cluster.return_value = cluster - cluster.id = 'cluster_id' - sh.do_cluster_create(service, args) - service.create_cluster.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, 'cluster_id') - - def test_do_cluster_delete(self): - service = mock.Mock() - args = {'id': ['CID'], 'force_delete': False} - args = self._make_args(args) - service.delete_cluster = mock.Mock() - sh.do_cluster_delete(service, args) - service.delete_cluster.assert_called_once_with('CID', False, False) - - def test_do_cluster_delete_force(self): - service = mock.Mock() - args = {'id': ['CID'], 'force_delete': True} - args = self._make_args(args) - service.delete_cluster = mock.Mock() - sh.do_cluster_delete(service, args) - service.delete_cluster.assert_called_once_with('CID', True, False) - - @mock.patch('subprocess.Popen') - def test__run_script(self, mock_proc): - x_proc = mock.Mock(returncode=0) - x_stdout = 'OUTPUT' - x_stderr = 'ERROR' - x_proc.communicate.return_value = (x_stdout, x_stderr) - mock_proc.return_value = x_proc - - addr = { - 'private': [ - { - 'OS-EXT-IPS:type': 'floating', - 'version': 4, - 'addr': '1.2.3.4', - } - ] - } - output = {} - - sh._run_script('NODE_ID', addr, 'private', 'floating', 22, 'john', - False, 'identity_path', 'echo foo', '-f bar', - output=output) - mock_proc.assert_called_once_with( - ['ssh', '-4', '-p22', '-i identity_path', '-f bar', 'john@1.2.3.4', - 'echo foo'], - stdout=subprocess.PIPE) - self.assertEqual( - {'status': 'SUCCEEDED (0)', 'output': 'OUTPUT', 'error': 'ERROR'}, - output) - - def test__run_script_network_not_found(self): - addr = {'foo': 'bar'} - output = {} - - sh._run_script('NODE_ID', addr, 'private', 'floating', 22, 'john', - False, 'identity_path', 'echo foo', '-f bar', - output=output) - self.assertEqual( - {'status': 'FAILED', - 'reason': "Node 'NODE_ID' is not attached to network 'private'." - }, - output) - - def test__run_script_more_than_one_network(self): - addr = {'foo': 'bar', 'koo': 'tar'} - output = {} - - sh._run_script('NODE_ID', addr, '', 'floating', 22, 'john', - False, 'identity_path', 'echo foo', '-f bar', - output=output) - self.assertEqual( - {'status': 'FAILED', - 'reason': "Node 'NODE_ID' is attached to more than one " - "network. Please pick the network to use."}, - output) - - def test__run_script_no_network(self): - addr = {} - output = {} - - sh._run_script('NODE_ID', addr, '', 'floating', 22, 'john', - False, 'identity_path', 'echo foo', '-f bar', - output=output) - - self.assertEqual( - {'status': 'FAILED', - 'reason': "Node 'NODE_ID' is not attached to any network."}, - output) - - def test__run_script_no_matching_address(self): - addr = { - 'private': [ - { - 'OS-EXT-IPS:type': 'fixed', - 'version': 4, - 'addr': '1.2.3.4', - } - ] - } - output = {} - - sh._run_script('NODE_ID', addr, 'private', 'floating', 22, 'john', - False, 'identity_path', 'echo foo', '-f bar', - output=output) - self.assertEqual( - {'status': 'FAILED', - 'reason': "No address that would match network 'private' and " - "type 'floating' of IPv4 has been found for node " - "'NODE_ID'."}, - output) - - def test__run_script_more_than_one_address(self): - addr = { - 'private': [ - { - 'OS-EXT-IPS:type': 'fixed', - 'version': 4, - 'addr': '1.2.3.4', - }, - { - 'OS-EXT-IPS:type': 'fixed', - 'version': 4, - 'addr': '5.6.7.8', - }, - ] - } - - output = {} - - sh._run_script('NODE_ID', addr, 'private', 'fixed', 22, 'john', - False, 'identity_path', 'echo foo', '-f bar', - output=output) - self.assertEqual( - {'status': 'FAILED', - 'reason': "More than one IPv4 fixed address found."}, - output) - - @mock.patch('threading.Thread') - @mock.patch.object(sh, '_run_script') - def test_do_cluster_run(self, mock_run, mock_thread): - service = mock.Mock() - args = { - 'script': 'script_name', - 'network': 'network_name', - 'address_type': 'fixed', - 'port': 22, - 'user': 'root', - 'ipv6': False, - 'identity_file': 'identity_filename', - 'ssh_options': '-f oo', - } - args = self._make_args(args) - args.id = 'CID' - addr1 = {'addresses': 'ADDR CONTENT 1'} - addr2 = {'addresses': 'ADDR CONTENT 2'} - attributes = [ - mock.Mock(node_id='NODE1', attr_value=addr1), - mock.Mock(node_id='NODE2', attr_value=addr2) - ] - service.collect_cluster_attrs.return_value = attributes - - th1 = mock.Mock() - th2 = mock.Mock() - mock_thread.side_effect = [th1, th2] - fake_script = 'blah blah' - with mock.patch('senlinclient.v1.shell.open', - mock.mock_open(read_data=fake_script)) as mock_open: - sh.do_cluster_run(service, args) - - service.collect_cluster_attrs.assert_called_once_with( - args.id, 'details') - mock_open.assert_called_once_with('script_name', 'r') - mock_thread.assert_has_calls([ - mock.call(target=mock_run, - args=('NODE1', 'ADDR CONTENT 1', 'network_name', - 'fixed', 22, 'root', False, 'identity_filename', - 'blah blah', '-f oo'), - kwargs={'output': {}}), - mock.call(target=mock_run, - args=('NODE2', 'ADDR CONTENT 2', 'network_name', - 'fixed', 22, 'root', False, 'identity_filename', - 'blah blah', '-f oo'), - kwargs={'output': {}}) - ]) - th1.start.assert_called_once_with() - th2.start.assert_called_once_with() - th1.join.assert_called_once_with() - th2.join.assert_called_once_with() - - @mock.patch.object(sh, '_show_cluster') - def test_do_cluster_update(self, mock_show): - service = mock.Mock() - args = { - 'profile': 'test_profile', - 'profile_only': 'false', - 'name': 'CLUSTER1', - 'metadata': ['user=demo'], - 'timeout': 100, - } - attrs = copy.deepcopy(args) - attrs['metadata'] = {'user': 'demo'} - attrs['profile_id'] = 'test_profile' - attrs['profile_only'] = False - del attrs['profile'] - args = self._make_args(args) - args.id = 'CID' - cluster = mock.Mock() - cluster.id = 'CID' - service.get_cluster.return_value = cluster - service.update_cluster = mock.Mock() - - sh.do_cluster_update(service, args) - - service.get_cluster.assert_called_once_with('CID') - service.update_cluster.assert_called_once_with(cluster, **attrs) - mock_show.assert_called_once_with(service, 'CID') - - @mock.patch.object(sh, '_show_cluster') - def test_do_cluster_show(self, mock_show): - service = mock.Mock() - args = {'id': 'cluster_id'} - args = self._make_args(args) - sh.do_cluster_show(service, args) - mock_show.assert_called_once_with(service, 'cluster_id') - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_node_list(self, mock_print): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'limit': 20, - 'marker': 'marker_id', - 'filters': ['status=ACTIVE'], - } - queries = copy.deepcopy(args) - queries['cluster_id'] = args['id'] - del queries['id'] - del queries['filters'] - queries['status'] = 'ACTIVE' - args = self._make_args(args) - args.full_id = True - nodes = mock.Mock() - service.nodes.return_value = nodes - formatters = {} - fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_at'] - sh.do_cluster_node_list(service, args) - service.nodes.assert_called_once_with(**queries) - mock_print.assert_called_once_with(nodes, fields, - formatters=formatters, - sortby_index=5) - - def test_do_cluster_node_add(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'nodes': 'node1,node2' - } - args = self._make_args(args) - node_ids = ['node1', 'node2'] - resp = {'action': 'CLUSTER_NODE_ADD'} - service.cluster_add_nodes.return_value = resp - sh.do_cluster_node_add(service, args) - service.cluster_add_nodes.assert_called_once_with( - 'cluster_id', node_ids) - - def test_do_cluster_node_del(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'nodes': 'node1,node2', - 'destroy_after_deletion': False - } - args = self._make_args(args) - node_ids = ['node1', 'node2'] - resp = {'action': 'CLUSTER_NODE_DEL'} - service.cluster_del_nodes.return_value = resp - - sh.do_cluster_node_del(service, args) - - service.cluster_del_nodes.assert_called_once_with( - 'cluster_id', - node_ids, - destroy_after_deletion=False) - - def test_do_cluster_resize(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'capacity': 2, - 'adjustment': 1, - 'percentage': 50.0, - 'min_size': 1, - 'max_size': 10, - 'min_step': 1, - 'strict': True, - } - args = self._make_args(args) - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _("Only one of 'capacity', 'adjustment' and " - "'percentage' can be specified.") - self.assertEqual(msg, six.text_type(ex)) - - # capacity - args.adjustment = None - args.percentage = None - args.min_step = None - action_args = { - 'adjustment_type': 'EXACT_CAPACITY', - 'number': 2, - 'min_size': 1, - 'max_size': 10, - 'strict': True, - 'min_step': None, - } - resp = {'action': 'action_id'} - service.cluster_resize.return_value = resp - sh.do_cluster_resize(service, args) - service.cluster_resize.assert_called_with('cluster_id', **action_args) - - # capacity is smaller than 0 - args.capacity = -1 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Cluster capacity must be larger than ' - 'or equal to zero.') - self.assertEqual(msg, six.text_type(ex)) - - # adjustment - args.capacity = None - args.percentage = None - args.adjustment = 1 - action_args['adjustment_type'] = 'CHANGE_IN_CAPACITY' - action_args['number'] = 1 - sh.do_cluster_resize(service, args) - service.cluster_resize.assert_called_with('cluster_id', **action_args) - - # adjustment is 0 - args.adjustment = 0 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Adjustment cannot be zero.') - self.assertEqual(msg, six.text_type(ex)) - - # percentage - args.capacity = None - args.percentage = 50.0 - args.adjustment = None - action_args['adjustment_type'] = 'CHANGE_IN_PERCENTAGE' - action_args['number'] = 50.0 - sh.do_cluster_resize(service, args) - service.cluster_resize.assert_called_with('cluster_id', **action_args) - - # percentage is 0 - args.percentage = 0 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Percentage cannot be zero.') - self.assertEqual(msg, six.text_type(ex)) - - # min_step is not None while percentage is None - args.capacity = 2 - args.percentage = None - args.adjustment = None - args.min_step = 1 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min step is only used with percentage.') - self.assertEqual(msg, six.text_type(ex)) - - # min_size < 0 - args.capacity = 2 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = -1 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min size cannot be less than zero.') - self.assertEqual(msg, six.text_type(ex)) - - # max_size < min_size - args.capacity = 5 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = 5 - args.max_size = 4 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min size cannot be larger than max size.') - self.assertEqual(msg, six.text_type(ex)) - - # min_size > capacity - args.capacity = 5 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = 6 - args.max_size = 8 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Min size cannot be larger than the specified capacity') - self.assertEqual(msg, six.text_type(ex)) - - # max_size < capacity - args.capacity = 5 - args.percentage = None - args.adjustment = None - args.min_step = None - args.min_size = 1 - args.max_size = 4 - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_resize, - service, args) - msg = _('Max size cannot be less than the specified capacity.') - self.assertEqual(msg, six.text_type(ex)) - - def test_do_cluster_scale_out(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'count': 3, - } - args = self._make_args(args) - resp = {'action': 'action_id'} - service.cluster_scale_out.return_value = resp - sh.do_cluster_scale_out(service, args) - service.cluster_scale_out.assert_called_once_with( - 'cluster_id', 3) - - def test_do_cluster_scale_in(self): - service = mock.Mock() - args = { - 'id': 'cluster_id', - 'count': 3, - } - args = self._make_args(args) - resp = {'action': 'action_id'} - service.cluster_scale_in.return_value = resp - - sh.do_cluster_scale_in(service, args) - - service.cluster_scale_in.assert_called_once_with('cluster_id', 3) - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_policy_list(self, mock_print): - fields = ['policy_id', 'policy_name', 'policy_type', 'is_enabled'] - service = mock.Mock() - args = { - 'id': 'C1', - 'filters': ['enabled=True'], - 'sort': 'enabled:asc', - 'full_id': True, - } - args = self._make_args(args) - queries = { - 'sort': 'enabled:asc', - 'enabled': 'True', - } - cluster = mock.Mock() - cluster.id = 'C1' - service.get_cluster.return_value = cluster - policies = mock.Mock() - service.cluster_policies.return_value = policies - sh.do_cluster_policy_list(service, args) - service.get_cluster.assert_called_once_with('C1') - service.cluster_policies.assert_called_once_with('C1', **queries) - formatters = {} - mock_print.assert_called_once_with(policies, fields, - formatters=formatters, - sortby_index=None) - - @mock.patch.object(utils, 'print_dict') - def test_do_cluster_policy_show(self, mock_print): - class Binding(object): - def to_dict(self): - pass - - service = mock.Mock() - args = { - 'id': 'CC', - 'policy': 'PP', - } - args = self._make_args(args) - binding = Binding() - service.get_cluster_policy.return_value = binding - sh.do_cluster_policy_show(service, args) - service.get_cluster_policy.assert_called_once_with('PP', 'CC') - mock_print.assert_called_once_with(binding.to_dict()) - - def test_do_cluster_policy_attach(self): - service = mock.Mock() - args = { - 'id': 'C1', - 'policy': 'P1', - 'enabled': True, - } - args = self._make_args(args) - kwargs = { - 'enabled': True, - } - service.cluster_attach_policy.return_value = {'action': 'action_id'} - sh.do_cluster_policy_attach(service, args) - service.cluster_attach_policy.assert_called_once_with('C1', 'P1', - **kwargs) - - def test_do_cluster_policy_detach(self): - args = { - 'id': 'CC', - 'policy': 'PP' - } - service = mock.Mock() - args = self._make_args(args) - resp = {'action': 'action_id'} - service.cluster_detach_policy.return_value = resp - sh.do_cluster_policy_detach(service, args) - service.cluster_detach_policy.assert_called_once_with('CC', 'PP') - - def test_do_cluster_policy_update(self): - service = mock.Mock() - args = { - 'id': 'C1', - 'policy': 'policy1', - 'enabled': True, - } - args = self._make_args(args) - kwargs = { - 'enabled': True, - } - service.cluster_update_policy.return_value = {'action': 'action_id'} - - sh.do_cluster_policy_update(service, args) - - service.cluster_update_policy.assert_called_once_with('C1', 'policy1', - **kwargs) - - def test_do_cluster_check(self): - service = mock.Mock() - args = self._make_args({'id': ['cluster1']}) - service.check_cluster = mock.Mock() - service.check_cluster.return_value = {'action': 'action_id'} - sh.do_cluster_check(service, args) - - service.check_cluster.assert_called_once_with('cluster1') - - def test_do_cluster_recover(self): - service = mock.Mock() - args = { - 'id': ['cluster1'], - 'check': 'false' - } - args = self._make_args(args) - params = { - 'check': False - } - service.recover_cluster = mock.Mock() - service.recover_cluster.return_value = {'action': 'action_id'} - - sh.do_cluster_recover(service, args) - - service.recover_cluster.assert_called_once_with('cluster1', **params) - - def test_do_cluster_collect(self): - service = mock.Mock() - args = self._make_args({ - 'path': 'path.to.attr', - 'list': False, - 'full_id': False, - 'id': 'cluster1' - }) - service.collect_cluster_attrs = mock.Mock( - return_value=[mock.Mock(node_id='FAKE1', attr_value='VALUE1')] - ) - - sh.do_cluster_collect(service, args) - - service.collect_cluster_attrs.assert_called_once_with( - 'cluster1', 'path.to.attr') - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_collect_as_list(self, mock_print): - service = mock.Mock() - args = self._make_args({ - 'path': 'path.to.attr', - 'list': True, - 'full_id': True, - 'id': 'cluster1' - }) - attrs = [mock.Mock(node_id='FAKE1', attr_value='VALUE1')] - fields = ['node_id', 'attr_value'] - formatters = {'attr_value': mock.ANY} - service.collect_cluster_attrs = mock.Mock(return_value=attrs) - - sh.do_cluster_collect(service, args) - - service.collect_cluster_attrs.assert_called_once_with( - 'cluster1', 'path.to.attr') - mock_print.assert_called_once_with(attrs, fields, - formatters=formatters) - - @mock.patch.object(utils, 'print_list') - def test_do_cluster_collect_as_list_with_shortid(self, mock_print): - service = mock.Mock() - args = self._make_args({ - 'path': 'path.to.attr', - 'list': True, - 'full_id': False, - 'id': 'cluster1' - }) - attrs = [mock.Mock(node_id='FAKE1', attr_value='VALUE1')] - fields = ['node_id', 'attr_value'] - formatters = {'node_id': mock.ANY, 'attr_value': mock.ANY} - service.collect_cluster_attrs = mock.Mock(return_value=attrs) - - sh.do_cluster_collect(service, args) - - service.collect_cluster_attrs.assert_called_once_with( - 'cluster1', 'path.to.attr') - mock_print.assert_called_once_with(attrs, fields, - formatters=formatters) - - def test_do_cluster_op(self): - service = mock.Mock() - args = { - 'id': 'cluster1', - 'operation': 'dance', - 'params': ['style=tango'] - } - args = self._make_args(args) - attrs = { - 'style': 'tango' - } - service.perform_operation_on_cluster = mock.Mock() - - sh.do_cluster_op(service, args) - - service.perform_operation_on_cluster.assert_called_once_with( - 'cluster1', 'dance', **attrs) - - def test_do_cluster_op_not_found(self): - service = mock.Mock() - ex = exc.HTTPNotFound - service.perform_operation_on_cluster.side_effect = ex - args = { - 'id': 'cluster1', - 'operation': 'swim', - 'params': [] - } - args = self._make_args(args) - - ex = self.assertRaises(exc.CommandError, - sh.do_cluster_op, service, args) - msg = _('Cluster "cluster1" is not found') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_node_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'index', 'status', 'cluster_id', 'physical_id', - 'profile_name', 'created_at', 'updated_at', 'project_id'] - args = { - 'cluster': 'cluster1', - 'sort': 'name:asc', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - 'filters': ['status=active'], - 'full_id': True, - } - queries = { - 'cluster_id': 'cluster1', - 'sort': 'name:asc', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - 'status': 'active', - } - args = self._make_args(args) - nodes = mock.Mock() - service.nodes.return_value = nodes - formatters = {} - sh.do_node_list(service, args) - mock_print.assert_called_once_with(nodes, fields, - formatters=formatters, - sortby_index=None) - service.nodes.assert_called_once_with(**queries) - - @mock.patch.object(utils, 'print_dict') - @mock.patch.object(utils, 'nested_dict_formatter') - def test_show_node(self, mock_nested, mock_print): - service = mock.Mock() - node_id = 'node1' - node = mock.Mock() - service.get_node.return_value = node - formatters = { - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - 'dependents': utils.json_formatter, - } - data = mock.Mock() - node.to_dict.return_value = data - - sh._show_node(service, node_id, show_details=False) - - service.get_node.assert_called_once_with(node_id, details=False) - mock_print.assert_called_once_with(data, formatters=formatters) - - @mock.patch.object(sh, '_show_node') - def test_do_node_create(self, mock_show): - args = { - 'name': 'node1', - 'cluster': 'cluster1', - 'profile': 'profile1', - 'role': 'master', - 'metadata': ['user=demo'], - } - args = self._make_args(args) - attrs = { - 'name': 'node1', - 'cluster_id': 'cluster1', - 'profile_id': 'profile1', - 'role': 'master', - 'metadata': {'user': 'demo'}, - } - service = mock.Mock() - node = mock.Mock() - node.id = 'node_id' - service.create_node.return_value = node - sh.do_node_create(service, args) - service.create_node.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, 'node_id') - - @mock.patch.object(sh, '_show_node') - def test_do_node_adopt(self, mock_show): - args = { - 'identity': 'fake-resoruce-id', - 'name': 'adopt-node1', - 'role': 'master', - 'metadata': ['user=demo'], - 'snapshot': None, - 'overrides': '{"networks": [{"network": "fake-net-name"}]}', - 'type': 'os.nova.server-1.0', - 'preview': False - } - args = self._make_args(args) - attrs = { - 'identity': 'fake-resoruce-id', - 'name': 'adopt-node1', - 'role': 'master', - 'metadata': {'user': 'demo'}, - 'overrides': {'networks': [{'network': 'fake-net-name'}]}, - 'snapshot': None, - 'type': 'os.nova.server-1.0', - } - service = mock.Mock() - node = mock.Mock() - node.id = 'node_id' - service.adopt_node.return_value = node - sh.do_node_adopt(service, args) - service.adopt_node.assert_called_once_with(**attrs) - mock_show.assert_called_once_with(service, 'node_id') - - @mock.patch.object(utils, 'print_dict') - @mock.patch.object(utils, 'nested_dict_formatter') - def test_do_node_adopt_preview(self, mock_nest, mock_print): - args = { - 'identity': 'fake-resoruce-id', - 'snapshot': None, - 'overrides': '{"networks": [{"network": "fake-net-name"}]}', - 'type': 'os.nova.server-1.0', - 'preview': True - } - args = self._make_args(args) - attrs = { - 'identity': 'fake-resoruce-id', - 'overrides': {'networks': [{'network': 'fake-net-name'}]}, - 'snapshot': None, - 'type': 'os.nova.server-1.0', - } - - fake_preview = { - "node_profile": { - "node_preview": { - "properties": { - }, - "type": "os.nova.server", - "version": "1.0"} - } - } - - service = mock.Mock() - service.adopt_node.return_value = fake_preview - sh.do_node_adopt(service, args) - service.adopt_node.assert_called_once_with(True, **attrs) - - formatters = {} - formatters['node_preview'] = utils.nested_dict_formatter( - ['type', 'version', 'properties'], - ['property', 'value']) - mock_print.assert_called_once_with(fake_preview['node_profile'], - formatters=formatters) - - @mock.patch.object(sh, '_show_node') - def test_do_node_show(self, mock_show): - service = mock.Mock() - args = { - 'id': 'node1', - 'details': False - } - args = self._make_args(args) - sh.do_node_show(service, args) - mock_show.assert_called_once_with(service, 'node1', False) - - @mock.patch.object(sh, '_show_node') - def test_do_node_update(self, mock_show): - service = mock.Mock() - args = { - 'id': 'node_id', - 'name': 'node1', - 'role': 'master', - 'profile': 'profile1', - 'metadata': ['user=demo'], - } - args = self._make_args(args) - attrs = { - 'name': 'node1', - 'role': 'master', - 'profile_id': 'profile1', - 'metadata': {'user': 'demo'}, - } - node = mock.Mock() - node.id = 'node_id' - service.get_node.return_value = node - sh.do_node_update(service, args) - service.get_node.assert_called_once_with('node_id') - service.update_node.assert_called_once_with(node, **attrs) - mock_show.assert_called_once_with(service, 'node_id') - - def test_do_node_delete(self): - service = mock.Mock() - args = self._make_args({'id': ['node1'], 'force_delete': False}) - service.delete_node = mock.Mock() - - sh.do_node_delete(service, args) - - service.delete_node.assert_called_once_with('node1', False, False) - - def test_do_node_delete_force(self): - service = mock.Mock() - args = self._make_args({'id': ['node1'], 'force_delete': True}) - service.delete_node = mock.Mock() - - sh.do_node_delete(service, args) - - service.delete_node.assert_called_once_with('node1', True, False) - - def test_do_node_check(self): - service = mock.Mock() - args = self._make_args({'id': ['node1']}) - service.check_node = mock.Mock() - - sh.do_node_check(service, args) - - service.check_node.assert_called_once_with('node1') - - def test_do_node_check_not_found(self): - service = mock.Mock() - ex = exc.HTTPNotFound - service.check_node.side_effect = ex - - args = self._make_args({'id': ['node1']}) - ex = self.assertRaises(exc.CommandError, - sh.do_node_check, service, args) - msg = _('Failed to check some of the specified nodes.') - self.assertEqual(msg, six.text_type(ex)) - - def test_do_node_recover(self): - service = mock.Mock() - args = { - 'id': ['node1'], - 'check': 'false' - } - args = self._make_args(args) - attrs = { - 'check': False - } - service.check_node = mock.Mock() - - sh.do_node_recover(service, args) - - service.recover_node.assert_called_once_with('node1', **attrs) - - def test_do_node_recover_not_found(self): - service = mock.Mock() - ex = exc.HTTPNotFound - service.recover_node.side_effect = ex - args = { - 'id': ['node1'], - 'check': 'false' - } - args = self._make_args(args) - - ex = self.assertRaises(exc.CommandError, - sh.do_node_recover, service, args) - msg = _('Failed to recover some of the specified nodes.') - self.assertEqual(msg, six.text_type(ex)) - - def test_do_node_op(self): - service = mock.Mock() - args = { - 'id': 'node1', - 'operation': 'dance', - 'params': ['style=tango'] - } - args = self._make_args(args) - attrs = { - 'style': 'tango' - } - service.perform_operation_on_node = mock.Mock() - - sh.do_node_op(service, args) - - service.perform_operation_on_node.assert_called_once_with( - 'node1', 'dance', **attrs) - - def test_do_node_op_not_found(self): - service = mock.Mock() - ex = exc.HTTPNotFound - service.perform_operation_on_node.side_effect = ex - args = { - 'id': 'node1', - 'operation': 'swim', - 'params': [] - } - args = self._make_args(args) - - ex = self.assertRaises(exc.CommandError, - sh.do_node_op, service, args) - msg = _('Node "node1" is not found') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_event_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'generated_at', 'obj_type', 'obj_id', 'obj_name', - 'action', 'status', 'level', 'cluster_id', 'project_id'] - field_labels = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', - 'action', 'status', 'level', 'cluster_id', - 'project_id'] - - args = { - 'sort': 'timestamp:asc', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - 'filters': ['action=NODE_DELETE'], - 'full_id': True, - } - queries = copy.deepcopy(args) - del queries['full_id'] - del queries['filters'] - queries['action'] = 'NODE_DELETE' - args = self._make_args(args) - formatters = {} - sortby_index = None - events = mock.Mock() - service.events.return_value = events - - sh.do_event_list(service, args) - - service.events.assert_called_once_with(**queries) - mock_print.assert_called_once_with(events, fields, - formatters=formatters, - sortby_index=sortby_index, - field_labels=field_labels) - - @mock.patch.object(utils, 'print_dict') - def test_do_event_show(self, mock_print): - class FakeEvent(object): - def to_dict(self): - pass - - service = mock.Mock() - args = { - 'id': 'event_id' - } - args = self._make_args(args) - - event = FakeEvent() - service.get_event.return_value = event - sh.do_event_show(service, args) - service.get_event.assert_called_once_with('event_id') - mock_print.assert_called_once_with(event.to_dict()) - - def test_do_event_show_not_found(self): - service = mock.Mock() - args = self._make_args({'id': 'FAKE'}) - # event not found - ex = exc.CommandError - service.get_event.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(ex, - sh.do_event_show, - service, args) - - @mock.patch.object(utils, 'print_list') - def test_do_action_list(self, mock_print): - service = mock.Mock() - fields = ['id', 'name', 'action', 'status', 'target_id', 'depends_on', - 'depended_by', 'created_at'] - args = { - 'sort': 'status', - 'limit': 20, - 'marker': 'marker_id', - 'global_project': True, - } - queries = copy.deepcopy(args) - args = self._make_args(args) - args.filters = ['status=ACTIVE'] - queries['status'] = 'ACTIVE' - actions = mock.Mock() - service.actions.return_value = actions - formatters = { - 'depends_on': mock.ANY, - 'depended_by': mock.ANY - } - args.full_id = True - sortby_index = None - sh.do_action_list(service, args) - service.actions.assert_called_once_with(**queries) - mock_print.assert_called_once_with(actions, fields, - formatters=formatters, - sortby_index=sortby_index) - - @mock.patch.object(utils, 'print_dict') - def test_do_action_show(self, mock_print): - class FakeAction(object): - def to_dict(self): - pass - - service = mock.Mock() - args = self._make_args({'id': 'action_id'}) - - action = FakeAction() - service.get_action.return_value = action - formatters = { - 'inputs': utils.json_formatter, - 'outputs': utils.json_formatter, - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - 'depends_on': utils.list_formatter, - 'depended_by': utils.list_formatter, - } - sh.do_action_show(service, args) - service.get_action.assert_called_once_with('action_id') - mock_print.assert_called_once_with(action.to_dict(), - formatters=formatters) - - def test_do_action_show_not_found(self): - service = mock.Mock() - args = self._make_args({'id': 'fake_id'}) - - service.get_action.side_effect = oexc.ResourceNotFound - ex = self.assertRaises(exc.CommandError, - sh.do_action_show, - service, args) - msg = _('Action not found: fake_id') - self.assertEqual(msg, six.text_type(ex)) - - @mock.patch.object(utils, 'print_list') - def test_do_service_list(self, mock_print): - service = mock.Mock() - fields = ['binary', 'host', 'status', 'state', 'updated_at', - 'disabled_reason'] - - result = mock.Mock() - service.services.return_value = result - formatters = {} - sh.do_service_list(service) - mock_print.assert_called_once_with(result, fields, - formatters=formatters) diff --git a/senlinclient/v1/shell.py b/senlinclient/v1/shell.py deleted file mode 100644 index 79501ed5..00000000 --- a/senlinclient/v1/shell.py +++ /dev/null @@ -1,1883 +0,0 @@ -# Licensed 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 logging -import subprocess -import threading -import time - -from openstack import exceptions as sdk_exc -from oslo_utils import strutils -import six - -from senlinclient.common import exc -from senlinclient.common.i18n import _ -from senlinclient.common import utils - -logger = logging.getLogger(__name__) - - -def show_deprecated(deprecated, recommended): - logger.warning( - ('"%(old)s" is deprecated and will be removed by Apr 2017, ' - 'please use "%(new)s" instead.'), - {'old': deprecated, 'new': recommended}) - - -def do_build_info(service, args=None): - """Retrieve build information.""" - show_deprecated('senlin build-info', 'openstack cluster build info') - result = service.get_build_info() - info = {'api': result.api, 'engine': result.engine} - - formatters = { - 'api': utils.json_formatter, - 'engine': utils.json_formatter, - } - utils.print_dict(info, formatters=formatters) - - -# PROFILE TYPES - -def do_profile_type_list(service, args=None): - """List the available profile types.""" - show_deprecated('senlin profile-type-list', - 'openstack cluster profile type list') - - class _ProfileType(object): - - def __init__(self, name, version, status): - self.name = name - self.version = version - self.support_status = status - - fields = ['name', 'version', 'support_status'] - types = service.profile_types() - results = [] - for t in types: - for v in t.support_status.keys(): - ss = '\n'.join([' since '.join((item['status'], item['since'])) - for item in t.support_status[v]]) - results.append(_ProfileType(t.name, v, ss)) - - utils.print_list(results, fields, sortby_index=0) - - -@utils.arg('type_name', metavar='', - help=_('Profile type to retrieve.')) -@utils.arg('-F', '--format', metavar='', - choices=utils.supported_formats.keys(), - help=_("The template output format, one of: %s.") - % ', '.join(utils.supported_formats.keys())) -def do_profile_type_show(service, args): - """Get the details about a profile type.""" - show_deprecated('senlin profile-type-show', - 'openstack cluster profile type show') - try: - res = service.get_profile_type(args.type_name) - except sdk_exc.ResourceNotFound: - raise exc.CommandError( - _('Profile Type not found: %s') % args.type_name) - - pt = res.to_dict() - - if args.format: - print(utils.format_output(pt, format=args.format)) - else: - print(utils.format_output(pt)) - - -@utils.arg('type_name', metavar='', - help=_('Profile type to retrieve.')) -@utils.arg('-F', '--format', metavar='', - choices=utils.supported_formats.keys(), - help=_("The template output format, one of: %s.") - % ', '.join(utils.supported_formats.keys())) -def do_profile_type_ops(service, args): - """Show the operations about a policy type.""" - show_deprecated('senlin profile-type-ops', - 'openstack cluster profile type ops') - try: - ops = service.list_profile_type_operations(args.type_name) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Profile Type not found: %s') - % args.type_name) - - if args.format: - print(utils.format_output(ops, format=args.format)) - else: - print(utils.format_output(ops)) - - -# PROFILES - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned profiles. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of profiles returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return profiles that appear after the given ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the list should include profiles from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_profile_list(service, args=None): - """List profiles that meet the criteria.""" - show_deprecated('senlin profile-list', 'openstack cluster profile list') - fields = ['id', 'name', 'type', 'created_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 1 - - profiles = service.profiles(**queries) - formatters = {} - if args.global_project: - fields.append('project_id') - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - } - if 'project_id' in fields: - formatters['project_id'] = lambda x: x.project_id[:8] - - utils.print_list(profiles, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_profile(service, profile_id): - try: - profile = service.get_profile(profile_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Profile not found: %s') % profile_id) - - formatters = { - 'metadata': utils.json_formatter, - } - - formatters['spec'] = utils.nested_dict_formatter( - ['type', 'version', 'properties'], - ['property', 'value']) - - utils.print_dict(profile.to_dict(), formatters=formatters) - - -@utils.arg('-s', '--spec-file', metavar='', required=True, - help=_('The spec file used to create the profile.')) -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Metadata values to be attached to the profile. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the profile to create.')) -def do_profile_create(service, args): - """Create a profile.""" - show_deprecated('senlin profile-create', - 'openstack cluster profile create') - spec = utils.get_spec_content(args.spec_file) - type_name = spec.get('type', None) - type_version = spec.get('version', None) - properties = spec.get('properties', None) - if type_name is None: - raise exc.CommandError(_("Missing 'type' key in spec file.")) - if type_version is None: - raise exc.CommandError(_("Missing 'version' key in spec file.")) - if properties is None: - raise exc.CommandError(_("Missing 'properties' key in spec file.")) - - if type_name == 'os.heat.stack': - stack_properties = utils.process_stack_spec(properties) - spec['properties'] = stack_properties - - params = { - 'name': args.name, - 'spec': spec, - 'metadata': utils.format_parameters(args.metadata), - } - - profile = service.create_profile(**params) - _show_profile(service, profile.id) - - -@utils.arg('id', metavar='', - help=_('Name or ID of profile to show.')) -def do_profile_show(service, args): - """Show the profile details.""" - show_deprecated('senlin profile-show', 'openstack cluster profile show') - _show_profile(service, args.id) - - -@utils.arg('-n', '--name', metavar='', - help=_('The new name for the profile.')) -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_("Metadata values to be attached to the profile. " - "This can be specified multiple times, or once with " - "key-value pairs separated by a semicolon. Use '{}' " - "can clean metadata "), - action='append') -@utils.arg('id', metavar='', - help=_('Name or ID of the profile to update.')) -def do_profile_update(service, args): - """Update a profile.""" - show_deprecated('senlin profile-update', - 'openstack cluster profile update') - params = { - 'name': args.name, - } - if args.metadata: - params['metadata'] = utils.format_parameters(args.metadata) - - # Find the profile first, we need its id - try: - profile = service.get_profile(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Profile not found: %s') % args.id) - service.update_profile(profile, **params) - _show_profile(service, profile.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of profile(s) to delete.')) -def do_profile_delete(service, args): - """Delete profile(s).""" - show_deprecated('senlin profile-delete', - 'openstack cluster profile delete') - failure_count = 0 - - for pid in args.id: - try: - service.delete_profile(pid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified profile(s).') - raise exc.CommandError(msg) - print('Profile deleted: %s' % args.id) - - -@utils.arg('-s', '--spec-file', metavar='', required=True, - help=_('The spec file of the profile to be validated.')) -def do_profile_validate(service, args): - """Validate a profile.""" - show_deprecated('senlin profile-validate', - 'openstack cluster profile validate') - spec = utils.get_spec_content(args.spec_file) - type_name = spec.get('type', None) - type_version = spec.get('version', None) - properties = spec.get('properties', None) - if type_name is None: - raise exc.CommandError(_("Missing 'type' key in spec file.")) - if type_version is None: - raise exc.CommandError(_("Missing 'version' key in spec file.")) - if properties is None: - raise exc.CommandError(_("Missing 'properties' key in spec file.")) - - if type_name == 'os.heat.stack': - stack_properties = utils.process_stack_spec(properties) - spec['properties'] = stack_properties - - params = { - 'spec': spec, - } - - profile = service.validate_profile(**params) - - formatters = { - 'metadata': utils.json_formatter, - } - - formatters['spec'] = utils.nested_dict_formatter( - ['type', 'version', 'properties'], - ['property', 'value']) - - utils.print_dict(profile.to_dict(), formatters=formatters) - - -# POLICY TYPES - - -def do_policy_type_list(service, args): - """List the available policy types.""" - show_deprecated('senlin policy-type-list', - 'openstack cluster policy type list') - - class _PolicyType(object): - - def __init__(self, name, version, status): - self.name = name - self.version = version - self.support_status = status - - fields = ['name', 'version', 'support_status'] - types = service.policy_types() - - results = [] - for t in types: - for v in t.support_status.keys(): - ss = '\n'.join([' since '.join((item['status'], item['since'])) - for item in t.support_status[v]]) - results.append(_PolicyType(t.name, v, ss)) - - utils.print_list(results, fields, sortby_index=0) - - -@utils.arg('type_name', metavar='', - help=_('Policy type to retrieve.')) -@utils.arg('-F', '--format', metavar='', - choices=utils.supported_formats.keys(), - help=_("The template output format, one of: %s.") - % ', '.join(utils.supported_formats.keys())) -def do_policy_type_show(service, args): - """Get the details about a policy type.""" - show_deprecated('senlin policy-type-show', - 'openstack cluster policy type show') - try: - res = service.get_policy_type(args.type_name) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Policy type not found: %s') % args.type_name) - - pt = res.to_dict() - if args.format: - print(utils.format_output(pt, format=args.format)) - else: - print(utils.format_output(pt)) - - -# POLICIES - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned policies. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of policies returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return policies that appear after the given ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the list should include policies from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_policy_list(service, args=None): - """List policies that meet the criteria.""" - show_deprecated('senlin policy-list', 'openstack cluster policy list') - fields = ['id', 'name', 'type', 'created_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 1 - policies = service.policies(**queries) - formatters = {} - if args.global_project: - fields.append('project_id') - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8] - } - if 'project_id' in fields: - formatters['project_id'] = lambda x: x.project_id[:8] - - utils.print_list(policies, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_policy(service, policy_id): - try: - policy = service.get_policy(policy_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Policy not found: %s') % policy_id) - - formatters = { - 'metadata': utils.json_formatter, - 'spec': utils.json_formatter, - } - utils.print_dict(policy.to_dict(), formatters=formatters) - - -@utils.arg('-s', '--spec-file', metavar='', required=True, - help=_('The spec file used to create the policy.')) -@utils.arg('name', metavar='', - help=_('Name of the policy to create.')) -def do_policy_create(service, args): - """Create a policy.""" - show_deprecated('senlin policy-create', 'openstack cluster policy create') - spec = utils.get_spec_content(args.spec_file) - attrs = { - 'name': args.name, - 'spec': spec, - } - - policy = service.create_policy(**attrs) - _show_policy(service, policy.id) - - -@utils.arg('id', metavar='', - help=_('Name or ID of the policy to be shown.')) -def do_policy_show(service, args): - """Show the policy details.""" - show_deprecated('senlin policy-show', 'openstack cluster policy show') - _show_policy(service, policy_id=args.id) - - -@utils.arg('-n', '--name', metavar='', - help=_('New name of the policy to be updated.')) -@utils.arg('id', metavar='', - help=_('Name of the policy to be updated.')) -def do_policy_update(service, args): - """Update a policy.""" - show_deprecated('senlin policy-update', 'openstack cluster policy update') - params = { - 'name': args.name, - } - - policy = service.get_policy(args.id) - if policy is not None: - service.update_policy(policy, **params) - _show_policy(service, policy_id=policy.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of policy(s) to delete.')) -def do_policy_delete(service, args): - """Delete policy(s).""" - show_deprecated('senlin policy-delete', 'openstack cluster policy delete') - failure_count = 0 - - for pid in args.id: - try: - service.delete_policy(pid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified policy(s).') - raise exc.CommandError(msg) - print('Policy deleted: %s' % args.id) - - -@utils.arg('-s', '--spec-file', metavar='', required=True, - help=_('The spec file of the policy to be validated.')) -def do_policy_validate(service, args): - """Validate a policy spec.""" - show_deprecated('senlin policy-validate', - 'openstack cluster policy validate') - spec = utils.get_spec_content(args.spec_file) - attrs = { - 'spec': spec, - } - - policy = service.validate_policy(**attrs) - formatters = { - 'metadata': utils.json_formatter, - 'spec': utils.json_formatter, - } - utils.print_dict(policy.to_dict(), formatters=formatters) - -# CLUSTERS - - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned clusters. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of clusters returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return clusters that appear after the given cluster ' - 'ID.')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the cluster list should include clusters from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_cluster_list(service, args=None): - """List the user's clusters.""" - show_deprecated('senlin cluster-list', 'openstack cluster list') - fields = ['id', 'name', 'status', 'created_at', 'updated_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 3 - - clusters = service.clusters(**queries) - formatters = {} - if args.global_project: - fields.append('project_id') - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8] - } - if 'project_id' in fields: - formatters['project_id'] = lambda x: x.project_id[:8] - - utils.print_list(clusters, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_cluster(service, cluster_id): - try: - cluster = service.get_cluster(cluster_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Cluster not found: %s') % cluster_id) - - formatters = { - 'config': utils.json_formatter, - 'metadata': utils.json_formatter, - 'node_ids': utils.list_formatter, - } - cluster_attrs = cluster.to_dict() - cluster_attrs.pop('is_profile_only') - utils.print_dict(cluster_attrs, formatters=formatters) - - -@utils.arg('-p', '--profile', metavar='', required=True, - help=_('Default profile Id or name used for this cluster.')) -@utils.arg('-n', '--min-size', metavar='', default=0, - help=_('Min size of the cluster. Default to 0.')) -@utils.arg('-m', '--max-size', metavar='', default=-1, - help=_('Max size of the cluster. Default to -1, means unlimited.')) -@utils.arg('-c', '--desired-capacity', metavar='', default=0, - help=_('Desired capacity of the cluster. Default to min_size if ' - 'min_size is specified else 0.')) -@utils.arg('-t', '--timeout', metavar='', type=int, - help=_('Cluster creation timeout in seconds.')) -@utils.arg('-C', '--config', metavar='<"key1=value1;key2=value2...">', - help=_('Configuration of the cluster. Default to {}. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Metadata values to be attached to the cluster. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the cluster to create.')) -def do_cluster_create(service, args): - """Create the cluster.""" - show_deprecated('senlin cluster-create', 'openstack cluster create') - if args.min_size and not args.desired_capacity: - args.desired_capacity = args.min_size - attrs = { - 'config': utils.format_parameters(args.config), - 'name': args.name, - 'profile_id': args.profile, - 'min_size': args.min_size, - 'max_size': args.max_size, - 'desired_capacity': args.desired_capacity, - 'metadata': utils.format_parameters(args.metadata), - 'timeout': args.timeout - } - - cluster = service.create_cluster(**attrs) - _show_cluster(service, cluster.id) - - -@utils.arg('-p', '--path', metavar='', required=True, - help=_('A Json path string specifying the attribute to collect.')) -@utils.arg('-L', '--list', default=False, action="store_true", - help=_('Print a full list that contains both node ids and ' - 'attribute values instead of values only. Default is ' - 'False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster(s) to operate on.')) -def do_cluster_collect(service, args): - """Collect attributes across a cluster.""" - show_deprecated('senlin cluster-collect', 'openstack cluster collect') - - attrs = service.collect_cluster_attrs(args.id, args.path) - if args.list: - fields = ['node_id', 'attr_value'] - formatters = { - 'attr_value': lambda x: utils.json_formatter(x.attr_value) - } - if not args.full_id: - formatters['node_id'] = lambda x: x.node_id[:8] - utils.print_list(attrs, fields, formatters=formatters) - else: - for attr in attrs: - print(attr.attr_value) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of cluster(s) to delete.')) -@utils.arg('-f', '--force-delete', default=False, action="store_true", - help=_('Force to delete cluster(s).')) -def do_cluster_delete(service, args): - """Delete the cluster(s).""" - show_deprecated('senlin cluster-delete', 'openstack cluster delete') - - result = {} - for cid in args.id: - try: - cluster = service.delete_cluster(cid, args.force_delete, False) - result[cid] = ('OK', cluster.location.split('/')[-1]) - except Exception as ex: - result[cid] = ('ERROR', six.text_type(ex)) - - for rid, res in result.items(): - utils.print_action_result(rid, res) - - -def _run_script(node_id, addr, net, addr_type, port, user, ipv6, identity_file, - script, options, output=None): - version = 6 if ipv6 else 4 - - # Select the network to use. - if net: - addresses = addr.get(net) - if not addresses: - output['status'] = _('FAILED') - output['reason'] = _("Node '%(node)s' is not attached to network " - "'%(net)s'.") % {'node': node_id, 'net': net} - return - else: - # network not specified - if len(addr) > 1: - output['status'] = _('FAILED') - output['reason'] = _("Node '%(node)s' is attached to more than " - "one network. Please pick the network to " - "use.") % {'node': node_id} - return - elif not addr: - output['status'] = _('FAILED') - output['reason'] = _("Node '%(node)s' is not attached to any " - "network.") % {'node': node_id} - return - else: - addresses = list(addr.values())[0] - - # Select the address in the selected network. - # If the extension is not present, we assume the address to be floating. - matching_addresses = [] - for a in addresses: - a_type = a.get('OS-EXT-IPS:type', 'floating') - a_version = a.get('version') - if (a_version == version and a_type == addr_type): - matching_addresses.append(a.get('addr')) - - if not matching_addresses: - output['status'] = _('FAILED') - output['reason'] = _("No address that would match network '%(net)s' " - "and type '%(type)s' of IPv%(ver)s has been " - "found for node '%(node)s'." - ) % {'net': net, 'type': addr_type, - 'ver': version, 'node': node_id} - return - - if len(matching_addresses) > 1: - output['status'] = _('FAILED') - output['reason'] = _("More than one IPv%(ver)s %(type)s address " - "found.") % {'ver': version, 'type': addr_type} - return - - ip_address = str(matching_addresses[0]) - identity = '-i %s' % identity_file if identity_file else '' - - cmd = [ - 'ssh', - '-%d' % version, - '-p%d' % port, - identity, - options, - '%s@%s' % (user, ip_address), - '%s' % script - ] - logger.debug("%s" % cmd) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - while proc.returncode is None: - time.sleep(1) - if proc.returncode == 0: - output['status'] = _('SUCCEEDED (0)') - output['output'] = stdout - if stderr: - output['error'] = stderr - else: - output['status'] = _('FAILED (%d)') % proc.returncode - output['output'] = stdout - if stderr: - output['error'] = stderr - - -@utils.arg("-p", "--port", metavar="", type=int, default=22, - help=_("Optional flag to indicate the port to use (Default=22).")) -@utils.arg("-t", "--address-type", type=str, default="floating", - help=_("Optional flag to indicate which IP type to use. Possible " - "values includes 'fixed' and 'floating' (the Default).")) -@utils.arg("-n", "--network", metavar='', default='', - help=_('Network to use for the ssh.')) -@utils.arg("-6", "--ipv6", action="store_true", default=False, - help=_("Optional flag to indicate whether to use an IPv6 address " - "attached to a server. (Defaults to IPv4 address)")) -@utils.arg("-u", "--user", metavar="", default="root", - help=_("Login to use.")) -@utils.arg("-i", "--identity-file", - help=_("Private key file, same as the '-i' option to the ssh " - "command.")) -@utils.arg("-O", "--ssh-options", default="", - help=_("Extra options to pass to ssh. see: man ssh.")) -@utils.arg("-s", "--script", metavar="", required=True, - help=_("Script file to run.")) -@utils.arg("id", metavar="", - help=_('Name or ID of the cluster.')) -def do_cluster_run(service, args): - """Run shell scripts on all nodes of a cluster.""" - show_deprecated('senlin cluster-run', 'openstack cluster run') - - if '@' in args.id: - user, cluster = args.id.split('@', 1) - args.user = user - args.cluster = cluster - - try: - attributes = service.collect_cluster_attrs(args.id, 'details') - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_("Cluster not found: %s") % args.id) - - script = None - try: - f = open(args.script, 'r') - script = f.read() - except Exception: - raise exc.CommandError(_("Cound not open script file: %s") % - args.script) - - tasks = dict() - for attr in attributes: - node_id = attr.node_id - addr = attr.attr_value['addresses'] - - output = dict() - th = threading.Thread( - target=_run_script, - args=(node_id, addr, args.network, args.address_type, args.port, - args.user, args.ipv6, args.identity_file, - script, args.ssh_options), - kwargs={'output': output}) - th.start() - tasks[th] = (node_id, output) - - for t in tasks: - t.join() - - for t in tasks: - node_id, result = tasks[t] - print("node: %s" % node_id) - print("status: %s" % result.get('status')) - if "reason" in result: - print("reason: %s" % result.get('reason')) - if "output" in result: - print("output:\n%s" % result.get('output')) - if "error" in result: - print("error:\n%s" % result.get('error')) - - -@utils.arg('-p', '--profile', metavar='', - help=_('ID or name of new profile to use.')) -@utils.arg('-P', '--profile-only', metavar='', default=False, - help=_("Whether the cluster should be updated profile only. " - "If false, it will be applied to all existing nodes. " - "If true, any newly created nodes will use the new profile, " - "but existing nodes will not be changed. Default is False.")) -@utils.arg('-t', '--timeout', metavar='', - help=_('New timeout (in seconds) value for the cluster.')) -@utils.arg('-C', '--config', metavar='<"key1=value1;key2=value2...">', - help=_('Configuration of the cluster. Default to {}. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_("Metadata values to be attached to the cluster. " - "This can be specified multiple times, or once with " - "key-value pairs separated by a semicolon. Use '{}' " - "can clean metadata "), - action='append') -@utils.arg('-n', '--name', metavar='', - help=_('New name for the cluster to update.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to be updated.')) -def do_cluster_update(service, args): - """Update the cluster.""" - show_deprecated('senlin cluster-update', 'openstack cluster update') - cluster = service.get_cluster(args.id) - attrs = { - 'name': args.name, - 'profile_id': args.profile, - 'profile_only': strutils.bool_from_string( - args.profile_only, strict=True - ), - 'metadata': utils.format_parameters(args.metadata), - 'timeout': args.timeout, - } - - service.update_cluster(cluster, **attrs) - _show_cluster(service, cluster.id) - - -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to show.')) -def do_cluster_show(service, args): - """Show details of the cluster.""" - show_deprecated('senlin cluster-show', 'openstack cluster show') - _show_cluster(service, args.id) - - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned nodes. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of nodes returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return nodes that appear after the given node ID.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to nodes from.')) -def do_cluster_node_list(service, args): - """List nodes from cluster.""" - show_deprecated('senlin cluster-node-list', - 'openstack cluster members list') - queries = { - 'cluster_id': args.id, - 'limit': args.limit, - 'marker': args.marker, - } - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - nodes = service.nodes(**queries) - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - 'physical_id': lambda x: x.physical_id[:8] if x.physical_id else '' - } - else: - formatters = {} - - fields = ['id', 'name', 'index', 'status', 'physical_id', 'created_at'] - utils.print_list(nodes, fields, formatters=formatters, sortby_index=5) - - -@utils.arg('-n', '--nodes', metavar='', required=True, - help=_('ID of nodes to be added; multiple nodes can be separated ' - 'with ","')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_node_add(service, args): - """Add specified nodes to cluster.""" - show_deprecated('senlin cluster-node-add', - 'openstack cluster node members add') - node_ids = args.nodes.split(',') - resp = service.cluster_add_nodes(args.id, node_ids) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-n', '--nodes', metavar='', required=True, - help=_('ID of nodes to be deleted; multiple nodes can be separated ' - 'with ",".')) -@utils.arg('-d', '--destroy-after-deletion', metavar='', - required=False, default=False, - help=_('Whether nodes should be destroyed after deleted. ' - 'Default is False.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_node_del(service, args): - """Delete specified nodes from cluster.""" - show_deprecated('senlin cluster-node-del', - 'openstack cluster node members del') - node_ids = args.nodes.split(',') - destroy = args.destroy_after_deletion - destroy = strutils.bool_from_string(destroy, strict=True) - kwargs = {"destroy_after_deletion": destroy} - resp = service.cluster_del_nodes(args.id, node_ids, **kwargs) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-n', '--nodes', metavar='', required=True, - help=_("OLD_NODE is the name or ID of a node to be replaced, " - "NEW_NODE is the name or ID of a node as replacement. " - "This can be specified multiple times, or once with " - "node-pairs separated by a comma ','."), - action='append') -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_node_replace(service, args): - """Replace the nodes in cluster with specified nodes.""" - show_deprecated('senlin cluster-node-replace', - 'openstack cluster node members replace') - nodepairs = {} - for nodepair in args.nodes: - key = nodepair.split('=')[0] - value = nodepair.split('=')[1] - nodepairs[key] = value - resp = service.cluster_replace_nodes(args.id, nodepairs) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-c', '--capacity', metavar='', type=int, - help=_('The desired number of nodes of the cluster.')) -@utils.arg('-a', '--adjustment', metavar='', type=int, - help=_('A positive integer meaning the number of nodes to add, ' - 'or a negative integer indicating the number of nodes to ' - 'remove.')) -@utils.arg('-p', '--percentage', metavar='', type=float, - help=_('A value that is interpreted as the percentage of size ' - 'adjustment. This value can be positive or negative.')) -@utils.arg('-t', '--min-step', metavar='', type=int, - help=_('An integer specifying the number of nodes for adjustment ' - 'when is specified.')) -@utils.arg('-s', '--strict', action='store_true', default=False, - help=_('A boolean specifying whether the resize should be ' - 'performed on a best-effort basis when the new capacity ' - 'may go beyond size constraints.')) -@utils.arg('-n', '--min-size', metavar='MIN', type=int, - help=_('New lower bound of cluster size.')) -@utils.arg('-m', '--max-size', metavar='MAX', type=int, - help=_('New upper bound of cluster size. A value of -1 indicates ' - 'no upper limit on cluster size.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_resize(service, args): - """Resize a cluster.""" - # validate parameters - # NOTE: this will be much simpler if cliutils supports exclusive groups - show_deprecated('senlin cluster-resize', 'openstack cluster resize') - action_args = {} - - capacity = args.capacity - adjustment = args.adjustment - percentage = args.percentage - min_size = args.min_size - max_size = args.max_size - min_step = args.min_step - - if sum(v is not None for v in (capacity, adjustment, percentage, min_size, - max_size)) == 0: - raise exc.CommandError(_("At least one parameter of 'capacity', " - "'adjustment', 'percentage', 'min_size', " - " and 'max_size' should be specified.")) - - if sum(v is not None for v in (capacity, adjustment, percentage)) > 1: - raise exc.CommandError(_("Only one of 'capacity', 'adjustment' and " - "'percentage' can be specified.")) - - action_args['adjustment_type'] = None - action_args['number'] = None - - if capacity is not None: - if capacity < 0: - raise exc.CommandError(_('Cluster capacity must be larger than ' - 'or equal to zero.')) - action_args['adjustment_type'] = 'EXACT_CAPACITY' - action_args['number'] = capacity - - if adjustment is not None: - if adjustment == 0: - raise exc.CommandError(_('Adjustment cannot be zero.')) - action_args['adjustment_type'] = 'CHANGE_IN_CAPACITY' - action_args['number'] = adjustment - - if percentage is not None: - if (percentage == 0 or percentage == 0.0): - raise exc.CommandError(_('Percentage cannot be zero.')) - action_args['adjustment_type'] = 'CHANGE_IN_PERCENTAGE' - action_args['number'] = percentage - - if min_step is not None: - if percentage is None: - raise exc.CommandError(_('Min step is only used with percentage.')) - - if min_size is not None: - if min_size < 0: - raise exc.CommandError(_('Min size cannot be less than zero.')) - if max_size is not None and max_size >= 0 and min_size > max_size: - raise exc.CommandError(_('Min size cannot be larger than ' - 'max size.')) - if capacity is not None and min_size > capacity: - raise exc.CommandError(_('Min size cannot be larger than the ' - 'specified capacity')) - - if max_size is not None: - if capacity is not None and max_size > 0 and max_size < capacity: - raise exc.CommandError(_('Max size cannot be less than the ' - 'specified capacity.')) - # do a normalization - if max_size < 0: - max_size = -1 - - action_args['min_size'] = min_size - action_args['max_size'] = max_size - action_args['min_step'] = min_step - action_args['strict'] = args.strict - - resp = service.cluster_resize(args.id, **action_args) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-c', '--count', metavar='', - help=_('Number of nodes to be added to the specified cluster.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_scale_out(service, args): - """Scale out a cluster by the specified number of nodes.""" - show_deprecated('senlin cluster-scale-out', 'openstack cluster expand') - resp = service.cluster_scale_out(args.id, args.count) - print('Request accepted by action %s' % resp['action']) - - -@utils.arg('-c', '--count', metavar='', - help=_('Number of nodes to be deleted from the specified cluster.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_scale_in(service, args): - """Scale in a cluster by the specified number of nodes.""" - show_deprecated('senlin cluster-scale-in', 'openstack cluster shrink') - resp = service.cluster_scale_in(args.id, args.count) - print('Request accepted by action %s' % resp['action']) - - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned results. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to query on.')) -def do_cluster_policy_list(service, args): - """List policies from cluster.""" - show_deprecated('senlin cluster-policy-list', - 'openstack cluster policy binding list') - fields = ['policy_id', 'policy_name', 'policy_type', 'is_enabled'] - - cluster = service.get_cluster(args.id) - queries = { - 'sort': args.sort, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 3 - policies = service.cluster_policies(cluster.id, **queries) - formatters = {} - if not args.full_id: - formatters = { - 'policy_id': lambda x: x.policy_id[:8] - } - - utils.print_list(policies, fields, formatters=formatters, - sortby_index=sortby_index) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of the policy to query on.')) -@utils.arg('id', metavar='', - help=_('ID or name of the cluster to query on.')) -def do_cluster_policy_show(service, args): - """Show a specific policy that is bound to the specified cluster.""" - show_deprecated('senlin cluster-policy-show', - 'openstack cluster policy binding show') - binding = service.get_cluster_policy(args.policy, args.id) - utils.print_dict(binding.to_dict()) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of policy to be attached.')) -@utils.arg('-e', '--enabled', metavar='', default=True, - help=_('Whether the policy should be enabled once attached. ' - 'Default to enabled.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_policy_attach(service, args): - """Attach policy to cluster.""" - show_deprecated('senlin cluster-policy-attach', - 'openstack cluster policy attach') - kwargs = { - 'enabled': strutils.bool_from_string(args.enabled, strict=True), - } - - resp = service.cluster_attach_policy(args.id, args.policy, **kwargs) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of policy to be detached.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_policy_detach(service, args): - """Detach policy from cluster.""" - show_deprecated('senlin cluster-policy-detach', - 'openstack cluster policy detach') - resp = service.cluster_detach_policy(args.id, args.policy) - print('Request accepted by action %s' % resp['action']) - - -@utils.arg('-p', '--policy', metavar='', required=True, - help=_('ID or name of policy to be updated.')) -@utils.arg('-e', '--enabled', metavar='', - help=_('Whether the policy should be enabled.')) -@utils.arg('id', metavar='', - help=_('Name or ID of cluster to operate on.')) -def do_cluster_policy_update(service, args): - """Update a policy's properties on a cluster.""" - show_deprecated('senlin cluster-policy-update', - 'openstack cluster policy binding update') - kwargs = { - 'enabled': strutils.bool_from_string(args.enabled, strict=True), - } - - resp = service.cluster_update_policy(args.id, args.policy, **kwargs) - print('Request accepted by action: %s' % resp['action']) - - -@utils.arg('id', metavar='', nargs='+', - help=_('ID or name of cluster(s) to operate on.')) -def do_cluster_check(service, args): - """Check the cluster(s).""" - show_deprecated('senlin cluster-check', 'openstack cluster check') - for cid in args.id: - resp = service.check_cluster(cid) - print('Cluster check request on cluster %(cid)s is accepted by ' - 'action %(action)s.' % {'cid': cid, 'action': resp['action']}) - - -@utils.arg('-c', '--check', metavar='', default=False, - help=_("Whether the cluster should check it's nodes status before " - "doing cluster recover. Default is false")) -@utils.arg('id', metavar='', nargs='+', - help=_('ID or name of cluster(s) to operate on.')) -def do_cluster_recover(service, args): - """Recover the cluster(s).""" - show_deprecated('senlin cluster-recover', 'openstack cluster recover') - - params = { - 'check': strutils.bool_from_string(args.check, strict=True) - } - - for cid in args.id: - resp = service.recover_cluster(cid, **params) - print('Cluster recover request on cluster %(cid)s is accepted by ' - 'action %(action)s.' % {'cid': cid, 'action': resp['action']}) - - -@utils.arg('-p', '--params', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_("Parameter name and values for the operation specified. " - "This can be specified multiple times, or once with " - "key-value pairs separated by a semicolon."), - action='append') -@utils.arg('-o', '--operation', metavar='', - help=_("Name of an operation to be executed on the cluster.")) -@utils.arg('id', metavar='', - help=_('ID or name of a cluster.')) -def do_cluster_op(service, args): - """Run an operation on a cluster.""" - show_deprecated('senlin cluster-op', 'openstack cluster op') - params = utils.format_parameters(args.params) - - try: - service.perform_operation_on_cluster(args.id, args.operation, - **params) - except exc.HTTPNotFound: - raise exc.CommandError(_('Cluster "%s" is not found') % args.id) - print('Request accepted') - - -# NODES - - -@utils.arg('-c', '--cluster', metavar='', - help=_('ID or name of cluster from which nodes are to be listed.')) -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned nodes. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of nodes returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return nodes that appear after the given node ID.')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that this node list should include nodes from ' - 'all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_node_list(service, args): - """Show list of nodes.""" - show_deprecated('senlin node-list', 'openstack cluster node list') - - fields = ['id', 'name', 'index', 'status', 'cluster_id', 'physical_id', - 'profile_name', 'created_at', 'updated_at'] - queries = { - 'cluster_id': args.cluster, - 'sort': args.sort, - 'limit': args.limit, - 'marker': args.marker, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 6 - - nodes = service.nodes(**queries) - - if args.global_project: - fields.append('project_id') - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - 'cluster_id': lambda x: x.cluster_id[:8] if x.cluster_id else '', - 'physical_id': lambda x: x.physical_id[:8] if x.physical_id else '' - } - if 'project_id' in fields: - formatters['project_id'] = lambda x: x.project_id[:8] - else: - formatters = {} - - utils.print_list(nodes, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_node(service, node_id, show_details=False): - """Show detailed info about the specified node.""" - try: - node = service.get_node(node_id, details=show_details) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Node not found: %s') % node_id) - - formatters = { - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - 'dependents': utils.json_formatter, - } - data = node.to_dict() - if show_details and data['details']: - formatters['details'] = utils.nested_dict_formatter( - list(data['details'].keys()), ['property', 'value']) - - utils.print_dict(data, formatters=formatters) - - -@utils.arg('-p', '--profile', metavar='', required=True, - help=_('Profile Id or name used for this node.')) -@utils.arg('-c', '--cluster', metavar='', - help=_('Cluster Id for this node.')) -@utils.arg('-r', '--role', metavar='', - help=_('Role for this node in the specific cluster.')) -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Metadata values to be attached to the node. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the node to create.')) -def do_node_create(service, args): - """Create the node.""" - show_deprecated('senlin node-create', 'openstack cluster node create') - attrs = { - 'name': args.name, - 'cluster_id': args.cluster, - 'profile_id': args.profile, - 'role': args.role, - 'metadata': utils.format_parameters(args.metadata), - } - - node = service.create_node(**attrs) - _show_node(service, node.id) - - -@utils.arg('-i', '--identity', metavar='', required=True, - help=_('Physical resource id.')) -@utils.arg('-t', '--type', metavar='', required=True, - help=_('The name of the profile type.')) -@utils.arg('-r', '--role', metavar='', - help=_('Role for this node in the specific cluster.')) -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Metadata values to be attached to the node. ' - 'This can be specified multiple times, or once with ' - 'key-value pairs separated by a semicolon.'), - action='append') -@utils.arg('-n', '--name', metavar='', - help=_('The name for the node.')) -@utils.arg('-o', '--overrides', metavar='', - help=_('JSON formatted specification for overriding this node ' - 'properties.')) -@utils.arg('-p', '--preview', default=False, - help=_('Whether preview the node adopt request. If set, ' - 'only previewing this node and do not adopt.'), - action='store_true') -@utils.arg('-s', '--snapshot', default=False, - help=_('Whether a shapshot of the existing physical object should ' - 'be created before the object is adopted as a node.'), - action='store_true') -def do_node_adopt(service, args): - """Adopt (or preview) a node.""" - show_deprecated('senlin node-adopt', 'openstack cluster node adopt') - if args.preview: - _do_node_adopt_preview(service, args) - else: - _do_node_adopt(service, args) - - -def _do_node_adopt_preview(service, args): - attrs = { - 'identity': args.identity, - 'overrides': utils.format_json_parameter(args.overrides), - 'snapshot': args.snapshot, - 'type': args.type - } - - node = service.adopt_node(True, **attrs) - - formatters = {} - formatters['node_preview'] = utils.nested_dict_formatter( - ['type', 'version', 'properties'], - ['property', 'value']) - utils.print_dict(node['node_profile'], formatters=formatters) - - -def _do_node_adopt(service, args): - attrs = { - 'identity': args.identity, - 'name': args.name, - 'role': args.role, - 'metadata': utils.format_parameters(args.metadata), - 'overrides': utils.format_json_parameter(args.overrides), - 'snapshot': args.snapshot, - 'type': args.type - } - - node = service.adopt_node(**attrs) - _show_node(service, node.id) - - -@utils.arg('-D', '--details', default=False, action="store_true", - help=_('Include physical object details.')) -@utils.arg('id', metavar='', - help=_('Name or ID of the node to show the details for.')) -def do_node_show(service, args): - """Show detailed info about the specified node.""" - show_deprecated('senlin node-show', 'openstack cluster node show') - _show_node(service, args.id, args.details) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of node(s) to delete.')) -@utils.arg('-f', '--force-delete', default=False, action="store_true", - help=_('Force to delete the node(s).')) -def do_node_delete(service, args): - """Delete the node(s).""" - show_deprecated('senlin node-delete', 'openstack cluster node delete') - - result = {} - for nid in args.id: - try: - node = service.delete_node(nid, args.force_delete, False) - result[nid] = ('OK', node.location.split('/')[-1]) - except Exception as ex: - result[nid] = ('ERROR', six.text_type(ex)) - - for rid, res in result.items(): - utils.print_action_result(rid, res) - - -@utils.arg('-n', '--name', metavar='', - help=_('New name for the node.')) -@utils.arg('-p', '--profile', metavar='', - help=_('ID or name of new profile to use.')) -@utils.arg('-r', '--role', metavar='', - help=_('Role for this node in the specific cluster.')) -@utils.arg('-M', '--metadata', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_("Metadata values to be attached to the node. " - "This can be specified multiple times, or once with " - "key-value pairs separated by a semicolon. Use '{}' " - "can clean metadata "), - action='append') -@utils.arg('id', metavar='', - help=_('Name or ID of node to update.')) -def do_node_update(service, args): - """Update the node.""" - show_deprecated('senlin node-update', 'openstack cluster node update') - # Find the node first, we need its UUID - try: - node = service.get_node(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Node not found: %s') % args.id) - - attrs = { - 'name': args.name, - 'role': args.role, - 'profile_id': args.profile, - 'metadata': utils.format_parameters(args.metadata), - } - - service.update_node(node, **attrs) - _show_node(service, node.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('ID or name of node(s) to check.')) -def do_node_check(service, args): - """Check the node(s).""" - show_deprecated('senlin node-check', 'openstack cluster node check') - failure_count = 0 - - for nid in args.id: - try: - service.check_node(nid) - except exc.HTTPNotFound: - failure_count += 1 - print('Node id "%s" not found' % nid) - if failure_count > 0: - msg = _('Failed to check some of the specified nodes.') - raise exc.CommandError(msg) - print('Request accepted') - - -@utils.arg('-c', '--check', metavar='', default=False, - help=_("Whether the node(s) should check physical resource status " - "before doing node recover.Default is false")) -@utils.arg('id', metavar='', nargs='+', - help=_('ID or name of node(s) to recover.')) -def do_node_recover(service, args): - """Recover the node(s).""" - show_deprecated('senlin node-recover', 'openstack cluster node recover') - failure_count = 0 - - params = { - 'check': strutils.bool_from_string(args.check, strict=True) - } - - for nid in args.id: - try: - service.recover_node(nid, **params) - except exc.HTTPNotFound: - failure_count += 1 - print('Node id "%s" not found' % nid) - if failure_count > 0: - msg = _('Failed to recover some of the specified nodes.') - raise exc.CommandError(msg) - print('Request accepted') - - -@utils.arg('-p', '--params', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_("Parameter name and values for the operation specified. " - "This can be specified multiple times, or once with " - "key-value pairs separated by a semicolon."), - action='append') -@utils.arg('-o', '--operation', metavar='', - help=_("Name of an operation to be executed on the node")) -@utils.arg('id', metavar='', - help=_('ID or name of a node.')) -def do_node_op(service, args): - """Run an operation on a node.""" - show_deprecated('senlin node-op', 'openstack cluster node op') - if args.params: - params = utils.format_parameters(args.params) - else: - params = {} - - try: - service.perform_operation_on_node(args.id, args.operation, - **params) - except exc.HTTPNotFound: - raise exc.CommandError(_('Node "%s" is not found') % args.id) - print('Request accepted') - - -# RECEIVERS - - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned receivers. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of receivers returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return receivers that appear after the given ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Indicate that the list should include receivers from' - ' all projects. This option is subject to access policy ' - 'checking. Default is False.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_receiver_list(service, args): - """List receivers that meet the criteria.""" - show_deprecated('senlin receiver-list', 'openstack cluster receiver list') - fields = ['id', 'name', 'type', 'cluster_id', 'action', 'created_at'] - queries = { - 'limit': args.limit, - 'marker': args.marker, - 'sort': args.sort, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 0 - - receivers = service.receivers(**queries) - formatters = {} - if args.global_project: - fields.append('project_id') - fields.append('user_id') - if not args.full_id: - formatters = { - 'id': lambda x: x.id[:8], - 'cluster_id': lambda x: x.cluster_id[:8] if x.cluster_id else '-', - } - if 'project_id' in fields: - formatters['project_id'] = lambda x: x.project_id[:8] - formatters['user_id'] = lambda x: x.user_id[:8] - - utils.print_list(receivers, fields, formatters=formatters, - sortby_index=sortby_index) - - -def _show_receiver(service, receiver_id): - try: - receiver = service.get_receiver(receiver_id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Receiver not found: %s') % receiver_id) - - formatters = { - 'actor': utils.json_formatter, - 'params': utils.json_formatter, - 'channel': utils.json_formatter, - } - - utils.print_dict(receiver.to_dict(), formatters=formatters) - - -@utils.arg('id', metavar='', - help=_('Name or ID of the receiver to show.')) -def do_receiver_show(service, args): - """Show the receiver details.""" - show_deprecated('senlin receiver-show', 'openstack cluster receiver show') - _show_receiver(service, receiver_id=args.id) - - -@utils.arg('-t', '--type', metavar='', default='webhook', - help=_('Type of the receiver to create. Receiver type can be ' - '"webhook" or "message". Default to "webhook".')) -@utils.arg('-c', '--cluster', metavar='', - help=_('Targeted cluster for this receiver. Required if receiver ' - 'type is webhook.')) -@utils.arg('-a', '--action', metavar='', - help=_('Name or ID of the targeted action to be triggered. ' - 'Required if receiver type is webhook.')) -@utils.arg('-P', '--params', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('A dictionary of parameters that will be passed to target ' - 'action when the receiver is triggered.'), - action='append') -@utils.arg('name', metavar='', - help=_('Name of the receiver to create.')) -def do_receiver_create(service, args): - """Create a receiver.""" - show_deprecated('senlin receiver-create', - 'openstack cluster receiver create') - - if args.type == 'webhook': - if (not args.cluster or not args.action): - msg = _('cluster and action parameters are required to create ' - 'webhook type of receiver.') - raise exc.CommandError(msg) - - params = { - 'name': args.name, - 'type': args.type, - 'cluster_id': args.cluster, - 'action': args.action, - 'params': utils.format_parameters(args.params) - } - - receiver = service.create_receiver(**params) - _show_receiver(service, receiver.id) - - -@utils.arg('-n', '--name', metavar='', - help=_('The new name for the receiver.')) -@utils.arg('-a', '--action', metavar='', - help=_('Name or ID of the targeted action to be triggered. ' - 'Required if receiver type is webhook.')) -@utils.arg('-P', '--params', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('A dictionary of parameters that will be passed to target ' - 'action when the receiver is triggered.'), - action='append') -@utils.arg('id', metavar='', - help=_('Name or ID of receiver to update.')) -def do_receiver_update(service, args): - """Update a receiver.""" - show_deprecated('senlin receiver-update', - 'openstack cluster receiver update') - params = { - 'name': args.name, - 'action': args.action, - 'params': utils.format_parameters(args.params) - } - - # Find the receiver first, we need its id - try: - receiver = service.get_receiver(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Receiver not found: %s') % args.id) - service.update_receiver(receiver, **params) - _show_receiver(service, receiver.id) - - -@utils.arg('id', metavar='', nargs='+', - help=_('Name or ID of receiver(s) to delete.')) -def do_receiver_delete(service, args): - """Delete receiver(s).""" - show_deprecated('senlin receiver-delete', - 'openstack cluster receiver delete') - failure_count = 0 - - for wid in args.id: - try: - service.delete_receiver(wid, False) - except Exception as ex: - failure_count += 1 - print(ex) - if failure_count > 0: - msg = _('Failed to delete some of the specified receiver(s).') - raise exc.CommandError(msg) - print('Receivers deleted: %s' % args.id) - - -# EVENTS - - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned events. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of events returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return events that appear after the given event ID.')) -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Whether events from all projects should be listed. ' - ' Default to False. Setting this to True may demand ' - 'for an admin privilege.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_event_list(service, args): - """List events.""" - show_deprecated('senlin event-list', 'openstack cluster event list') - fields = ['id', 'generated_at', 'obj_type', 'obj_id', 'obj_name', 'action', - 'status', 'level', 'cluster_id'] - field_labels = ['id', 'timestamp', 'obj_type', 'obj_id', 'obj_name', - 'action', 'status', 'level', 'cluster_id'] - - queries = { - 'sort': args.sort, - 'limit': args.limit, - 'marker': args.marker, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 0 - - formatters = {} - if args.global_project: - fields.append('project_id') - field_labels.append('project_id') - if not args.full_id: - formatters['id'] = lambda x: x.id[:8] - formatters['obj_id'] = lambda x: x.obj_id[:8] if x.obj_id else '' - formatters['cluster_id'] = (lambda x: x.cluster_id[:8] - if x.cluster_id else '') - if 'project_id' in fields: - formatters['project_id'] = lambda x: x.project_id[:8] - - events = service.events(**queries) - utils.print_list(events, fields, formatters=formatters, - sortby_index=sortby_index, field_labels=field_labels) - - -@utils.arg('id', metavar='', - help=_('ID of event to display details for.')) -def do_event_show(service, args): - """Describe the event.""" - show_deprecated('senlin event-show', 'openstack cluster event show') - try: - event = service.get_event(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_("Event not found: %s") % args.id) - - utils.print_dict(event.to_dict()) - - -# ACTIONS - - -@utils.arg('-f', '--filters', metavar='<"KEY1=VALUE1;KEY2=VALUE2...">', - help=_('Filter parameters to apply on returned actions. ' - 'This can be specified multiple times, or once with ' - 'parameters separated by a semicolon.'), - action='append') -@utils.arg('-o', '--sort', metavar='', - help=_('Sorting option which is a string containing a list of keys ' - 'separated by commas. Each key can be optionally appended ' - 'by a sort direction (:asc or :desc)')) -@utils.arg('-l', '--limit', metavar='', - help=_('Limit the number of actions returned.')) -@utils.arg('-m', '--marker', metavar='', - help=_('Only return actions that appear after the given node ID.')) -@utils.arg('-g', '--global-project', default=False, action="store_true", - help=_('Whether actions from all projects should be listed. ' - ' Default to False. Setting this to True may demand ' - 'for an admin privilege.')) -@utils.arg('-F', '--full-id', default=False, action="store_true", - help=_('Print full IDs in list.')) -def do_action_list(service, args): - """List actions.""" - show_deprecated('senlin action-list', 'openstack cluster action list') - fields = ['id', 'name', 'action', 'status', 'target_id', 'depends_on', - 'depended_by', 'created_at'] - - queries = { - 'sort': args.sort, - 'limit': args.limit, - 'marker': args.marker, - 'global_project': args.global_project, - } - - if args.filters: - queries.update(utils.format_parameters(args.filters)) - - sortby_index = None if args.sort else 0 - - actions = service.actions(**queries) - - formatters = {} - s = None - if not args.full_id: - s = 8 - formatters['id'] = lambda x: x.id[:s] - formatters['target_id'] = lambda x: x.target_id[:s] - - formatters['depends_on'] = lambda x: '\n'.join(a[:s] for a in x.depends_on) - formatters['depended_by'] = lambda x: '\n'.join(a[:s] for a in x. - depended_by) - - utils.print_list(actions, fields, formatters=formatters, - sortby_index=sortby_index) - - -@utils.arg('id', metavar='', - help=_('Name or ID of the action to show the details for.')) -def do_action_show(service, args): - """Show detailed info about the specified action.""" - show_deprecated('senlin action-show', 'openstack cluster action show') - try: - action = service.get_action(args.id) - except sdk_exc.ResourceNotFound: - raise exc.CommandError(_('Action not found: %s') % args.id) - - formatters = { - 'inputs': utils.json_formatter, - 'outputs': utils.json_formatter, - 'metadata': utils.json_formatter, - 'data': utils.json_formatter, - 'depends_on': utils.list_formatter, - 'depended_by': utils.list_formatter, - } - - utils.print_dict(action.to_dict(), formatters=formatters) - - -def do_service_list(service, args=None): - """Show a list of all running services.""" - show_deprecated('senlin service-list', - 'openstack cluster service list') - fields = ['binary', 'host', 'status', 'state', 'updated_at', - 'disabled_reason'] - queries = {} - result = service.services(**queries) - - formatters = {} - utils.print_list(result, fields, formatters=formatters) diff --git a/setup.cfg b/setup.cfg index 5fae003e..56fbbaf5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,9 +23,6 @@ packages = senlinclient [entry_points] -console_scripts = - senlin = senlinclient.shell:main - openstack.cli.extension = clustering = senlinclient.plugin diff --git a/tools/senlin.bash_completion b/tools/senlin.bash_completion deleted file mode 100644 index a82bad82..00000000 --- a/tools/senlin.bash_completion +++ /dev/null @@ -1,27 +0,0 @@ -# bash completion for openstack senlin - -_senlin_opts="" # lazy init -_senlin_flags="" # lazy init -_senlin_opts_exp="" # lazy init -_senlin() -{ - local cur prev kbc - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - if [ "x$_senlin_opts" == "x" ] ; then - kbc="`senlin bash-completion | sed -e "s/ -h / /"`" - _senlin_opts="`echo "$kbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _senlin_flags="`echo " $kbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _senlin_opts_exp="`echo $_senlin_opts | sed -e "s/[ ]/|/g"`" - fi - - if [[ " ${COMP_WORDS[@]} " =~ " "($_senlin_opts_exp)" " && "$prev" != "help" ]] ; then - COMPREPLY=($(compgen -W "${_senlin_flags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_senlin_opts}" -- ${cur})) - fi - return 0 -} -complete -o default -F _senlin senlin