kolla-cli: deprecation - Removing project content
step 2b from: https://docs.openstack.org/project-team-guide/repository.html#step-2b-remove-project-content Deprecation notice: http://lists.openstack.org/pipermail/openstack-discuss/2020-May/014983.html Change-Id: I744f5c17232928cec68c1b90b2f5d791faf7d9e7 Depends-On: https://review.opendev.org/748259
This commit is contained in:
parent
7ba715987a
commit
3b56a5e0f7
|
@ -1,27 +0,0 @@
|
|||
etc/
|
||||
*.pyc
|
||||
.stestr/*
|
||||
.tox/*
|
||||
kolla_cli.egg-info/*
|
||||
.mypy_cache/*
|
||||
.stestr
|
||||
|
||||
# Files generated by JetBrains
|
||||
.idea/
|
||||
|
||||
# Files generates by Eclipse
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Files generated by Ansible
|
||||
ansible/*.retry
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
.coverage
|
||||
cover/
|
|
@ -1,3 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_path=${OS_TEST_PATH:-./kolla_cli/tests/unit}
|
||||
top_dir=./
|
38
.zuul.yaml
38
.zuul.yaml
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
- job:
|
||||
name: kollacli-tox-functional-py36
|
||||
parent: openstack-tox
|
||||
description: |
|
||||
Run tox-based py36 functional tests for the OpenStack Kolla-cli project.
|
||||
Uses tox with the ``functional`` environment.
|
||||
irrelevant-files:
|
||||
- ^.*\.rst$
|
||||
- ^doc/source/.*$
|
||||
- ^releasenotes/.*$
|
||||
vars:
|
||||
tox_envlist: functional-py36
|
||||
timeout: 3600
|
||||
|
||||
- job:
|
||||
name: kollacli-tox-mypy
|
||||
parent: openstack-tox
|
||||
description: |
|
||||
Run static mypy type checker for the OpenStack Kolla-cli project.
|
||||
vars:
|
||||
tox_envlist: mypy
|
||||
timeout: 3600
|
||||
|
||||
- project:
|
||||
templates:
|
||||
- openstack-cover-jobs
|
||||
- openstack-lower-constraints-jobs
|
||||
- openstack-python3-victoria-jobs
|
||||
- publish-openstack-docs-pti
|
||||
check:
|
||||
jobs:
|
||||
- kollacli-tox-mypy
|
||||
- kollacli-tox-functional-py36
|
||||
gate:
|
||||
jobs:
|
||||
- kollacli-tox-mypy
|
||||
- kollacli-tox-functional-py36
|
176
LICENSE
176
LICENSE
|
@ -1,176 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
142
README.rst
142
README.rst
|
@ -1,135 +1,13 @@
|
|||
**This project is deprecated since the Ussuri release and will not be
|
||||
maintained in the future.**
|
||||
This project is no longer maintained.
|
||||
|
||||
=========
|
||||
Kolla-CLI
|
||||
=========
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
The Kolla-CLI project provides the ability to more easily manage
|
||||
Kolla-Ansible deployments. It provides both a CLI and a python
|
||||
API that you can use to configure and deploy OpenStack using Kolla-Ansible.
|
||||
Please use Kolla Ansible at https://opendev.org/openstack/kolla-ansible
|
||||
directly or Kayobe at https://opendev.org/openstack/kayobe .
|
||||
|
||||
Kolla-Ansible requires that hosts, groups, and services are specified
|
||||
in an inventory file. With Kolla-CLI, you can add/remove hosts, change group
|
||||
associations, etc from the CLI or API. Kolla-Ansible also maintains
|
||||
passwords and various configuration variables in a variety of global, group
|
||||
and host files. With Kolla-CLI, you can now view and change these from the
|
||||
CLI/API.
|
||||
|
||||
Finally, Kolla-CLI provides commands to setup the SSH keys on hosts, run
|
||||
deployments and perform upgrades.
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
||||
The installation process below assumes that the kolla-ansible repository
|
||||
exists at the same level as the kolla-cli repository. This is made clear
|
||||
in the cli_setup.py script which makes a relative '../' reference to
|
||||
the kolla-ansible repository. If your kolla-ansible directory is somewhere
|
||||
else then that location can be passed as an argument to the cli_setup.py
|
||||
script. The location on the system where the kolla-cli expects the
|
||||
kolla-ansible files to be and installs them to can be tweaked by setting
|
||||
the KOLLA_HOME and KOLLA_ETC environment variables before running the
|
||||
cli_setup.py script, and while running the kolla-cli command itself. The
|
||||
default value for KOLLA_HOME is /usr/share/kolla-ansible and the default
|
||||
value for KOLLA_ETC is /etc/kolla.
|
||||
|
||||
The following steps can be used to build / run the kolla-cli
|
||||
|
||||
* install ansible and docker
|
||||
* virtualenv .venv
|
||||
* . .venv/bin/activate
|
||||
* pip install -r requirements.txt
|
||||
* python setup.py install
|
||||
* python ./cli_setup.py
|
||||
* kolla-cli
|
||||
|
||||
At that point you will be dropped into the kollacli shell where
|
||||
you can run commands like help or ? to see what commands are
|
||||
available and any of the sub commands can be executed directly.
|
||||
|
||||
Alternately you can not use the shell and just execute commands
|
||||
directly via kollacli host add, etc.
|
||||
|
||||
If you make changes to the i18n strings (denoted by methods like
|
||||
_("message")) make sure to re-generate the i18n files with the
|
||||
``python setup.py extract_messages`` command and check in the
|
||||
files generated in openstack-kollacli.
|
||||
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
To use the API, import the ClientAPI into your module:
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
|
||||
Then define a global:
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
And then you can use that global to execute API commands, for example,
|
||||
to add a host to the inventory:
|
||||
|
||||
CLIENT.host_add(['host_name'])
|
||||
|
||||
Generating Documentation
|
||||
========================
|
||||
|
||||
We use `Sphinx <https://pypi.python.org/pypi/Sphinx>`_ to maintain the
|
||||
documentation. You can install Sphinx using pip.
|
||||
|
||||
::
|
||||
|
||||
$ pip install -U Sphinx
|
||||
|
||||
In addition to Sphinx you will also need the following requirements
|
||||
(covered by `doc/requirements.txt`)::
|
||||
|
||||
$ pip install openstackdocstheme reno 'reno[sphinx]'
|
||||
|
||||
The source code of the documentation are under *doc*, you can generate the
|
||||
html files using the following command. If the generation succeeds,a
|
||||
*build/html* dir will be created under *doc*.
|
||||
|
||||
::
|
||||
|
||||
$ cd doc
|
||||
$ make html
|
||||
|
||||
Now you can serve the documentation at http://localhost:8080 as a simple
|
||||
website.
|
||||
|
||||
::
|
||||
|
||||
$ cd build/html
|
||||
$ python -m SimpleHTTPServer 8080
|
||||
|
||||
Getting Involved
|
||||
================
|
||||
|
||||
Need a feature? Find a bug? Let us know! Contributions are much
|
||||
appreciated and should follow the standard `Gerrit
|
||||
workflow <https://docs.openstack.org/infra/manual/developers.html>`__.
|
||||
|
||||
- We communicate using the #openstack-kolla irc channel.
|
||||
- File bugs, blueprints, track releases, etc on
|
||||
`Launchpad <https://launchpad.net/kolla-cli>`__.
|
||||
- Attend weekly
|
||||
`meetings <https://wiki.openstack.org/wiki/Meetings/Kolla>`__.
|
||||
- Contribute `code <https://opendev.org/openstack/kolla-cli>`__.
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
Check out who is `contributing
|
||||
code <https://www.stackalytics.com/?module=kolla-group&metric=commits>`__ and
|
||||
`contributing
|
||||
reviews <https://www.stackalytics.com/?module=kolla-group&metric=marks>`__.
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
If you get an error about missing python.h install the python-dev
|
||||
package via apt-get or yum or whatever mechanism is appropriate
|
||||
for your platform.
|
||||
For any further questions, please email
|
||||
openstack-discuss@lists.openstack.org or join #openstack-kolla on
|
||||
Freenode.
|
||||
|
|
144
cli_setup.py
144
cli_setup.py
|
@ -1,144 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2018 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
from kolla_cli.common import utils
|
||||
|
||||
kolla_ansible_source_base = '../kolla-ansible'
|
||||
kolla_ansible_home_target = utils.get_kolla_ansible_home()
|
||||
kolla_ansible_etc_target = utils.get_kolla_etc()
|
||||
ansible = 'ansible'
|
||||
kolla = 'kolla'
|
||||
kolla_cli = 'kolla-cli'
|
||||
tools = 'tools'
|
||||
|
||||
|
||||
def setup_ansible_etc():
|
||||
# if the kolla-cli directory for the inventory doesn't exist
|
||||
# already then make it. this will also create the directory the
|
||||
# globals and password file goes into
|
||||
cli_etc_dir = os.path.join(kolla_ansible_etc_target,
|
||||
kolla_cli, ansible)
|
||||
if not os.path.exists(cli_etc_dir):
|
||||
make_cli_etc_dir_cmd = ('mkdir -p %s' % cli_etc_dir)
|
||||
_command_exec(make_cli_etc_dir_cmd)
|
||||
|
||||
# Create the inventory file (if it doesn't already exist).
|
||||
# (The script will exit here if the user doesn't have sufficient privs.)
|
||||
inventory_file_path = os.path.join(cli_etc_dir, 'inventory.json')
|
||||
if not os.path.exists(inventory_file_path):
|
||||
touch_inventory_file_cmd = ('touch %s' % inventory_file_path)
|
||||
_command_exec(touch_inventory_file_cmd)
|
||||
|
||||
# copy over all kolla ansible etc files
|
||||
kolla_ansible_etc_source = os.path.join(kolla_ansible_source_base,
|
||||
'etc', kolla)
|
||||
for etc_file in os.listdir(kolla_ansible_etc_source):
|
||||
if not os.path.exists(os.path.join(kolla_ansible_etc_target,
|
||||
etc_file)):
|
||||
copy_kolla_etc_files_cmd = (
|
||||
'cp -a {source_dir}/{filename} {target_dir}'.format(
|
||||
source_dir=kolla_ansible_etc_source,
|
||||
filename=etc_file,
|
||||
target_dir=kolla_ansible_etc_target))
|
||||
_command_exec(copy_kolla_etc_files_cmd)
|
||||
|
||||
# add ssh keys for cli
|
||||
key_path = os.path.join(os.getenv('HOME'), '.ssh', 'id_rsa')
|
||||
if not os.path.exists(key_path):
|
||||
# generate new ssh keys
|
||||
keygen_cmd = 'ssh-keygen -t rsa -N \'\' -f %s' % key_path
|
||||
_command_exec(keygen_cmd)
|
||||
# copy the public key to where kolla-cli expects it
|
||||
pub_key_path = os.path.join(os.getenv('HOME'), '.ssh', 'id_rsa.pub')
|
||||
cli_etc_path = os.path.join(kolla_ansible_etc_target, kolla_cli)
|
||||
copy_cmd = 'cp -p %s %s/' % (pub_key_path, cli_etc_path)
|
||||
_command_exec(copy_cmd)
|
||||
|
||||
|
||||
def setup_ansible_home():
|
||||
# make cli home ansible directory
|
||||
cli_ansible_dir = os.path.join(kolla_ansible_home_target,
|
||||
kolla_cli, ansible)
|
||||
if not os.path.exists(cli_ansible_dir):
|
||||
make_cli_ansible_dir_cmd = ('mkdir -p %s' % cli_ansible_dir)
|
||||
_command_exec(make_cli_ansible_dir_cmd)
|
||||
|
||||
# make cli home tools directory
|
||||
cli_tools_dir = os.path.join(kolla_ansible_home_target,
|
||||
kolla_cli, tools)
|
||||
if not os.path.exists(cli_tools_dir):
|
||||
make_cli_tools_dir_cmd = ('mkdir -p %s' % cli_tools_dir)
|
||||
_command_exec(make_cli_tools_dir_cmd)
|
||||
|
||||
# move cli tools files to tools directory
|
||||
copy_cli_tools_files_cmd = ('cp -a %s %s' % ('./tools/*', cli_tools_dir))
|
||||
_command_exec(copy_cli_tools_files_cmd)
|
||||
|
||||
# create cli ansible lock file
|
||||
lock_file_path = os.path.join(kolla_ansible_home_target,
|
||||
kolla_cli, 'ansible.lock')
|
||||
if not os.path.exists(lock_file_path):
|
||||
touch_ansible_lock_file_cmd = ('touch %s' % lock_file_path)
|
||||
_command_exec(touch_ansible_lock_file_cmd)
|
||||
|
||||
# copy over all kolla ansible home files
|
||||
kolla_ansible_home_source = os.path.join(kolla_ansible_source_base,
|
||||
ansible)
|
||||
copy_kolla_home_files_cmd = ('cp -a %s %s' % (kolla_ansible_home_source,
|
||||
kolla_ansible_home_target))
|
||||
_command_exec(copy_kolla_home_files_cmd)
|
||||
|
||||
# create the host_vars directory if it doesn't exist already
|
||||
host_vars_path = os.path.join(kolla_ansible_home_target,
|
||||
ansible, 'host_vars')
|
||||
if not os.path.exists(host_vars_path):
|
||||
make_kolla_host_vars_dir_cmd = ('mkdir %s' % host_vars_path)
|
||||
_command_exec(make_kolla_host_vars_dir_cmd)
|
||||
|
||||
# make link from etc globals to home globals
|
||||
target_etc_path = os.path.join(kolla_ansible_etc_target, 'globals.yml')
|
||||
target_home_link = os.path.join(kolla_ansible_home_target,
|
||||
ansible, 'group_vars', '__GLOBAL__')
|
||||
if not os.path.exists(target_home_link):
|
||||
link_globals_file_cmd = ('ln -s %s %s' % (target_etc_path,
|
||||
target_home_link))
|
||||
_command_exec(link_globals_file_cmd)
|
||||
|
||||
|
||||
def _command_exec(command):
|
||||
print('running - %s' % command)
|
||||
error, _ = utils.run_cmd(command)
|
||||
if error:
|
||||
print('error - %s' % error)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
"""make sure kolla-ansible and cli files are in the right places"""
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
global kolla_ansible_source_base
|
||||
kolla_ansible_source_base = sys.argv[1]
|
||||
|
||||
setup_ansible_etc()
|
||||
setup_ansible_home()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
177
doc/Makefile
177
doc/Makefile
|
@ -1,177 +0,0 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/kollacli.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/kollacli.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/kollacli"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/kollacli"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
|
@ -1,8 +0,0 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
sphinx>=1.8.0,!=2.1.0 # BSD
|
||||
sphinxcontrib-pecanwsme>=0.8
|
|
@ -1,264 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# kolla-cli documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Mar 17 18:17:04 2016.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'openstackdocstheme',
|
||||
'sphinx.ext.autodoc',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'kolla-cli'
|
||||
copyright = u'2016, Oracle'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
|
||||
latex_use_xindy = False
|
||||
|
||||
latex_domain_indices = False
|
||||
|
||||
latex_elements = {
|
||||
'makeindex': '',
|
||||
'printindex': '',
|
||||
'preamble': r'\setcounter{tocdepth}{3}',
|
||||
'maxlistdepth': 10,
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
# NOTE(jacky06): Specify toctree_only=True for a better document structure of
|
||||
# the generated PDF file.
|
||||
latex_documents = [
|
||||
('index', 'doc-kolla-cli.tex', u'kolla-cli Documentation',
|
||||
u'Oracle', 'manual', True),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'kolla-cli', u'kolla-cli Documentation',
|
||||
[u'Oracle'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'kolla-cli', u'kolla-cli Documentation',
|
||||
u'Oracle', 'kolla-cli', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
|
@ -1,25 +0,0 @@
|
|||
.. kollacli documentation master file, created by
|
||||
sphinx-quickstart on Thu Mar 17 18:17:04 2016.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to kolla-cli's documentation!
|
||||
=====================================
|
||||
|
||||
**This project is deprecated since the Ussuri release and will not be
|
||||
maintained in the future.**
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
modules
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
api Package
|
||||
===========
|
||||
|
||||
:mod:`certificate` Module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.certificate
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`client` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.client
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`config` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`control_plane` Module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.control_plane
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`exceptions` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.exceptions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`group` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.group
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`host` Module
|
||||
------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.host
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`job` Module
|
||||
-----------------
|
||||
|
||||
.. automodule:: kolla_cli.api.job
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`password` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.password
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`properties` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.properties
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`service` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.service
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`support` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.api.support
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
commands Package
|
||||
================
|
||||
|
||||
:mod:`config` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`exceptions` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.exceptions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`group` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.group
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`host` Module
|
||||
------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.host
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`kolla_action` Module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.kolla_action
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`mode` Module
|
||||
------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.mode
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`password` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.password
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`property` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.property
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`service` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.service
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`support` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.commands.support
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
ansible Package
|
||||
===============
|
||||
|
||||
:mod:`actions` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.ansible.actions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`job` Module
|
||||
-----------------
|
||||
|
||||
.. automodule:: kolla_cli.common.ansible.job
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`playbook` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.ansible.playbook
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`utils` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.ansible.utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
common Package
|
||||
==============
|
||||
|
||||
:mod:`ansible_inventory` Module
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.ansible_inventory
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`host` Module
|
||||
------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.host
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`host_group` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.host_group
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`inventory` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.inventory
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`passwords` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.passwords
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`properties` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.properties
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`service` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.service
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`sshutils` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.sshutils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`subservice` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.subservice
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`support` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.support
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`utils` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: kolla_cli.common.utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
kolla_cli.common.ansible
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
kolla_cli Package
|
||||
=================
|
||||
|
||||
:mod:`i18n` Module
|
||||
------------------
|
||||
|
||||
.. automodule:: kolla_cli.i18n
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`shell` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: kolla_cli.shell
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
kolla_cli.api
|
||||
kolla_cli.commands
|
||||
kolla_cli.common
|
||||
kolla_cli.tests
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
functional Package
|
||||
==================
|
||||
|
||||
:mod:`common` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.common
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_client_upgrade` Module
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_client_upgrade
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_config` Module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_deploy` Module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_deploy
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_destroy` Module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_destroy
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_group` Module
|
||||
------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_group
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_host` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_host
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_password` Module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_password
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_property` Module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_property
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_reconfigure` Module
|
||||
------------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_reconfigure
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_service` Module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_service
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_stop` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_stop
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_support` Module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.functional.test_support
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
tests Package
|
||||
=============
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
kolla_cli.tests.functional
|
||||
kolla_cli.tests.unit
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
unit Package
|
||||
============
|
||||
|
||||
:mod:`common` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.unit.common
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_deploy_cmd` Module
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.unit.test_deploy_cmd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_group_cmd` Module
|
||||
----------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.unit.test_group_cmd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_host_cmd` Module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.unit.test_host_cmd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_service_cmd` Module
|
||||
------------------------------
|
||||
|
||||
.. automodule:: kolla_cli.tests.unit.test_service_cmd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
kolla_cli
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
kolla_cli
|
|
@ -1,79 +0,0 @@
|
|||
# Translations template for kollacli.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the kollacli project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: kollacli 0.1.0.dev22\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2015-08-06 15:09-0700\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
|
||||
#: kollacli/common.py:30
|
||||
msgid "deploy"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:40
|
||||
msgid "install"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:50
|
||||
msgid "list"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:62
|
||||
msgid "start"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:80
|
||||
msgid "stop"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:89
|
||||
msgid "sync"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:98
|
||||
msgid "upgrade"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/common.py:107
|
||||
msgid "dump"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/host.py:156
|
||||
msgid "host addservice"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/host.py:166
|
||||
msgid "host removeservice"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/property.py:29
|
||||
msgid "property set"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/service.py:27
|
||||
msgid "service activate"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/service.py:37
|
||||
msgid "service deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/service.py:47
|
||||
msgid "service autodeploy"
|
||||
msgstr ""
|
||||
|
||||
#: kollacli/service.py:57
|
||||
msgid "service list"
|
||||
msgstr ""
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# Copyright(c) 2018, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.api.job import Job
|
||||
from kolla_cli.common.ansible.actions import KollaAction
|
||||
from kolla_cli.common.utils import check_arg
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
class CertificateApi(object):
|
||||
|
||||
@staticmethod
|
||||
def certificate_init(verbose_level=1):
|
||||
"""Certificate Init.
|
||||
|
||||
Creates a self-signed certificate for secure TLS communication.
|
||||
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='certificates.yml')
|
||||
ansible_job = action.certificate_init()
|
||||
return Job(ansible_job)
|
|
@ -1,77 +0,0 @@
|
|||
# Copyright(c) 2017, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
from kolla_cli.api.certificate import CertificateApi
|
||||
from kolla_cli.api.config import ConfigApi
|
||||
from kolla_cli.api.control_plane import ControlPlaneApi
|
||||
from kolla_cli.api.group import GroupApi
|
||||
from kolla_cli.api.host import HostApi
|
||||
from kolla_cli.api.password import PasswordApi
|
||||
from kolla_cli.api.properties import PropertyApi
|
||||
from kolla_cli.api.service import ServiceApi
|
||||
from kolla_cli.api.support import SupportApi
|
||||
|
||||
CONSOLE_MESSAGE_FORMAT = '%(message)s'
|
||||
|
||||
# TODO(bmace) - API version should probably be stored somewhere else
|
||||
VERSION = '0.1'
|
||||
|
||||
|
||||
class ClientApi(
|
||||
CertificateApi,
|
||||
ConfigApi,
|
||||
ControlPlaneApi,
|
||||
GroupApi,
|
||||
HostApi,
|
||||
PasswordApi,
|
||||
PropertyApi,
|
||||
ServiceApi,
|
||||
SupportApi,
|
||||
):
|
||||
"""Client API Notes
|
||||
|
||||
Objects returned by the API contain a local copy of the information
|
||||
in the datastore. While changes made to the local copy will be
|
||||
reflected in the local object, changes made to the datastore
|
||||
from other objects will not be reflected in this local copy. The
|
||||
object will need to be re-fetched from the datastore to reflect
|
||||
the updates.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_version():
|
||||
# type: () -> str
|
||||
return VERSION
|
||||
|
||||
@staticmethod
|
||||
def enable_console_logging(level, enable=True):
|
||||
# type: (int, bool) -> None
|
||||
"""enable/disable console logging for the api
|
||||
|
||||
enable: True/False
|
||||
level: logging.INFO, logging.DEBUG, logging.WARNING,
|
||||
logging.CRITICAL...
|
||||
"""
|
||||
root_logger = logging.getLogger('')
|
||||
console = logging.StreamHandler(sys.stderr)
|
||||
if enable:
|
||||
console.setLevel(level)
|
||||
formatter = logging.Formatter(CONSOLE_MESSAGE_FORMAT)
|
||||
console.setFormatter(formatter)
|
||||
root_logger.addHandler(console)
|
||||
else:
|
||||
root_logger.removeHandler(console)
|
|
@ -1,58 +0,0 @@
|
|||
# Copyright(c) 2018, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
|
||||
from kolla_cli.api.exceptions import FailedOperation
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common import utils
|
||||
from kolla_cli.common.utils import check_arg
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
class ConfigApi(object):
|
||||
|
||||
@staticmethod
|
||||
def config_reset():
|
||||
"""Config Reset.
|
||||
|
||||
Resets the kolla-ansible configuration to its release defaults.
|
||||
"""
|
||||
actions_path = utils.get_kolla_actions_path()
|
||||
cmd = ('%s config_reset' % actions_path)
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation(
|
||||
u._('Configuration reset failed. {error} {message}')
|
||||
.format(error=err_msg, message=output))
|
||||
|
||||
def config_import_inventory(self, file_path):
|
||||
# type: (str) -> None
|
||||
"""Config Import Inventory
|
||||
|
||||
Import groups and child associations from the provided
|
||||
inventory file. This currently does not import hosts, group
|
||||
vars, or host vars that may also exist in the inventory file.
|
||||
|
||||
:param file_path: path to inventory file to import
|
||||
:type file_path: string
|
||||
"""
|
||||
check_arg(file_path, u._('File path'), str)
|
||||
if not os.path.isfile(file_path):
|
||||
raise InvalidArgument(
|
||||
u._('File {path} is not valid.').format(
|
||||
path=file_path))
|
||||
inventory = Inventory(file_path)
|
||||
Inventory.save(inventory)
|
|
@ -1,345 +0,0 @@
|
|||
# Copyright(c) 2017, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.api.job import Job
|
||||
from kolla_cli.common.ansible.actions import KollaAction
|
||||
from kolla_cli.common.ansible.utils import check_kolla_args
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
class ControlPlaneApi(object):
|
||||
|
||||
@staticmethod
|
||||
def deploy(hostnames=[],
|
||||
serial_flag=False, verbose_level=1, servicenames=[]):
|
||||
# type: (List[str], bool, int, List[str]) -> Job
|
||||
"""Deploy.
|
||||
|
||||
Deploy and start all kolla containers.
|
||||
|
||||
:param hostnames: hosts to deploy to. If empty, then deploy to all.
|
||||
:type hostnames: list of strings
|
||||
:param serial_flag: if true, deploy will be done one host at a time
|
||||
:type serial_flag: boolean
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to deploy. If empty, then deploy all.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(serial_flag, u._('Serial flag'), bool)
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.deploy(hostnames, serial_flag, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def prechecks(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
# type: (int, List[str], List[str]) -> Job
|
||||
"""Check pre-deployment configuration of hosts.
|
||||
|
||||
Check if host is ready for a new deployment. This will fail if
|
||||
any of the hosts are not configured correctly or if they have
|
||||
already been deployed to.
|
||||
:param hostnames: host names
|
||||
:type hostnames: list
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to prechecks.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.precheck(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def pull(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
"""Pull.
|
||||
|
||||
Pull all images for containers (only pulls, no running container).
|
||||
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:param hostnames: hosts to pull to. If empty, then pull to all.
|
||||
:type hostnames: list of strings
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to pull. If empty, then pull all.
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.pull(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def stop(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
# type: (int, List[str], List[str]) -> Job
|
||||
"""Stop Hosts.
|
||||
|
||||
Stops all kolla related docker containers on the specified hosts.
|
||||
|
||||
:param hostnames: host names
|
||||
:type hostnames: list
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to stop. If empty, then stop all.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.stop(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def upgrade(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
# type: (int, List[str], List[str]) -> Job
|
||||
"""Upgrade.
|
||||
|
||||
Upgrades existing OpenStack Environment.
|
||||
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param hostnames: hostnames to upgrade.
|
||||
:type hostnames: list of strings.
|
||||
:param servicenames: services to upgrade. If empty, then upgrade all.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
|
||||
Upgrade containers to new version specified by the property
|
||||
"openstack_release."
|
||||
"""
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.upgrade(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def genconfig(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
# type: (int, List[str], List[str]) -> Job
|
||||
"""Genconfig.
|
||||
|
||||
Generate configuration files for enabled OpenStack services.
|
||||
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to generate. If empty, then generate all.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
|
||||
Upgrade containers to new version specified by the property
|
||||
"openstack_release."
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.genconfig(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def check(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
# type: (int, List[str], List[str]) -> Job
|
||||
"""Do post-deployment smoke tests.
|
||||
|
||||
:param hostnames: host names
|
||||
:type hostnames: list
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to check. If empty, then check all.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.check(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def postdeploy(verbose_level=1):
|
||||
"""Post-Deploy.
|
||||
|
||||
Do post deploy on deploy node.
|
||||
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='post-deploy.yml')
|
||||
ansible_job = action.postdeploy()
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def reconfigure(verbose_level=1, hostnames=[], servicenames=[]):
|
||||
# type: (int, List[str], List[str]) -> Job
|
||||
"""Reconfigure.
|
||||
|
||||
Reconfigure OpenStack service.
|
||||
|
||||
:param hostnames: host names
|
||||
:type hostnames: list
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param servicenames: services to prechecks.
|
||||
:type servicenames: list of strings
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(servicenames, u._('Service names'), list,
|
||||
empty_ok=True, none_ok=True)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
servicenames = safe_decode(servicenames)
|
||||
|
||||
check_kolla_args(hostnames=hostnames,
|
||||
servicenames=servicenames)
|
||||
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='site.yml')
|
||||
ansible_job = action.reconfigure(hostnames, servicenames)
|
||||
return Job(ansible_job)
|
||||
|
||||
@staticmethod
|
||||
def set_deploy_mode(remote_mode):
|
||||
# type: (bool) -> None
|
||||
"""Set deploy mode to either local or remote.
|
||||
|
||||
Local indicates that the openstack deployment will be
|
||||
to the local host. Remote means that the deployment is
|
||||
executed via ssh.
|
||||
|
||||
NOTE: local mode is not supported and should never be used
|
||||
in production environments.
|
||||
|
||||
:param remote_mode: if remote mode is True deployment is done via ssh
|
||||
:type remote_mode: bool
|
||||
"""
|
||||
check_arg(remote_mode, u._('Remote mode'), bool)
|
||||
inventory = Inventory.load()
|
||||
inventory.set_deploy_mode(remote_mode)
|
||||
Inventory.save(inventory)
|
||||
|
||||
@staticmethod
|
||||
def get_deploy_mode():
|
||||
"""Get deploy mode from either local or remote.
|
||||
|
||||
Local indicates that the openstack deployment will be
|
||||
to the local host. Remote means that the deployment is
|
||||
executed via ssh.
|
||||
|
||||
NOTE: local mode is not supported and should never be used
|
||||
in production environments.
|
||||
"""
|
||||
inventory = Inventory.load()
|
||||
remote_mode = inventory.remote_mode
|
||||
deploy_mode = 'remote' if remote_mode else 'local'
|
||||
return deploy_mode
|
|
@ -1,81 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Exception definitions."""
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
"""KollaClient Base Class Exception"""
|
||||
def __init__(self, message, *args):
|
||||
if not message:
|
||||
message = u._('An unknown exception occurred.')
|
||||
super(ClientException, self).__init__(message, *args)
|
||||
|
||||
|
||||
class NotInInventory(ClientException):
|
||||
"""Not in inventory exception"""
|
||||
def __init__(self, obj_type, obj_names, *args):
|
||||
if isinstance(obj_names, list):
|
||||
# list of names
|
||||
invalid_objs = ''
|
||||
comma = ''
|
||||
for obj_name in obj_names:
|
||||
invalid_objs = ''.join([invalid_objs, comma, obj_name])
|
||||
comma = ','
|
||||
else:
|
||||
# single object name
|
||||
invalid_objs = obj_names
|
||||
message = (u._('{type} ({objs}) does not exist.')
|
||||
.format(type=obj_type, objs=invalid_objs))
|
||||
super(NotInInventory, self).__init__(message, *args)
|
||||
|
||||
|
||||
class HostError(ClientException):
|
||||
pass
|
||||
|
||||
|
||||
class HostsSshCheckError(ClientException):
|
||||
"""Host failed its ssh check"""
|
||||
def __init__(self, hostnames, *args):
|
||||
failed_hosts = ''
|
||||
comma = ''
|
||||
for hostname in hostnames:
|
||||
failed_hosts = ''.join([failed_hosts, comma, hostname])
|
||||
comma = ','
|
||||
message = (u._('Host(s) ssh check failed: {hosts}')
|
||||
.format(hosts=failed_hosts))
|
||||
super(HostsSshCheckError, self).__init__(message, *args)
|
||||
|
||||
|
||||
class InvalidArgument(ClientException):
|
||||
"""Invalid argument"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidConfiguration(ClientException):
|
||||
"""Invalid configuration"""
|
||||
pass
|
||||
|
||||
|
||||
class FailedOperation(ClientException):
|
||||
pass
|
||||
|
||||
|
||||
class MissingArgument(ClientException):
|
||||
"""Missing argument"""
|
||||
def __init__(self, argname, *args):
|
||||
message = (u._('Argument is missing: {name}')
|
||||
.format(name=argname))
|
||||
super(MissingArgument, self).__init__(message, *args)
|
|
@ -1,217 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
class GroupApi(object):
|
||||
|
||||
def group_add(self, groupnames):
|
||||
# type: (List[str]) -> None
|
||||
"""Add groups to the inventory
|
||||
|
||||
:param groupnames: names of the groups to add to the inventory
|
||||
:type groupnames: list of strings
|
||||
|
||||
"""
|
||||
check_arg(groupnames, u._('Group names'), list)
|
||||
groupnames = safe_decode(groupnames)
|
||||
|
||||
inventory = Inventory.load()
|
||||
for groupname in groupnames:
|
||||
inventory.add_group(groupname)
|
||||
Inventory.save(inventory)
|
||||
|
||||
def group_remove(self, groupnames):
|
||||
# type: (List[str]) -> None
|
||||
"""Remove groups from the inventory
|
||||
|
||||
:param groupnames: names of the groups to remove from the inventory
|
||||
:type groupnames: list of strings
|
||||
"""
|
||||
check_arg(groupnames, u._('Group names'), list)
|
||||
groupnames = safe_decode(groupnames)
|
||||
|
||||
inventory = Inventory.load()
|
||||
for groupname in groupnames:
|
||||
inventory.remove_group(groupname)
|
||||
Inventory.save(inventory)
|
||||
|
||||
def group_get_all(self):
|
||||
# type: () -> List[Group]
|
||||
"""Get all groups in the inventory
|
||||
|
||||
:return: groups
|
||||
:rtype: list of Group objects
|
||||
"""
|
||||
return self._get_groups([], get_all=True)
|
||||
|
||||
def group_get(self, groupnames):
|
||||
# type: (List[str]) -> List[Group]
|
||||
"""Get selected groups in the inventory
|
||||
|
||||
:param groupnames: names of groups to be read
|
||||
:type groupnames: list of strings
|
||||
:return: groups
|
||||
:rtype: list of Group objects
|
||||
"""
|
||||
check_arg(groupnames, u._('Group names'), list)
|
||||
groupnames = safe_decode(groupnames)
|
||||
return self._get_groups(groupnames)
|
||||
|
||||
def _get_groups(self, groupnames, get_all=False):
|
||||
# type: (List[str], bool) -> List[Group]
|
||||
groups = []
|
||||
inventory = Inventory.load()
|
||||
if get_all:
|
||||
groupnames = inventory.get_groupnames()
|
||||
else:
|
||||
inventory.validate_groupnames(groupnames)
|
||||
|
||||
group_services = inventory.get_group_services()
|
||||
for groupname in groupnames:
|
||||
inv_group = inventory.get_group(groupname)
|
||||
group = Group(groupname,
|
||||
group_services[groupname],
|
||||
inv_group.get_hostnames())
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
|
||||
class Group(object):
|
||||
def __init__(self, groupname, servicenames, hostnames):
|
||||
# type: (str, List[str], List[str]) -> None
|
||||
self.name = groupname
|
||||
self._servicenames = servicenames
|
||||
self._hostnames = hostnames
|
||||
|
||||
def get_name(self):
|
||||
# type: () -> str
|
||||
"""Get name
|
||||
|
||||
:return: group name
|
||||
:rtype: string
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def get_services(self):
|
||||
# type: () -> List[str]
|
||||
"""Get names of services associated with this group.
|
||||
|
||||
:return: service names
|
||||
:rtype: list of strings
|
||||
"""
|
||||
return copy(self._servicenames)
|
||||
|
||||
def add_service(self, servicename):
|
||||
# type: (str) -> None
|
||||
"""Add service to group
|
||||
|
||||
:param servicename: name of the service to add to the group
|
||||
:type servicename: string
|
||||
"""
|
||||
check_arg(servicename, u._('Service name'), str)
|
||||
servicename = safe_decode(servicename)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_servicenames([servicename], client_filter=True)
|
||||
|
||||
group_services = inventory.get_group_services()
|
||||
self._servicenames = group_services[self.name]
|
||||
if servicename not in self._servicenames:
|
||||
# service not associated with group, add it
|
||||
inventory.add_group_to_service(self.name, servicename)
|
||||
self._servicenames.append(servicename)
|
||||
Inventory.save(inventory)
|
||||
|
||||
def remove_service(self, servicename):
|
||||
# type: (str) -> None
|
||||
"""Remove service from group
|
||||
|
||||
:param servicename: name of the service to remove from the group
|
||||
:type servicename: string
|
||||
|
||||
"""
|
||||
check_arg(servicename, u._('Service name'), str)
|
||||
servicename = safe_decode(servicename)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_servicenames([servicename], client_filter=True)
|
||||
|
||||
group_services = inventory.get_group_services()
|
||||
self._servicenames = group_services[self.name]
|
||||
if servicename in self._servicenames:
|
||||
# service is associated with group, remove it
|
||||
inventory.remove_group_from_service(self.name, servicename)
|
||||
self._servicenames.remove(servicename)
|
||||
Inventory.save(inventory)
|
||||
|
||||
def get_hosts(self):
|
||||
# type: () -> List[str]
|
||||
"""Get names of hosts associated with this group.
|
||||
|
||||
:return: host names
|
||||
:rtype: list of strings
|
||||
"""
|
||||
return copy(self._hostnames)
|
||||
|
||||
def add_host(self, hostname):
|
||||
# type: (str) -> None
|
||||
"""Add host to group
|
||||
|
||||
:param hostname: name of the host to add to the group
|
||||
:type hostname: string
|
||||
|
||||
"""
|
||||
check_arg(hostname, u._('Host name'), str)
|
||||
hostname = safe_decode(hostname)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_hostnames([hostname])
|
||||
|
||||
group = inventory.get_group(self.name)
|
||||
self._hostnames = group.get_hostnames()
|
||||
if hostname not in self._hostnames:
|
||||
# host not associated with group, add it
|
||||
inventory.add_host(hostname, self.name)
|
||||
self._hostnames.append(hostname)
|
||||
Inventory.save(inventory)
|
||||
|
||||
def remove_host(self, hostname):
|
||||
# type: (str) -> None
|
||||
"""Remove host from group
|
||||
|
||||
:param hostname: name of the host to remove from the group
|
||||
:type hostname: string
|
||||
|
||||
"""
|
||||
check_arg(hostname, u._('Host name'), str)
|
||||
hostname = safe_decode(hostname)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_hostnames([hostname])
|
||||
|
||||
group = inventory.get_group(self.name)
|
||||
self._hostnames = group.get_hostnames()
|
||||
if hostname in self._hostnames:
|
||||
# host is associated with group, remove it
|
||||
inventory.remove_host(hostname, self.name)
|
||||
self._hostnames.remove(hostname)
|
||||
Inventory.save(inventory)
|
|
@ -1,222 +0,0 @@
|
|||
# Copyright(c) 2017, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.api.job import Job
|
||||
from kolla_cli.common.ansible.actions import KollaAction
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import Dict # noqa
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
class HostApi(object):
|
||||
|
||||
@staticmethod
|
||||
def host_add(hostnames):
|
||||
# type: (List[str]) -> None
|
||||
"""Add hosts to the inventory
|
||||
|
||||
:param hostnames: list of strings
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list)
|
||||
hostnames = safe_decode(hostnames)
|
||||
|
||||
inventory = Inventory.load()
|
||||
any_changed = False
|
||||
for hostname in hostnames:
|
||||
changed = inventory.add_host(hostname)
|
||||
if changed:
|
||||
any_changed = True
|
||||
if any_changed:
|
||||
Inventory.save(inventory)
|
||||
|
||||
@staticmethod
|
||||
def host_remove(hostnames):
|
||||
# type: (List[str]) -> None
|
||||
"""Remove hosts from the inventory
|
||||
|
||||
:param hostnames: list of strings
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list)
|
||||
hostnames = safe_decode(hostnames)
|
||||
|
||||
inventory = Inventory.load()
|
||||
any_changed = False
|
||||
for hostname in hostnames:
|
||||
changed = inventory.remove_host(hostname)
|
||||
if changed:
|
||||
any_changed = True
|
||||
if any_changed:
|
||||
Inventory.save(inventory)
|
||||
|
||||
@staticmethod
|
||||
def host_get_all():
|
||||
# type: () -> List[Host]
|
||||
"""Get all hosts in the inventory
|
||||
|
||||
:return: Hosts
|
||||
:rtype: list of Host objects
|
||||
"""
|
||||
inventory = Inventory.load()
|
||||
hosts = []
|
||||
host_groups = inventory.get_host_groups()
|
||||
for hostname, groupnames in host_groups.items():
|
||||
hosts.append(Host(hostname, groupnames))
|
||||
return hosts
|
||||
|
||||
@staticmethod
|
||||
def host_get(hostnames):
|
||||
# type: (List[str]) -> List[Host]
|
||||
"""Get selected hosts in the inventory
|
||||
|
||||
:param hostnames: list of strings
|
||||
:return: hosts
|
||||
:rtype: list of Host objects
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list)
|
||||
hostnames = safe_decode(hostnames)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_hostnames(hostnames)
|
||||
|
||||
hosts = []
|
||||
host_groups = inventory.get_host_groups()
|
||||
for hostname in hostnames:
|
||||
hosts.append(Host(hostname, host_groups[hostname]))
|
||||
return hosts
|
||||
|
||||
@staticmethod
|
||||
def host_ssh_check(hostnames):
|
||||
# type: (List[str]) -> Dict[str,Dict[str,object]]
|
||||
"""Check hosts for ssh connectivity
|
||||
|
||||
Check status is a dictionary of form:
|
||||
- {hostname: {
|
||||
'success':<True|False>,
|
||||
'msg':message_string},
|
||||
...
|
||||
}
|
||||
|
||||
:param hostnames: list of strings
|
||||
:return: check status
|
||||
:rtype: dictionary
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list)
|
||||
inventory = Inventory.load()
|
||||
hostnames = safe_decode(hostnames)
|
||||
inventory.validate_hostnames(hostnames)
|
||||
summary = inventory.ssh_check_hosts(hostnames)
|
||||
return summary
|
||||
|
||||
@staticmethod
|
||||
def host_setup(hosts_info):
|
||||
# type: (Dict[str,Dict[str,object]]) -> None
|
||||
"""Setup multiple hosts for ssh access
|
||||
|
||||
hosts_info is a dictionary of form:
|
||||
- {hostname': {
|
||||
'password': password
|
||||
'uname': user_name},
|
||||
...
|
||||
}
|
||||
|
||||
The uname entry is optional.
|
||||
|
||||
:param hosts_info: dictionary
|
||||
"""
|
||||
check_arg(hosts_info, u._('Hosts info'), dict)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_hostnames(hosts_info.keys())
|
||||
inventory.setup_hosts(hosts_info)
|
||||
|
||||
@staticmethod
|
||||
def host_destroy(hostnames, destroy_type, verbose_level=1,
|
||||
include_data=False, remove_images=False):
|
||||
# type: (List[str], str, int, bool, bool) -> Job
|
||||
"""Destroy Hosts.
|
||||
|
||||
Stops and removes all kolla related docker containers on the
|
||||
specified hosts.
|
||||
|
||||
:param hostnames: host names
|
||||
:type hostnames: list
|
||||
:param destroy_type: either 'kill' or 'stop'
|
||||
:type destroy_type: string
|
||||
:param verbose_level: the higher the number, the more verbose
|
||||
:type verbose_level: integer
|
||||
:param include_data: if true, destroy data containers too.
|
||||
:type include_data: boolean
|
||||
:param remove_images: if true, destroy will remove the docker images
|
||||
:type remove_images: boolean
|
||||
:return: Job object
|
||||
:rtype: Job
|
||||
"""
|
||||
check_arg(hostnames, u._('Host names'), list)
|
||||
check_arg(destroy_type, u._('Destroy type'), str)
|
||||
check_arg(verbose_level, u._('Verbose level'), int)
|
||||
check_arg(include_data, u._('Include data'), bool)
|
||||
check_arg(remove_images, u._('Remove images'), bool)
|
||||
if destroy_type not in ['stop', 'kill']:
|
||||
raise InvalidArgument(
|
||||
u._('Invalid destroy type ({type}). Must be either '
|
||||
'"stop" or "kill".').format(type=destroy_type))
|
||||
|
||||
hostnames = safe_decode(hostnames)
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_hostnames(hostnames)
|
||||
|
||||
action = KollaAction(verbose_level=verbose_level,
|
||||
playbook_name='destroy.yml')
|
||||
ansible_job = action.destroy_hosts(hostnames, destroy_type,
|
||||
include_data, remove_images)
|
||||
return Job(ansible_job)
|
||||
|
||||
|
||||
class Host(object):
|
||||
"""Host"""
|
||||
|
||||
def __init__(self, hostname, groupnames=[]):
|
||||
# type: (str, List[str]) -> None
|
||||
self.name = hostname
|
||||
self._groupnames = groupnames
|
||||
|
||||
def get_name(self):
|
||||
# type: () -> str
|
||||
"""Get name
|
||||
|
||||
:return: host name
|
||||
:rtype: string
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def get_groups(self):
|
||||
# type: () -> List[str]
|
||||
"""Get names of the groups associated with this host
|
||||
|
||||
:return: group names
|
||||
:rtype: list of strings
|
||||
|
||||
Note: If the groups associated with this host change after this
|
||||
host is fetched, the host must be re-fetched to reflect those
|
||||
changes.
|
||||
"""
|
||||
return copy(self._groupnames)
|
|
@ -1,62 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class Job(object):
|
||||
"""Job"""
|
||||
def __init__(self, ansible_job):
|
||||
self._ansible_job = ansible_job
|
||||
|
||||
def wait(self):
|
||||
# type: () -> int
|
||||
"""Wait for job to complete
|
||||
|
||||
:return: 0 if job succeeded, 1 if job failed
|
||||
:rtype: int
|
||||
"""
|
||||
return self._ansible_job.wait()
|
||||
|
||||
def get_status(self):
|
||||
# type: () -> int
|
||||
"""Get status of job
|
||||
|
||||
:return: None: job is still running
|
||||
0: job succeeded
|
||||
1: job failed
|
||||
2: job killed by user
|
||||
:rtype: int or None
|
||||
"""
|
||||
return self._ansible_job.get_status()
|
||||
|
||||
def get_error_message(self):
|
||||
# type: () -> str
|
||||
"""Get error message
|
||||
|
||||
:return: if job failed, this will return the error message.
|
||||
:rtype: string
|
||||
"""
|
||||
return self._ansible_job.get_error_message()
|
||||
|
||||
def get_console_output(self):
|
||||
# type: () -> str
|
||||
"""Get the console output from the job
|
||||
|
||||
:return: console output useful for debugging failed jobs.
|
||||
:rtype: string
|
||||
"""
|
||||
return self._ansible_job.get_command_output()
|
||||
|
||||
def kill(self):
|
||||
"""kill the job"""
|
||||
self._ansible_job.kill()
|
|
@ -1,100 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.common.passwords import clear_password
|
||||
from kolla_cli.common.passwords import get_password_names
|
||||
from kolla_cli.common.passwords import init_passwords
|
||||
from kolla_cli.common.passwords import set_password
|
||||
from kolla_cli.common.passwords import set_password_sshkey
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import disallow_chars
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
class PasswordApi(object):
|
||||
|
||||
def password_set(self, name, value):
|
||||
# type: (str, str) -> None
|
||||
"""Set password
|
||||
|
||||
:param name: name of the password
|
||||
:type name: string
|
||||
:param value: value of the password
|
||||
:type value: string
|
||||
"""
|
||||
password_name_string = u._('Password name')
|
||||
password_value_string = u._('Password value')
|
||||
check_arg(name, password_name_string, str)
|
||||
disallow_chars(name, password_name_string, '\'')
|
||||
check_arg(value, password_value_string, str, display_param=False,
|
||||
empty_ok=True, none_ok=True)
|
||||
disallow_chars(value, password_value_string, '\'')
|
||||
set_password(name, value)
|
||||
|
||||
def password_set_sshkey(self, name, private_key, public_key):
|
||||
# type: (str, str, str) -> None
|
||||
"""Set password to an ssh key
|
||||
|
||||
:param name: name of the password
|
||||
:type name: string
|
||||
:param private_key: ssh private key
|
||||
:type value: string
|
||||
:param public_key: ssh public key
|
||||
:type value: string
|
||||
"""
|
||||
password_name_string = u._('Password name')
|
||||
private_key_string = u._('Private key')
|
||||
public_key_string = u._('Public key')
|
||||
check_arg(name, password_name_string, str)
|
||||
disallow_chars(name, password_name_string, '\'')
|
||||
check_arg(private_key, private_key_string, str, display_param=False)
|
||||
disallow_chars(private_key, private_key_string, '\'')
|
||||
check_arg(public_key, public_key_string, str, display_param=False)
|
||||
disallow_chars(public_key, public_key_string, '\'')
|
||||
set_password_sshkey(name, private_key, public_key)
|
||||
|
||||
def password_clear(self, name):
|
||||
# type: (str) -> None
|
||||
"""Clear password
|
||||
|
||||
:param name: name of the password
|
||||
:type name: string
|
||||
"""
|
||||
password_name_string = u._('Password name')
|
||||
check_arg(name, password_name_string, str)
|
||||
disallow_chars(name, password_name_string, '\'')
|
||||
clear_password(name)
|
||||
|
||||
def password_get_names(self):
|
||||
# type: () -> List[str]
|
||||
"""Get password names
|
||||
|
||||
:return: password names
|
||||
:rtype: list of strings
|
||||
"""
|
||||
return get_password_names()
|
||||
|
||||
def password_init(self):
|
||||
# type: () -> None
|
||||
"""Init empty passwords
|
||||
|
||||
Init empty passwords and ssh keys in /etc/kolla/passwords.yml
|
||||
to auto-generated values
|
||||
"""
|
||||
return init_passwords()
|
|
@ -1,198 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 yaml
|
||||
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.common.properties import AnsibleProperties
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import Dict # noqa
|
||||
from typing import List # noqa
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
GLOBAL_TYPE = 'global'
|
||||
GROUP_TYPE = 'group'
|
||||
HOST_TYPE = 'host'
|
||||
PROP_TYPES = [GLOBAL_TYPE, GROUP_TYPE, HOST_TYPE]
|
||||
|
||||
|
||||
class PropertyApi(object):
|
||||
|
||||
def property_set(self, property_dict,
|
||||
property_type=GLOBAL_TYPE, change_set=None):
|
||||
# type: (Dict[str,str], str, List[str]) -> None
|
||||
"""Set a property
|
||||
|
||||
:param property_dict: property dictionary containing key / values
|
||||
:type property_dict: dictionary
|
||||
:param property_type: one of 'global', 'group' or 'host'
|
||||
:type property_type: string
|
||||
:param change_set: for group or host sets this is the list of groups
|
||||
or hosts to set the property for
|
||||
:type change_set: list of strings
|
||||
|
||||
"""
|
||||
ansible_properties = AnsibleProperties()
|
||||
for key, value in property_dict.items():
|
||||
check_arg(key, u._('Property Key'), str)
|
||||
current_property = ansible_properties.get_property(key)
|
||||
if current_property is not None:
|
||||
current_property_type = current_property.value_type
|
||||
if current_property_type is not str:
|
||||
original_value = value
|
||||
value = yaml.safe_load(value)
|
||||
|
||||
# this check is to make sure that we can assign an empty
|
||||
# string to a property. without this safe_load will turn
|
||||
# an empty string into a None which is different than an
|
||||
# empty string.
|
||||
if isinstance(original_value, str) and value is None:
|
||||
value = ''
|
||||
if current_property.value is None:
|
||||
current_property_type = None
|
||||
check_arg(value, u._('Property Value'),
|
||||
current_property_type, empty_ok=True)
|
||||
property_dict[key] = value
|
||||
else:
|
||||
check_arg(value, u._('Property Value'), str, empty_ok=True)
|
||||
if type(value) is str and '"' in value:
|
||||
raise InvalidArgument(u._('Cannot use double quotes in '
|
||||
'a property value.'))
|
||||
|
||||
self._check_type(property_type)
|
||||
if property_type is not GLOBAL_TYPE:
|
||||
check_arg(change_set, u._('Change Set'), list, none_ok=True)
|
||||
change_set = safe_decode(change_set)
|
||||
|
||||
if property_type == GLOBAL_TYPE:
|
||||
ansible_properties.set_property(property_dict)
|
||||
elif property_type == GROUP_TYPE:
|
||||
ansible_properties.set_group_property(property_dict, change_set)
|
||||
else:
|
||||
ansible_properties.set_host_property(property_dict, change_set)
|
||||
|
||||
def property_clear(self, property_list, property_type=GLOBAL_TYPE,
|
||||
change_set=None):
|
||||
# type: (List[str], str, List[str]) -> None
|
||||
"""Clear a property
|
||||
|
||||
:param property_list: property list
|
||||
:type property_list: list
|
||||
:param property_type: one of 'global', 'group' or 'host'
|
||||
:type property_type: string
|
||||
:param change_set: for group or host clears this is the list of
|
||||
groups or hosts to clear the property for
|
||||
:type change_set: list of strings
|
||||
|
||||
"""
|
||||
check_arg(property_list, u._('Property List'), list)
|
||||
property_list = safe_decode(property_list)
|
||||
|
||||
self._check_type(property_type)
|
||||
if property_type is not GLOBAL_TYPE:
|
||||
check_arg(change_set, u._('Change Set'), list, none_ok=True)
|
||||
change_set = safe_decode(change_set)
|
||||
|
||||
ansible_properties = AnsibleProperties()
|
||||
|
||||
if property_type == GLOBAL_TYPE:
|
||||
ansible_properties.clear_property(property_list)
|
||||
elif property_type == GROUP_TYPE:
|
||||
ansible_properties.clear_group_property(property_list, change_set)
|
||||
else:
|
||||
ansible_properties.clear_host_property(property_list, change_set)
|
||||
|
||||
def property_get(self, property_type=GLOBAL_TYPE, get_set=None):
|
||||
# type: (str, List[str]) -> List[Property]
|
||||
"""Returns a list of Property objects
|
||||
|
||||
:param property_type: one of 'global', 'group', or 'host'
|
||||
:type property_type: string
|
||||
:param get_set: optional list of hosts or groups to be used when
|
||||
getting group or host related property lists
|
||||
:type get_set: list of strings
|
||||
:return: properties
|
||||
:rtype: list of Property objects
|
||||
"""
|
||||
self._check_type(property_type)
|
||||
get_set = safe_decode(get_set)
|
||||
|
||||
ansible_properties = AnsibleProperties()
|
||||
|
||||
result_list = []
|
||||
if property_type == GLOBAL_TYPE:
|
||||
property_list = ansible_properties.get_all_unique()
|
||||
elif property_type == GROUP_TYPE:
|
||||
property_list = ansible_properties.get_group_list(get_set)
|
||||
else:
|
||||
property_list = ansible_properties.get_host_list(get_set)
|
||||
|
||||
override_flags = ansible_properties.get_all_override_flags()
|
||||
|
||||
for prop in property_list:
|
||||
result = Property(prop, override_flags.get(prop.name, None))
|
||||
result_list.append(result)
|
||||
|
||||
return result_list
|
||||
|
||||
def _check_type(self, property_type):
|
||||
if property_type is None or property_type not in PROP_TYPES:
|
||||
raise InvalidArgument(u._('Property Type ({value} is not one of '
|
||||
'global, group or host')
|
||||
.format(value=property_type))
|
||||
|
||||
|
||||
class Property(object):
|
||||
"""Property
|
||||
|
||||
Members:
|
||||
- name (str): key
|
||||
- value (Any): value
|
||||
- file_name (str): name of file property is from
|
||||
- overrides (bool): does the property override some other value
|
||||
- orig_value (str): the value which is overridden or None
|
||||
- target (str): group or host name for group or host properties
|
||||
- prop_type (str): one of 'global', 'group' or 'host'
|
||||
- ovr_global (bool): true if property is overridden at global level
|
||||
- ovr_group (bool): true if property is overridden at group level
|
||||
- ovr_host (bool): true if property is overridden at host level
|
||||
- value_type (type): the python type of the value
|
||||
"""
|
||||
|
||||
def __init__(self, ansible_property, override_flags):
|
||||
self.name = ansible_property.name
|
||||
self.value = ansible_property.value
|
||||
self.file_name = ansible_property.file_name
|
||||
self.overrides = ansible_property.overrides
|
||||
self.orig_value = ansible_property.orig_value
|
||||
self.target = ansible_property.target
|
||||
self.prop_type = ansible_property.prop_type
|
||||
self.value_type = ansible_property.value_type
|
||||
|
||||
if override_flags is not None:
|
||||
self.ovr_global = override_flags.ovr_global
|
||||
self.ovr_group = override_flags.ovr_group
|
||||
self.ovr_host = override_flags.ovr_host
|
||||
else:
|
||||
self.ovr_global = False
|
||||
self.ovr_group = False
|
||||
self.ovr_host = False
|
|
@ -1,144 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
class ServiceApi(object):
|
||||
|
||||
def service_get_all(self):
|
||||
# type: () -> List[Service]
|
||||
"""Get all services in the inventory
|
||||
|
||||
:return: services
|
||||
:rtype: List of Service objects
|
||||
"""
|
||||
return self._get_services([], get_all=True)
|
||||
|
||||
def service_get(self, servicenames):
|
||||
# type: (List[str]) -> List[Service]
|
||||
"""Get selected services in the inventory
|
||||
|
||||
:param servicenames: names of services to be read
|
||||
:type servicenames: list of strings
|
||||
:return: services
|
||||
:rtype: list of Service objects
|
||||
"""
|
||||
check_arg(servicenames, u._('Service names'), list)
|
||||
servicenames = safe_decode(servicenames)
|
||||
return self._get_services(servicenames)
|
||||
|
||||
def _get_services(self, servicenames, get_all=False):
|
||||
# type: (List[str], bool) -> List[Service]
|
||||
services = []
|
||||
inventory = Inventory.load()
|
||||
|
||||
if get_all:
|
||||
inv_services = inventory.get_services(client_filter=True)
|
||||
for inv_service in inv_services:
|
||||
service = Service(inv_service.name,
|
||||
inv_service.get_parentnames(),
|
||||
inv_service.get_childnames(),
|
||||
inv_service.get_groupnames())
|
||||
services.append(service)
|
||||
else:
|
||||
inventory.validate_servicenames(servicenames, client_filter=True)
|
||||
|
||||
for servicename in servicenames:
|
||||
inv_service = inventory.get_service(servicename,
|
||||
client_filter=True)
|
||||
if inv_service:
|
||||
service = Service(inv_service.name,
|
||||
inv_service.get_parentnames(),
|
||||
inv_service.get_childnames(),
|
||||
inv_service.get_groupnames())
|
||||
services.append(service)
|
||||
return services
|
||||
|
||||
|
||||
class Service(object):
|
||||
"""Service
|
||||
|
||||
A service is one of the services available in openstack-kolla-ansible.
|
||||
|
||||
For example, this would be how the murano services would be
|
||||
represented:
|
||||
|
||||
- murano
|
||||
- parentnames: []
|
||||
- childnames: [murano-api, murano-engine]
|
||||
- murano-api
|
||||
- parentnames: [murano]
|
||||
- childnames: []
|
||||
- murano-engine
|
||||
- parentnames: [murano]
|
||||
- childnames: []
|
||||
"""
|
||||
|
||||
def __init__(self, servicename, parentnames=[],
|
||||
childnames=[], groupnames=[]):
|
||||
# type: (str, List[str], List[str], List[str]) -> None
|
||||
self.name = servicename
|
||||
self._parentnames = parentnames
|
||||
self._childnames = childnames
|
||||
self._groupnames = groupnames
|
||||
|
||||
def get_name(self):
|
||||
# type: () -> str
|
||||
"""Get name
|
||||
|
||||
:return: service name
|
||||
:rtype: string
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def get_parents(self):
|
||||
# type: () -> List[str]
|
||||
"""Get name of parent services
|
||||
|
||||
:return: parent service names
|
||||
:rtype: string
|
||||
"""
|
||||
return copy(self._parentnames)
|
||||
|
||||
def get_children(self):
|
||||
# type: () -> List[str]
|
||||
"""Get names of the child services
|
||||
|
||||
:return: child names
|
||||
:rtype: list of strings
|
||||
"""
|
||||
return copy(self._childnames)
|
||||
|
||||
def get_groups(self):
|
||||
# type: () -> List[str]
|
||||
"""Get names of the groups
|
||||
|
||||
:return: group names
|
||||
:rtype: list of strings
|
||||
|
||||
Note: If the groups associated with this service change after this
|
||||
service is fetched, the service must be re-fetched to reflect those
|
||||
changes.
|
||||
"""
|
||||
return copy(self._groupnames)
|
|
@ -1,79 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.common.support import dump
|
||||
from kolla_cli.common.support import get_logs
|
||||
from kolla_cli.common.utils import check_arg
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
class SupportApi(object):
|
||||
|
||||
def support_dump(self, dirpath):
|
||||
# type: (str) -> str
|
||||
"""Dumps configuration data for debugging.
|
||||
|
||||
Dumps most files in /etc/kolla and /usr/share/kolla into a
|
||||
tar file so be given to support / development to help with
|
||||
debugging problems.
|
||||
|
||||
:param dirpath: path to directory where dump will be placed
|
||||
:type dirpath: string
|
||||
:return: path to dump file
|
||||
:rtype: string
|
||||
"""
|
||||
check_arg(dirpath, u._('Directory path'), str)
|
||||
dirpath = safe_decode(dirpath)
|
||||
if not os.path.exists(dirpath):
|
||||
raise InvalidArgument(u._('Directory path: {path} does not exist')
|
||||
.format(path=dirpath))
|
||||
dumpfile_path = dump(dirpath)
|
||||
return dumpfile_path
|
||||
|
||||
def support_get_logs(self, servicenames, hostname, dirpath):
|
||||
# type: (List[str], str, str) -> None
|
||||
"""get container logs
|
||||
|
||||
Fetch the container log files of services from the specified hosts.
|
||||
The log files will be placed in the named directory. All the containers
|
||||
for the host will be placed in a directory named hostname. The file
|
||||
names for each log will be servicename_id.log.
|
||||
|
||||
:param servicenames: names of services (ie nova, glance, etc)
|
||||
:type servicenames: list of strings
|
||||
:param hostname: name of host to look for logs on
|
||||
:type hostname: string
|
||||
:param dirpath: path of directory where log files will be written
|
||||
:type dirpath: string
|
||||
"""
|
||||
check_arg(dirpath, u._('Directory path'), str)
|
||||
dirpath = safe_decode(dirpath)
|
||||
if not os.path.exists(dirpath):
|
||||
raise InvalidArgument(u._('Directory path: {path} does not exist')
|
||||
.format(path=dirpath))
|
||||
|
||||
check_arg(servicenames, u._('Service names'), list)
|
||||
servicenames = safe_decode(servicenames)
|
||||
check_arg(hostname, u._('Host names'), str)
|
||||
hostname = safe_decode(hostname)
|
||||
|
||||
get_logs(servicenames, hostname, dirpath)
|
|
@ -1,63 +0,0 @@
|
|||
# Copyright(c) 2018, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 traceback
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
CLIENT = ClientApi()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigReset(Command):
|
||||
"""Resets the kolla-ansible configuration to its release defaults."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
CLIENT.config_reset()
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class ConfigImport(Command):
|
||||
"""Config Import
|
||||
|
||||
"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ConfigImport, self).get_parser(prog_name)
|
||||
parser.add_argument('import_type', metavar='<import_type>',
|
||||
help=u._('Import type=<inventory>'))
|
||||
parser.add_argument('file_path', metavar='<file_path>',
|
||||
help=u._('File path'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
legal_types = ['inventory']
|
||||
import_type = parsed_args.import_type
|
||||
if not import_type or import_type not in legal_types:
|
||||
raise CommandError(u._(
|
||||
'Import type must be {type}.').format(type=legal_types))
|
||||
|
||||
file_path = None
|
||||
if parsed_args.file_path:
|
||||
file_path = parsed_args.file_path.strip()
|
||||
CLIENT.config_import_inventory(file_path)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
|
@ -1,25 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Exception definitions."""
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
|
||||
class CommandError(Exception):
|
||||
"""CLI command error"""
|
||||
def __init__(self, message, *args):
|
||||
prefix = u._('ERROR: ')
|
||||
if not message.startswith(prefix):
|
||||
message = prefix + message
|
||||
super(CommandError, self).__init__(message, *args)
|
|
@ -1,173 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 traceback
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.api.exceptions import ClientException
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
from kolla_cli.common.utils import convert_lists_to_string
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class GroupAdd(Command):
|
||||
"""Add group to openstack-kolla."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupAdd, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
|
||||
CLIENT.group_add([groupname])
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class GroupRemove(Command):
|
||||
"""Remove group from openstack-kolla."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupRemove, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
|
||||
CLIENT.group_remove([groupname])
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class GroupAddhost(Command):
|
||||
"""Add host to group."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupAddhost, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help=u._('Group name'))
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
hostname = parsed_args.hostname.strip()
|
||||
|
||||
group = CLIENT.group_get([groupname])[0]
|
||||
group.add_host(hostname)
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class GroupRemovehost(Command):
|
||||
"""Remove host group from group."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupRemovehost, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help=u._('Group name'))
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
hostname = parsed_args.hostname.strip()
|
||||
|
||||
group = CLIENT.group_get([groupname])[0]
|
||||
group.remove_host(hostname)
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class GroupList(Lister):
|
||||
"""Only list all groups """
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
data = [('',)]
|
||||
groups = CLIENT.group_get_all()
|
||||
if groups:
|
||||
data = []
|
||||
for group in groups:
|
||||
data.append((group.get_name(),))
|
||||
data = convert_lists_to_string(data, parsed_args)
|
||||
return ((u._('Group'), ), sorted(data))
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class GroupListhosts(Lister):
|
||||
"""List all groups and their hosts."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
data = [('', '')]
|
||||
groups = CLIENT.group_get_all()
|
||||
if groups:
|
||||
data = []
|
||||
for group in groups:
|
||||
data.append((group.get_name(),
|
||||
sorted(group.get_hosts())))
|
||||
data = convert_lists_to_string(data, parsed_args)
|
||||
return ((u._('Group'), u._('Hosts')), sorted(data))
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class GroupListservices(Lister):
|
||||
"""List all groups and their services."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
data = [('', '')]
|
||||
groups = CLIENT.group_get_all()
|
||||
if groups:
|
||||
data = []
|
||||
for group in groups:
|
||||
data.append((group.get_name(),
|
||||
sorted(group.get_services())))
|
||||
data = convert_lists_to_string(data, parsed_args)
|
||||
return ((u._('Group'), u._('Services')), sorted(data))
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
|
@ -1,302 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 getpass
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
from six.moves import input
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.api.exceptions import ClientException
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
from kolla_cli.common.utils import convert_lists_to_string
|
||||
from kolla_cli.common.utils import get_setup_user
|
||||
from kolla_cli.common.utils import handers_action_result
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class HostAdd(Command):
|
||||
"""Add host to openstack-kolla."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostAdd, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help=u._('Host name or ip address'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
CLIENT.host_add([hostname])
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class HostDestroy(Command):
|
||||
"""Destroy all kolla containers on host(s).
|
||||
|
||||
Stops and removes all kolla related docker containers on either the
|
||||
specified host or all hosts if the hostname all is used.
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostDestroy, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname | all>',
|
||||
help=u._('Host name or ip address or "all"'))
|
||||
parser.add_argument('--stop', action='store_true',
|
||||
help=u._('Stop rather than kill'))
|
||||
parser.add_argument('--includedata', action='store_true',
|
||||
help=u._('Destroy data containers'))
|
||||
parser.add_argument('--removeimages', action='store_true',
|
||||
help=u._('Remove docker images'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
|
||||
hostnames = [hostname]
|
||||
if hostname == 'all':
|
||||
hostnames = _get_all_hostnames()
|
||||
# if there are no hosts, don't bother doing anything
|
||||
if not hostnames:
|
||||
return
|
||||
|
||||
destroy_type = 'kill'
|
||||
if parsed_args.stop:
|
||||
destroy_type = 'stop'
|
||||
include_data = False
|
||||
if parsed_args.includedata:
|
||||
include_data = True
|
||||
remove_images = False
|
||||
if parsed_args.removeimages:
|
||||
remove_images = True
|
||||
|
||||
if include_data and not self._is_ok_to_delete_data():
|
||||
LOG.info('Aborting destroy')
|
||||
return
|
||||
|
||||
verbose_level = self.app.options.verbose_level
|
||||
|
||||
job = CLIENT.host_destroy(hostnames, destroy_type,
|
||||
verbose_level, include_data,
|
||||
remove_images)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
def _is_ok_to_delete_data(self):
|
||||
question = ('This will delete all containers and data'
|
||||
', are you sure? (y/n)')
|
||||
answer = input(question)
|
||||
while answer != 'y' and answer != 'n':
|
||||
answer = input(question)
|
||||
return True if answer == 'y' else False
|
||||
|
||||
|
||||
class HostRemove(Command):
|
||||
"""Remove host from openstack-kolla."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostRemove, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname | all>',
|
||||
help=u._('Host name or "all"'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostnames = [hostname]
|
||||
if hostname == 'all':
|
||||
hostnames = _get_all_hostnames()
|
||||
if len(hostnames) > 0:
|
||||
CLIENT.host_remove(hostnames)
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class HostList(Lister):
|
||||
"""List hosts and their groups.
|
||||
|
||||
If a hostname is provided, only list information about that host.
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostList, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', nargs='?', metavar='[hostname]',
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = None
|
||||
if parsed_args.hostname:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
|
||||
hosts = []
|
||||
if hostname:
|
||||
hosts = CLIENT.host_get([hostname])
|
||||
else:
|
||||
hosts = CLIENT.host_get_all()
|
||||
|
||||
data = []
|
||||
if hosts:
|
||||
for host in hosts:
|
||||
data.append((host.name, host.get_groups()))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
|
||||
data = convert_lists_to_string(data, parsed_args)
|
||||
return ((u._('Host'), u._('Groups')), sorted(data))
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class HostCheck(Command):
|
||||
"""Check an ssh check of host(s)."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostCheck, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname | all>',
|
||||
help=u._('Host name or "all"'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
if hostname == 'all':
|
||||
hostnames = _get_all_hostnames()
|
||||
else:
|
||||
hostnames = [hostname]
|
||||
|
||||
# just do an ssh check
|
||||
summary = CLIENT.host_ssh_check(hostnames)
|
||||
all_ok = True
|
||||
for hostname, info in summary.items():
|
||||
status = u._('success')
|
||||
msg = ''
|
||||
if not info['success']:
|
||||
status = u._('failed-')
|
||||
msg = info['msg']
|
||||
all_ok = False
|
||||
LOG.info(u._('Host {host}: {sts} {msg}')
|
||||
.format(host=hostname, sts=status, msg=msg))
|
||||
|
||||
if not all_ok:
|
||||
raise CommandError(u._('Host check failed.'))
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class HostSetup(Command):
|
||||
"""Setup kolla-cli on host."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostSetup, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', nargs='?',
|
||||
metavar='<hostname>', help=u._('Host name'))
|
||||
parser.add_argument('--insecure', nargs='?', help=argparse.SUPPRESS)
|
||||
parser.add_argument('--file', '-f', nargs='?',
|
||||
metavar='<hosts_info_file>',
|
||||
help=u._('Absolute path to hosts info file '))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
if not parsed_args.hostname and not parsed_args.file:
|
||||
raise CommandError(
|
||||
u._('Host name or hosts info file path is required.'))
|
||||
if parsed_args.hostname and parsed_args.file:
|
||||
raise CommandError(
|
||||
u._('Host name and hosts info file path '
|
||||
'cannot both be present.'))
|
||||
|
||||
if parsed_args.file:
|
||||
# multi-host setup via xml file
|
||||
# The xml file's content is like a dict of format:
|
||||
# hostname1:
|
||||
# uname: user1
|
||||
# password: <password1>
|
||||
# hostname2:
|
||||
# uname: user2
|
||||
# password: <password2>
|
||||
hosts_data = self._get_yml_data(parsed_args.file.strip())
|
||||
CLIENT.host_setup(hosts_data)
|
||||
else:
|
||||
# single host setup
|
||||
hostname = parsed_args.hostname.strip()
|
||||
summary = CLIENT.host_ssh_check([hostname])
|
||||
if summary[hostname]['success']:
|
||||
LOG.info(
|
||||
u._LI('Skipping setup of host ({host}) as '
|
||||
'ssh check is ok.').format(host=hostname))
|
||||
return 0
|
||||
|
||||
if parsed_args.insecure:
|
||||
password = parsed_args.insecure.strip()
|
||||
else:
|
||||
password = getpass.getpass(
|
||||
u._('{name} password for {host}: ')
|
||||
.format(name=get_setup_user(), host=hostname))
|
||||
CLIENT.host_setup({hostname: {'password': password}})
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
def _get_yml_data(self, yml_path):
|
||||
if not os.path.isfile(yml_path):
|
||||
raise CommandError(
|
||||
u._('No file exists at {path}. An absolute file path is '
|
||||
'required.').format(path=yml_path))
|
||||
|
||||
with open(yml_path, 'r') as hosts_file:
|
||||
file_data = hosts_file.read()
|
||||
|
||||
hosts_info = yaml.safe_load(file_data)
|
||||
if not hosts_info:
|
||||
raise CommandError(u._('{path} is empty.').format(path=yml_path))
|
||||
return hosts_info
|
||||
|
||||
|
||||
def _get_all_hostnames():
|
||||
hostnames = []
|
||||
hosts = CLIENT.host_get_all()
|
||||
for host in hosts:
|
||||
hostnames.append(host.name)
|
||||
return hostnames
|
|
@ -1,342 +0,0 @@
|
|||
# Copyright(c) 2017, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 time
|
||||
import traceback
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
from kolla_cli.common.utils import handers_action_result
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
CLIENT = ClientApi()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Deploy(Command):
|
||||
"""Deploy and start all kolla containers."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Deploy, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Deployment host list'))
|
||||
parser.add_argument('--serial', action='store_true',
|
||||
help=u._('Deploy serially'))
|
||||
parser.add_argument('--timeout', nargs=1,
|
||||
metavar='<timeout>',
|
||||
help=u._('timeout (in minutes)'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Deploy service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = None
|
||||
serial_flag = False
|
||||
verbose_level = self.app.options.verbose_level
|
||||
timeout_target = 0
|
||||
services = None
|
||||
try:
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.serial:
|
||||
serial_flag = True
|
||||
if parsed_args.timeout:
|
||||
try:
|
||||
timeout = float(parsed_args.timeout[0])
|
||||
except Exception:
|
||||
raise CommandError(u._('Timeout value is not a number.'))
|
||||
timeout_target = time.time() + (60 * timeout)
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
|
||||
# if we are doing a targeted host deploy make sure we are doing it
|
||||
# to only compute nodes
|
||||
if hosts:
|
||||
invalid_host_list = []
|
||||
compute_group = CLIENT.group_get(['compute'])[0]
|
||||
compute_hosts = compute_group.get_hosts()
|
||||
for host in hosts:
|
||||
if host not in compute_hosts:
|
||||
invalid_host_list.append(host)
|
||||
if len(invalid_host_list) > 0:
|
||||
raise CommandError(
|
||||
u._('Invalid hosts for host targeted deploy. '
|
||||
'Hosts must be in the compute group only.'
|
||||
'Invalid hosts: {hosts}')
|
||||
.format(hosts=invalid_host_list))
|
||||
|
||||
job = CLIENT.deploy(hosts, serial_flag, verbose_level, services)
|
||||
|
||||
# wait for job to complete
|
||||
status = None
|
||||
while status is None:
|
||||
if timeout_target and time.time() > timeout_target:
|
||||
job.kill()
|
||||
raise CommandError(u._('Job timed out and was killed.'))
|
||||
time.sleep(1)
|
||||
status = job.get_status()
|
||||
|
||||
# job is done
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Prechecks(Command):
|
||||
"""Do pre-deployment checks for hosts."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Prechecks, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Precheck host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Precheck service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = []
|
||||
services = []
|
||||
try:
|
||||
verbose_level = self.app.options.verbose_level
|
||||
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
job = CLIENT.prechecks(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Pull(Command):
|
||||
"""Pull all images for containers (only pulls, no running container)."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Pull, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Pull host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Pull service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = []
|
||||
services = []
|
||||
try:
|
||||
verbose_level = self.app.options.verbose_level
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
job = CLIENT.pull(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Reconfigure(Command):
|
||||
"""Reconfigure OpenStack service."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Reconfigure, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Reconfigure host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Reconfigure service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = []
|
||||
services = []
|
||||
try:
|
||||
verbose_level = self.app.options.verbose_level
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
job = CLIENT.reconfigure(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Stop(Command):
|
||||
"""Stop all kolla containers on host(s).
|
||||
|
||||
Stops all kolla related docker containers on either the
|
||||
specified host or all hosts if the hostname all is used.
|
||||
"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Stop, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Stop host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Stop service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hosts = []
|
||||
services = []
|
||||
verbose_level = self.app.options.verbose_level
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
job = CLIENT.stop(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Upgrade(Command):
|
||||
"""Upgrades existing OpenStack Environment."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Upgrade, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Upgrade host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Upgrade service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = []
|
||||
services = []
|
||||
try:
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
verbose_level = self.app.options.verbose_level
|
||||
job = CLIENT.upgrade(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Check(Command):
|
||||
"""Do post-deployment smoke tests."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Check, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Check host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Check service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = []
|
||||
services = None
|
||||
try:
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
verbose_level = self.app.options.verbose_level
|
||||
job = CLIENT.check(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Genconfig(Command):
|
||||
"""Generate configuration files for enabled OpenStack services."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Genconfig, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('genarate configs host list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('genarate configs service list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = []
|
||||
services = []
|
||||
try:
|
||||
verbose_level = self.app.options.verbose_level
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = host_list.split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = service_list.split(',')
|
||||
job = CLIENT.genconfig(verbose_level, hosts, services)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PostDeploy(Command):
|
||||
"""Do post deploy on deploy node."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
verbose_level = self.app.options.verbose_level
|
||||
try:
|
||||
job = CLIENT.postdeploy(verbose_level)
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class CertificateInit(Command):
|
||||
"""Generates self-signed certificate"""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
verbose_level = self.app.options.verbose_level
|
||||
try:
|
||||
job = CLIENT.certificate_init(verbose_level)
|
||||
|
||||
# wait for job to complete
|
||||
status = job.wait()
|
||||
handers_action_result(job, status, verbose_level)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
|
@ -1,76 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 traceback
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class Setdeploy(Command):
|
||||
"""Set deploy mode to either local or remote.
|
||||
|
||||
Local indicates that the openstack deployment will be
|
||||
to the local host. Remote means that the deployment is
|
||||
on remote hosts.
|
||||
"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Setdeploy, self).get_parser(prog_name)
|
||||
parser.add_argument('mode', metavar='<mode>',
|
||||
help=u._('mode=<local, remote>'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
mode = parsed_args.mode.strip()
|
||||
remote_flag = True
|
||||
if mode == 'local':
|
||||
remote_flag = False
|
||||
LOG.info(u._('Please note that local mode is not supported '
|
||||
'and should never be used in production '
|
||||
'environments.'))
|
||||
elif mode != 'remote':
|
||||
raise CommandError(
|
||||
u._('Invalid deploy mode. Mode must be '
|
||||
'either "local" or "remote".'))
|
||||
CLIENT.set_deploy_mode(remote_flag)
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Getdeploy(Command):
|
||||
"""get deploy mode from either local or remote.
|
||||
|
||||
Local indicates that the openstack deployment will be
|
||||
to the local host. Remote means that the deployment is
|
||||
on remote hosts.
|
||||
"""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
mode = CLIENT.get_deploy_mode()
|
||||
return mode
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
|
@ -1,142 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 getpass
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class PasswordSet(Command):
|
||||
"Password Set"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PasswordSet, self).get_parser(prog_name)
|
||||
parser.add_argument('passwordname', metavar='<passwordname>',
|
||||
help=u._('Password name'))
|
||||
parser.add_argument('--insecure', nargs='?', default=False,
|
||||
help=argparse.SUPPRESS)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
password_name = parsed_args.passwordname.strip()
|
||||
if parsed_args.insecure is not False:
|
||||
# --insecure flag is present
|
||||
password = '' # nosec
|
||||
if parsed_args.insecure:
|
||||
password = parsed_args.insecure.strip()
|
||||
else:
|
||||
password = getpass.getpass(u._('Password: ')).strip()
|
||||
passtwo = getpass.getpass(u._('Retype Password: ')).strip()
|
||||
|
||||
if password != passtwo:
|
||||
raise CommandError(u._('Passwords do not match'))
|
||||
|
||||
CLIENT.password_set(password_name, password)
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PasswordSetKey(Command):
|
||||
"Password Set SSH Key"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PasswordSetKey, self).get_parser(prog_name)
|
||||
parser.add_argument('passwordname', metavar='<passwordname>',
|
||||
help=u._('Password name'))
|
||||
parser.add_argument('privatekeypath', metavar='<privatekeypath>',
|
||||
help=u._('Path to private key file'))
|
||||
parser.add_argument('publickeypath', metavar='<publickeypath>',
|
||||
help=u._('Path to public key file'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
password_name = parsed_args.passwordname.strip()
|
||||
private_keypath = parsed_args.privatekeypath.strip()
|
||||
private_keypath = os.path.abspath(private_keypath)
|
||||
public_keypath = parsed_args.publickeypath.strip()
|
||||
public_keypath = os.path.abspath(public_keypath)
|
||||
|
||||
if not os.path.isfile(private_keypath):
|
||||
raise(CommandError(u._('Private key file not found: {path}')
|
||||
.format(path=private_keypath)))
|
||||
if not os.path.isfile(public_keypath):
|
||||
raise(CommandError(u._('Public key file not found: {path}')
|
||||
.format(path=public_keypath)))
|
||||
|
||||
with open(private_keypath, 'r') as f:
|
||||
private_key = f.read()
|
||||
with open(public_keypath, 'r') as f:
|
||||
public_key = f.read()
|
||||
CLIENT.password_set_sshkey(password_name, private_key.strip(),
|
||||
public_key.strip())
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PasswordClear(Command):
|
||||
"Password Clear"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PasswordClear, self).get_parser(prog_name)
|
||||
parser.add_argument('passwordname', metavar='<passwordname>',
|
||||
help=u._('Password name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
password_name = parsed_args.passwordname.strip()
|
||||
CLIENT.password_clear(password_name)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PasswordList(Lister):
|
||||
"""List all password names."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
password_names = CLIENT.password_get_names()
|
||||
password_names = sorted(password_names)
|
||||
|
||||
data = []
|
||||
for password_name in password_names:
|
||||
data.append((password_name, '-'))
|
||||
|
||||
return ((u._('Password Name'), u._('Password')), data)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PasswordInit(Command):
|
||||
"""Init all empty passwords and ssh keys."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
CLIENT.password_init()
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
|
@ -1,266 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 traceback
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
from kolla_cli.common import utils
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
def _get_names(args_list):
|
||||
csv_list = args_list[0].strip()
|
||||
names = csv_list.split(',')
|
||||
if 'all' in names:
|
||||
names = None
|
||||
return names
|
||||
|
||||
|
||||
class PropertySet(Command):
|
||||
"Property Set"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PropertySet, self).get_parser(prog_name)
|
||||
parser.add_argument('propertyname', metavar='<propertyname>',
|
||||
help=u._('Property name'))
|
||||
parser.add_argument('propertyvalue', metavar='<propertyvalue',
|
||||
help=u._('Property value'))
|
||||
parser.add_argument('--hosts', nargs=1,
|
||||
metavar='<host_list>',
|
||||
help=u._('Property host list'))
|
||||
parser.add_argument('--groups', nargs=1,
|
||||
metavar='<group_list>',
|
||||
help=u._('Property group list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
property_name = parsed_args.propertyname.strip()
|
||||
property_value = parsed_args.propertyvalue.strip()
|
||||
property_dict = {}
|
||||
property_dict[property_name] = property_value
|
||||
|
||||
if parsed_args.hosts:
|
||||
if parsed_args.groups:
|
||||
raise CommandError(
|
||||
u._('Invalid to use both hosts and groups arguments '
|
||||
'together.'))
|
||||
|
||||
host_names = _get_names(parsed_args.hosts)
|
||||
|
||||
CLIENT.property_set(property_dict,
|
||||
'host', host_names)
|
||||
|
||||
elif parsed_args.groups:
|
||||
group_names = _get_names(parsed_args.groups)
|
||||
|
||||
CLIENT.property_set(property_dict,
|
||||
'group', group_names)
|
||||
else:
|
||||
CLIENT.property_set(property_dict,
|
||||
'global')
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PropertyClear(Command):
|
||||
"Property Clear"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PropertyClear, self).get_parser(prog_name)
|
||||
parser.add_argument('propertyname', metavar='<propertyname>',
|
||||
help=u._('Property name'))
|
||||
parser.add_argument('--hosts', nargs=1,
|
||||
metavar='<host_list>',
|
||||
help=u._('Property host list'))
|
||||
parser.add_argument('--groups', nargs=1,
|
||||
metavar='<group_list>',
|
||||
help=u._('Property group list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
property_name = parsed_args.propertyname.strip()
|
||||
property_list = []
|
||||
property_list.append(property_name)
|
||||
|
||||
if parsed_args.hosts:
|
||||
if parsed_args.groups:
|
||||
raise CommandError(
|
||||
u._('Invalid to use both hosts and groups arguments '
|
||||
'together.'))
|
||||
|
||||
host_names = _get_names(parsed_args.hosts)
|
||||
|
||||
CLIENT.property_clear(property_list, 'host',
|
||||
host_names)
|
||||
elif parsed_args.groups:
|
||||
group_names = _get_names(parsed_args.groups)
|
||||
|
||||
CLIENT.property_clear(property_list, 'group',
|
||||
group_names)
|
||||
else:
|
||||
CLIENT.property_clear(property_list, 'global')
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PropertyList(Lister):
|
||||
"""List all properties."""
|
||||
|
||||
def __init__(self, app, app_args, cmd_name=None):
|
||||
super(Lister, self).__init__(app, app_args,
|
||||
cmd_name=cmd_name)
|
||||
|
||||
self.is_global = True
|
||||
self.is_all_flag = False
|
||||
self.is_long_flag = False
|
||||
self.list_type = None
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PropertyList, self).get_parser(prog_name)
|
||||
parser.add_argument('--all', action='store_true',
|
||||
help=u._('List all properties'))
|
||||
parser.add_argument('--long', action='store_true',
|
||||
help=u._('Show all property attributes'))
|
||||
parser.add_argument('--hosts', nargs=1,
|
||||
metavar='<host_list>',
|
||||
help=u._('Property host list'))
|
||||
parser.add_argument('--groups', nargs=1,
|
||||
metavar='<group_list>',
|
||||
help=u._('Property group list'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
if parsed_args.all:
|
||||
self.is_all_flag = True
|
||||
if parsed_args.long:
|
||||
self.is_long_flag = True
|
||||
|
||||
if parsed_args.hosts:
|
||||
if parsed_args.groups:
|
||||
raise CommandError(
|
||||
u._('Invalid to use both hosts and groups arguments '
|
||||
'together.'))
|
||||
|
||||
self.is_global = False
|
||||
self.list_type = u._('Host')
|
||||
host_names = _get_names(parsed_args.hosts)
|
||||
|
||||
property_list = CLIENT.property_get('host',
|
||||
host_names)
|
||||
|
||||
elif parsed_args.groups:
|
||||
self.is_global = False
|
||||
self.list_type = u._('Group')
|
||||
group_names = _get_names(parsed_args.groups)
|
||||
property_list = CLIENT.property_get('group',
|
||||
group_names)
|
||||
|
||||
else:
|
||||
property_list = CLIENT.property_get('global')
|
||||
|
||||
data = self._get_list_data(property_list)
|
||||
header = self._get_list_header()
|
||||
return (header, data)
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
def _get_list_header(self):
|
||||
header = None
|
||||
if self.is_long_flag:
|
||||
if self.is_global:
|
||||
header = (u._('OVR'),
|
||||
u._('Property Name'), u._('Property Value'),
|
||||
u._('Original Value'))
|
||||
else:
|
||||
header = (u._('OVR'),
|
||||
u._('Property Name'), u._('Property Value'),
|
||||
u._('Original Value'),
|
||||
self.list_type)
|
||||
else:
|
||||
if self.is_global:
|
||||
header = (u._('OVR'),
|
||||
u._('Property Name'), u._('Property Value'))
|
||||
else:
|
||||
header = (u._('OVR'),
|
||||
u._('Property Name'), u._('Property Value'),
|
||||
self.list_type)
|
||||
return header
|
||||
|
||||
def _get_list_data(self, property_list):
|
||||
data = []
|
||||
if property_list:
|
||||
property_length = utils.get_property_list_length()
|
||||
for prop in property_list:
|
||||
include_prop = False
|
||||
if (prop.value is not None and
|
||||
len(str(prop.value)) > property_length):
|
||||
if self.is_all_flag:
|
||||
include_prop = True
|
||||
else:
|
||||
include_prop = True
|
||||
|
||||
if not include_prop:
|
||||
continue
|
||||
|
||||
ovr_global = '-'
|
||||
ovr_group = '-'
|
||||
ovr_host = '-'
|
||||
|
||||
if prop.ovr_global:
|
||||
ovr_global = '*'
|
||||
if prop.ovr_group:
|
||||
ovr_group = 'G'
|
||||
if prop.ovr_host:
|
||||
ovr_host = 'H'
|
||||
|
||||
prop_ovr = ovr_global + ovr_group + ovr_host
|
||||
|
||||
if self.is_long_flag:
|
||||
if self.is_global:
|
||||
data.append((prop_ovr, prop.name, prop.value,
|
||||
prop.orig_value))
|
||||
else:
|
||||
data.append((prop_ovr, prop.name, prop.value,
|
||||
prop.orig_value, prop.target))
|
||||
else:
|
||||
if self.is_global:
|
||||
data.append((prop_ovr, prop.name, prop.value))
|
||||
else:
|
||||
data.append((prop_ovr, prop.name, prop.value,
|
||||
prop.target))
|
||||
else:
|
||||
if self.is_long_flag:
|
||||
if self.is_global:
|
||||
data.append(('', '', '', ''))
|
||||
else:
|
||||
data.append(('', '', '', '', ''))
|
||||
else:
|
||||
if self.is_global:
|
||||
data.append(('', '', ''))
|
||||
else:
|
||||
data.append(('', '', '', ''))
|
||||
|
||||
return data
|
|
@ -1,123 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 traceback
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.api.exceptions import ClientException
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
from kolla_cli.common.utils import convert_lists_to_string
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class ServiceAddGroup(Command):
|
||||
"""Add group to service.
|
||||
|
||||
Associated the service to a group. If this is a sub-service,
|
||||
the inherit flag will be cleared.
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ServiceAddGroup, self).get_parser(prog_name)
|
||||
parser.add_argument('servicename', metavar='<servicename>',
|
||||
help=u._('Service name'))
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
servicename = parsed_args.servicename.strip()
|
||||
|
||||
group = CLIENT.group_get([groupname])[0]
|
||||
group.add_service(servicename)
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class ServiceRemoveGroup(Command):
|
||||
"""Remove group from service."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ServiceRemoveGroup, self).get_parser(prog_name)
|
||||
parser.add_argument('servicename', metavar='<servicename>',
|
||||
help=u._('Service name'))
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
servicename = parsed_args.servicename.strip()
|
||||
|
||||
group = CLIENT.group_get([groupname])[0]
|
||||
group.remove_service(servicename)
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class ServiceListGroups(Lister):
|
||||
"""List services and their groups."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
data = [('', '')]
|
||||
services = CLIENT.service_get_all()
|
||||
if services:
|
||||
data = []
|
||||
for service in services:
|
||||
groupnames = sorted(service.get_groups())
|
||||
data.append((service.name, groupnames))
|
||||
|
||||
data = convert_lists_to_string(data, parsed_args)
|
||||
return (u._('Service'), u._('Groups')), sorted(data)
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class ServiceList(Lister):
|
||||
"""List services and their sub-services."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
data = [('', '')]
|
||||
services = CLIENT.service_get_all()
|
||||
if services:
|
||||
data = []
|
||||
for service in services:
|
||||
data.append((service.name,
|
||||
sorted(service.get_children())))
|
||||
|
||||
data = convert_lists_to_string(data, parsed_args)
|
||||
return ((u._('Service'), u._('Children')), sorted(data))
|
||||
|
||||
except ClientException as e:
|
||||
raise CommandError(str(e))
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 tempfile
|
||||
import traceback
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class Dump(Command):
|
||||
"""Dumps configuration data for debugging.
|
||||
|
||||
Dumps most files in /etc/kolla and /usr/share/kolla into a
|
||||
tar file so be given to support / development to help with
|
||||
debugging problems.
|
||||
"""
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
dump_path = CLIENT.support_dump(tempfile.gettempdir())
|
||||
LOG.info(u._('Dump successful to {path}').format(path=dump_path))
|
||||
except Exception:
|
||||
msg = (u._('Dump failed: {reason}')
|
||||
.format(reason=traceback.format_exc()))
|
||||
raise Exception(msg)
|
|
@ -1,240 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.api.exceptions import InvalidConfiguration
|
||||
from kolla_cli.api.exceptions import NotInInventory
|
||||
from kolla_cli.common.ansible.playbook import AnsiblePlaybook
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.passwords import get_empty_password_values
|
||||
from kolla_cli.common.properties import AnsibleProperties
|
||||
from kolla_cli.common.utils import get_admin_user
|
||||
from kolla_cli.common.utils import get_kolla_ansible_home
|
||||
from kolla_cli.common.utils import get_kolla_etc
|
||||
from kolla_cli.common.utils import is_string_true
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KollaAction(object):
|
||||
"""Kolla Action."""
|
||||
|
||||
def __init__(self, verbose_level=0, playbook_name=''):
|
||||
self.playbook_name = playbook_name
|
||||
self.playbook_path = os.path.join(get_kolla_ansible_home(),
|
||||
'ansible/',
|
||||
self.playbook_name)
|
||||
self.playbook = AnsiblePlaybook()
|
||||
self.playbook.verbose_level = verbose_level
|
||||
self.playbook.playbook_path = self.playbook_path
|
||||
|
||||
def certificate_init(self):
|
||||
'''Creates a self-signed certificate'''
|
||||
|
||||
self.playbook.local_only = True
|
||||
self.playbook.become_user = get_admin_user()
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def deploy(self, hostnames=[], serial_flag=False, servicenames=[]):
|
||||
'''Deploy and start all kolla containers.'''
|
||||
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.serial = serial_flag
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=deploy'
|
||||
|
||||
self._run_deploy_rules(self.playbook)
|
||||
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def reconfigure(self, hostnames=[], servicenames=[]):
|
||||
'''Reconfigure OpenStack service.'''
|
||||
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=reconfigure'
|
||||
|
||||
self._run_deploy_rules(self.playbook)
|
||||
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def postdeploy(self):
|
||||
'''Do post deploy on deploy node.'''
|
||||
|
||||
self.playbook.local_only = True
|
||||
self.playbook.become_user = get_admin_user()
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def destroy_hosts(self, hostnames, destroy_type,
|
||||
include_data=False, remove_images=False):
|
||||
'''destroy containers on a set of hosts.
|
||||
|
||||
The containers on the specified hosts will be stopped
|
||||
or killed.
|
||||
'''
|
||||
|
||||
LOG.info(u._LI('Please be patient as this may take a while.'))
|
||||
# 'hosts' is defined as 'all' in the playbook yml code, but inventory
|
||||
# filtering will subset that down to the hosts in playbook.hosts.
|
||||
self.playbook.hosts = hostnames
|
||||
if remove_images:
|
||||
self.playbook.extra_vars = 'destroy_include_images=yes'
|
||||
if self.playbook.verbose_level <= 1:
|
||||
self.playbook.print_output = False
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def stop(self, hostnames=[], servicenames=[]):
|
||||
'''stop containers on a set of hosts.
|
||||
|
||||
The containers on the specified hosts will be stopped
|
||||
or killed if the stop takes over 20 seconds.
|
||||
'''
|
||||
|
||||
LOG.info(u._LI('Please be patient as this may take a while.'))
|
||||
# 'hosts' is defined as 'all' in the playbook yml code, but inventory
|
||||
# filtering will subset that down to the hosts in playbook.hosts.
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=stop'
|
||||
if self.playbook.verbose_level <= 1:
|
||||
self.playbook.print_output = False
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def precheck(self, hostnames=[], servicenames=[]):
|
||||
'''run check playbooks on a set of hosts'''
|
||||
|
||||
# check that password file has no empty password values
|
||||
empty_keys = get_empty_password_values()
|
||||
if empty_keys:
|
||||
raise InvalidConfiguration(
|
||||
u._('password check failed. There are empty password values '
|
||||
'in {etc}passwords.yml. '
|
||||
'Please run kolla-cli password init or '
|
||||
'kolla-cli password set(key) to correct them. '
|
||||
'\nEmpty passwords: '
|
||||
'{keys}').format(etc=get_kolla_etc(), keys=empty_keys))
|
||||
|
||||
# define 'hosts' to be all, but inventory filtering will subset
|
||||
# that down to the hosts in playbook.hosts.
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=precheck'
|
||||
self.playbook.print_output = True
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def pull(self, hostnames=[], servicenames=[]):
|
||||
'''run pull action against all hosts'''
|
||||
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=pull'
|
||||
self.playbook.print_output = True
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def upgrade(self, hostnames=[], servicenames=[]):
|
||||
'''Upgrades existing OpenStack Environment.'''
|
||||
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=upgrade'
|
||||
self.playbook.print_output = True
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def genconfig(self, hostnames=[], servicenames=[]):
|
||||
'''Generate configuration files for enabled OpenStack services'''
|
||||
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=config'
|
||||
self.playbook.print_output = True
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def check(self, hostnames=[], servicenames=[]):
|
||||
'''Do post-deployment smoke tests.'''
|
||||
|
||||
self.playbook.hosts = hostnames
|
||||
self.playbook.services = servicenames
|
||||
self.playbook.extra_vars = 'kolla_action=check'
|
||||
self.playbook.print_output = True
|
||||
job = self.playbook.run()
|
||||
return job
|
||||
|
||||
def _run_deploy_rules(self, playbook):
|
||||
properties = AnsibleProperties()
|
||||
inventory = Inventory.load()
|
||||
|
||||
# cannot have both groups and hosts
|
||||
if playbook.hosts and playbook.groups:
|
||||
raise InvalidArgument(
|
||||
u._('Hosts and Groups arguments cannot '
|
||||
'both be present at the same time.'))
|
||||
|
||||
# verify that all services exists
|
||||
if playbook.services:
|
||||
for service in playbook.services:
|
||||
valid_service = inventory.get_service(service)
|
||||
if not valid_service:
|
||||
raise NotInInventory(u._('Service'), service)
|
||||
|
||||
# check that every group with enabled services
|
||||
# has hosts associated to it
|
||||
group_services = inventory.get_group_services()
|
||||
failed_groups = []
|
||||
failed_services = []
|
||||
if group_services:
|
||||
for (groupname, servicenames) in group_services.items():
|
||||
group = inventory.get_group(groupname)
|
||||
hosts = group.get_hostnames()
|
||||
|
||||
group_needs_host = False
|
||||
if not hosts:
|
||||
for servicename in servicenames:
|
||||
if self._is_service_enabled(servicename,
|
||||
inventory,
|
||||
properties):
|
||||
group_needs_host = True
|
||||
failed_services.append(servicename)
|
||||
if group_needs_host:
|
||||
failed_groups.append(groupname)
|
||||
|
||||
if len(failed_groups) > 0:
|
||||
raise InvalidConfiguration(
|
||||
u._('Deploy failed. '
|
||||
'Groups: {groups} with enabled '
|
||||
'services : {services} '
|
||||
'have no associated hosts')
|
||||
.format(groups=failed_groups, services=failed_services))
|
||||
|
||||
def _is_service_enabled(self, servicename, inventory, properties):
|
||||
service = inventory.get_service(servicename)
|
||||
if service is not None:
|
||||
enabled_property = 'enable_' + servicename.replace('-', '_')
|
||||
is_enabled = properties.get_property_value(enabled_property)
|
||||
if type(is_enabled) is str:
|
||||
is_enabled = is_string_true(is_enabled)
|
||||
return is_enabled
|
|
@ -1,269 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 fcntl
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import re
|
||||
import subprocess # nosec
|
||||
import time
|
||||
|
||||
from kolla_cli.common.inventory import remove_temp_inventory
|
||||
from kolla_cli.common.utils import get_kolla_actions_path
|
||||
from kolla_cli.common.utils import Lock
|
||||
from kolla_cli.common.utils import PidManager
|
||||
from kolla_cli.common.utils import run_cmd
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
LINE_LENGTH = 80
|
||||
|
||||
ANSIBLE_1_OR_MORE = 'One or more items failed'
|
||||
|
||||
|
||||
class AnsibleJob(object):
|
||||
"""class for running ansible commands"""
|
||||
|
||||
def __init__(self, cmd, deploy_id, print_output, inventory_path):
|
||||
self._command = cmd
|
||||
self._deploy_id = deploy_id
|
||||
self._print_output = print_output
|
||||
self._temp_inv_path = inventory_path
|
||||
|
||||
self._process = None
|
||||
self._process_std_err = None
|
||||
self._errors = []
|
||||
self._error_total = 0
|
||||
self._ignore_total = 0
|
||||
self._cmd_output = ''
|
||||
self._kill_uname = None
|
||||
self._ansible_lock = Lock(owner='ansible_job')
|
||||
self._ignore_error_strings = None
|
||||
self._host_ignored_error_count = {}
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
locked = self._ansible_lock.wait_acquire()
|
||||
if not locked:
|
||||
raise Exception(
|
||||
u._('unable to get lock: {lock}, to run '
|
||||
'ansible job: {cmd} ')
|
||||
.format(lock=self._ansible_lock.lockpath,
|
||||
cmd=self._command))
|
||||
|
||||
LOG.debug('playbook command: %s' % self._command)
|
||||
# ansible 2.2 and later introduced an issue where if
|
||||
# the playbook is executed from within a directory without
|
||||
# read / write permission (which can happen when you,
|
||||
# for example, execute via sudo) it will fail. the
|
||||
# workaround will be to run the ansible command from /tmp
|
||||
# and then change back to the original directory at the end
|
||||
current_dir = os.getcwd() # nosec
|
||||
os.chdir('/tmp') # nosec
|
||||
self._process = subprocess.Popen(self._command, # nosec
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
# setup stdout to be read without blocking
|
||||
LOG.debug('process pid: %s' % self._process.pid)
|
||||
flags = fcntl.fcntl(self._process.stdout, fcntl.F_GETFL)
|
||||
fcntl.fcntl(self._process.stdout, fcntl.F_SETFL,
|
||||
(flags | os.O_NONBLOCK))
|
||||
|
||||
# this is also part of the fix for ansible 2.2 and later
|
||||
os.chdir(current_dir)
|
||||
except Exception as e:
|
||||
self._cleanup()
|
||||
raise e
|
||||
|
||||
def wait(self):
|
||||
"""wait for job to complete
|
||||
|
||||
return status of job (see get_status for status values)
|
||||
"""
|
||||
while True:
|
||||
status = self.get_status()
|
||||
if status is not None:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
return status
|
||||
|
||||
def get_status(self):
|
||||
"""get process status
|
||||
|
||||
status:
|
||||
- None: running
|
||||
- 0: done, success
|
||||
- 1: done, error
|
||||
- 2: done, killed by user
|
||||
"""
|
||||
status = self._process.poll()
|
||||
out = self._read_stream(self._process.stdout)
|
||||
self._cmd_output = ''.join([self._cmd_output, out])
|
||||
|
||||
# unnecessary spaces should be hidden
|
||||
if out:
|
||||
self._log_output(out)
|
||||
if status is not None:
|
||||
# job has completed
|
||||
if self._kill_uname:
|
||||
status = 2
|
||||
msg = (u._('Job killed by user ({name})')
|
||||
.format(name=self._kill_uname))
|
||||
self._errors = [msg]
|
||||
else:
|
||||
status = self._process.returncode
|
||||
if status != 0:
|
||||
# if the process ran and returned a non zero return
|
||||
# code we want to see if we got some ansible errors
|
||||
# and if so if we ignored all the errors. if all
|
||||
# errors are ignored we consider the job a success
|
||||
if (self._error_total > 0 and
|
||||
self._error_total == self._ignore_total):
|
||||
status = 0
|
||||
else:
|
||||
status = 1
|
||||
if not self._process_std_err:
|
||||
# read stderr from process
|
||||
std_err = self._read_stream(self._process.stderr)
|
||||
self._process_std_err = std_err.strip()
|
||||
self._cleanup()
|
||||
return status
|
||||
|
||||
def get_error_message(self):
|
||||
""""get error message"""
|
||||
msg = ''
|
||||
for error in self._errors:
|
||||
if error:
|
||||
msg = ''.join([msg, error, '\n'])
|
||||
|
||||
if ANSIBLE_1_OR_MORE in msg:
|
||||
msg = self._get_msg_from_cmdout(msg)
|
||||
if not msg:
|
||||
msg = self._process_std_err
|
||||
return msg
|
||||
|
||||
def get_command_output(self):
|
||||
"""get command output
|
||||
|
||||
get final output text from command execution
|
||||
"""
|
||||
return self._cmd_output
|
||||
|
||||
def kill(self):
|
||||
"""kill job in progress
|
||||
|
||||
The process pid is owned by root, so
|
||||
that is not killable. Need to kill all its children.
|
||||
"""
|
||||
# the kill must be run as the kolla user so the
|
||||
# kolla_actions program must be used.
|
||||
try:
|
||||
actions_path = get_kolla_actions_path()
|
||||
cmd_prefix = ('%s job -t -p '
|
||||
% actions_path)
|
||||
|
||||
# kill the children from largest to smallest pids.
|
||||
child_pids = PidManager.get_child_pids(self._process.pid)
|
||||
for child_pid in sorted(child_pids, reverse=True):
|
||||
cmd = ''.join([cmd_prefix, child_pid])
|
||||
err_msg, output = run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
LOG.debug('kill failed: %s %s' % (err_msg, output))
|
||||
else:
|
||||
LOG.debug('kill succeeded: %s' % child_pid)
|
||||
|
||||
# record the name of user who killed the job
|
||||
cur_uid = os.getuid()
|
||||
self._kill_uname = pwd.getpwuid(cur_uid)[0]
|
||||
finally:
|
||||
self._cleanup()
|
||||
|
||||
def _get_msg_from_cmdout(self, msg):
|
||||
"""get message from command output
|
||||
|
||||
This is where the error message is in cmd out-
|
||||
\nfailed: [ol7-c5] (item=[u'/etc/kolla/config/aodh.conf',
|
||||
u'/usr/share/kolla/templates/aodh/aodh.conf_augment']) =>
|
||||
{"failed": true, "invocation": {"module_args": {"dest":
|
||||
"/usr/share/kolla/templates/aodh/aodh.conf_augment",
|
||||
"src": "/etc/kolla/config/aodh.conf"}, "module_name": "template"},
|
||||
"item": ["/etc/kolla/config/aodh.conf",
|
||||
"/usr/share/kolla/templates/aodh/aodh.conf_augment"],
|
||||
"msg": "IOError: [Errno 2] No such file or directory:
|
||||
u'/etc/kolla/config/aodh.conf'"}\n
|
||||
"""
|
||||
fail_key = '\nfailed: '
|
||||
hostnames = re.findall(fail_key + r'\[(.+?)]', self._cmd_output)
|
||||
msgs = re.findall(fail_key + '.+ => (.+?)\n', self._cmd_output)
|
||||
|
||||
for i in range(0, min(len(hostnames), len(msgs))):
|
||||
err = ''
|
||||
hostname = hostnames[i]
|
||||
ans_dict_str = msgs[i]
|
||||
try:
|
||||
ans_dict = json.loads(ans_dict_str)
|
||||
err = ans_dict.get('msg', '')
|
||||
except Exception as e:
|
||||
LOG.warn('Exception reading cmd_out ansible dictionary: %s'
|
||||
% str(e))
|
||||
msg = ''.join([msg, 'Host: ', hostname, ', ', err, '\n'])
|
||||
return msg
|
||||
|
||||
def _read_stream(self, stream):
|
||||
out = ''
|
||||
if stream and not stream.closed:
|
||||
try:
|
||||
out = safe_decode(stream.read())
|
||||
except IOError: # nosec
|
||||
# error can happen if stream is empty
|
||||
pass
|
||||
if out is None:
|
||||
out = ''
|
||||
return out
|
||||
|
||||
def _log_lines(self, lines):
|
||||
if self._print_output:
|
||||
for line in lines:
|
||||
LOG.info(line)
|
||||
|
||||
def _log_output(self, output):
|
||||
if self._print_output:
|
||||
LOG.info(output)
|
||||
|
||||
def _cleanup(self):
|
||||
"""cleanup job
|
||||
|
||||
- release the ansible lock
|
||||
- close stdout and stderr
|
||||
- delete temp inventory
|
||||
"""
|
||||
# try to clear the ansible lock
|
||||
self._ansible_lock.release()
|
||||
|
||||
# close the process's stdout and stderr streams
|
||||
if (self._process and self._process.stdout and not
|
||||
self._process.stdout.closed):
|
||||
self._process.stdout.close()
|
||||
if (self._process and self._process.stderr and not
|
||||
self._process.stderr.closed):
|
||||
self._process.stderr.close()
|
||||
|
||||
# delete temp inventory file
|
||||
remove_temp_inventory(self._temp_inv_path)
|
|
@ -1,160 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
import traceback
|
||||
|
||||
from kolla_cli.common.ansible.job import AnsibleJob
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.utils import get_ansible_command
|
||||
from kolla_cli.common.utils import get_kolla_etc
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from typing import List # noqa
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnsiblePlaybook(object):
|
||||
playbook_path = ''
|
||||
extra_vars = ''
|
||||
include_passwords = True
|
||||
flush_cache = True
|
||||
print_output = True
|
||||
verbose_level = 0
|
||||
hosts = None # type: List[str]
|
||||
groups = None # type: List[str]
|
||||
services = None # type: List[str]
|
||||
ignore_error_strings = None # type: List[str]
|
||||
serial = False
|
||||
deploy_id = None # type: str
|
||||
inventory = None # type: Inventory
|
||||
local_only = False
|
||||
become_user = None # type: str
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self.local_only:
|
||||
# Create a temporary local inventory with only localhost
|
||||
self.inventory = Inventory()
|
||||
self.inventory.set_deploy_mode(False)
|
||||
self.inventory.add_host('localhost')
|
||||
else:
|
||||
self.inventory = Inventory.load()
|
||||
inventory_path = self._make_temp_inventory()
|
||||
cmd = self._get_playbook_cmd(inventory_path)
|
||||
self._log_ansible_cmd(cmd, inventory_path)
|
||||
|
||||
# create and run the job
|
||||
job = AnsibleJob(cmd, self.deploy_id,
|
||||
self.print_output, inventory_path)
|
||||
job._ignore_error_strings = self.ignore_error_strings
|
||||
job.run()
|
||||
return job
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
def _get_playbook_cmd(self, inventory_path):
|
||||
flag = ''
|
||||
# verbose levels: 1=not verbose, 2=more verbose
|
||||
if self.verbose_level > 1:
|
||||
flag = '-'
|
||||
for x in range(1, self.verbose_level):
|
||||
flag += 'v'
|
||||
|
||||
ansible_cmd = get_ansible_command(playbook=True)
|
||||
cmd = '%s %s' % (ansible_cmd, flag)
|
||||
|
||||
cmd += ' -i %s' % inventory_path
|
||||
|
||||
if self.include_passwords:
|
||||
cmd += ' %s' % self._get_password_path()
|
||||
|
||||
cmd += ' %s' % self.playbook_path
|
||||
|
||||
if self.extra_vars or self.serial:
|
||||
extra_vars = ''
|
||||
if self.extra_vars:
|
||||
extra_vars = self.extra_vars
|
||||
if self.serial:
|
||||
extra_vars += ' '
|
||||
if self.serial:
|
||||
extra_vars += 'serial_var=1'
|
||||
|
||||
cmd += ' --extra-vars \"%s\"' % extra_vars
|
||||
|
||||
if self.services:
|
||||
service_string = ''
|
||||
first = True
|
||||
for service in self.services:
|
||||
if not first:
|
||||
service_string += ','
|
||||
else:
|
||||
first = False
|
||||
service_string = service_string + service
|
||||
cmd += ' --tags %s' % service_string
|
||||
|
||||
if self.hosts:
|
||||
host_string = ''
|
||||
first = True
|
||||
for host in self.hosts:
|
||||
if not first:
|
||||
host_string += ','
|
||||
else:
|
||||
first = False
|
||||
host_string = host_string + host
|
||||
cmd += ' --limit %s' % host_string
|
||||
|
||||
if self.flush_cache:
|
||||
cmd += ' --flush-cache'
|
||||
|
||||
if self.become_user:
|
||||
cmd += ' --become-user %s' % self.become_user
|
||||
|
||||
return cmd
|
||||
|
||||
def _make_temp_inventory(self):
|
||||
"""Create temporary inventory file
|
||||
|
||||
A temporary inventory is created so that a
|
||||
unique id can be assigned to the deployment.
|
||||
"""
|
||||
inventory_filter = {}
|
||||
inventory_path = \
|
||||
self.inventory.create_json_gen_file(inventory_filter)
|
||||
|
||||
# inv path = /tmp/kolla_UUID/temp_inventory.py
|
||||
deploy_id = os.path.dirname(inventory_path)
|
||||
self.deploy_id = deploy_id.split('kolla_')[1]
|
||||
|
||||
return inventory_path
|
||||
|
||||
def _get_password_path(self):
|
||||
kolla_etc = get_kolla_etc()
|
||||
return ('-e @' + os.path.join(kolla_etc, 'passwords.yml '))
|
||||
|
||||
def _log_ansible_cmd(self, cmd, inventory_path):
|
||||
if self.verbose_level > 2:
|
||||
# log the ansible command
|
||||
LOG.debug('cmd:\n%s' % cmd)
|
||||
|
||||
if self.verbose_level > 3:
|
||||
# log the inventory
|
||||
with open(inventory_path, 'r') as inv_file:
|
||||
inv = inv_file.read()
|
||||
LOG.debug('\ninventory: \n%s' % inv)
|
|
@ -1,28 +0,0 @@
|
|||
# Copyright(c) 2019, caoyuan. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.common.inventory import Inventory
|
||||
|
||||
|
||||
def check_kolla_args(hostnames=[], servicenames=[]):
|
||||
|
||||
if not any([hostnames, servicenames]):
|
||||
return
|
||||
|
||||
inventory = Inventory.load()
|
||||
if hostnames:
|
||||
inventory.validate_hostnames(hostnames)
|
||||
|
||||
if servicenames:
|
||||
inventory.validate_servicenames(servicenames)
|
|
@ -1,121 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
|
||||
from kolla_cli.common.service import Service
|
||||
from kolla_cli.common.utils import get_kolla_ansible_home
|
||||
|
||||
|
||||
class AnsibleInventory(object):
|
||||
"""AnsibleInventory helper class
|
||||
|
||||
This class parses an ansible inventory file and provides an
|
||||
easier to use way to represent that file.
|
||||
"""
|
||||
def __init__(self, inventory_path=None):
|
||||
self.groups = []
|
||||
self.services = {}
|
||||
self.group_vars = {}
|
||||
self.host_vars = {}
|
||||
|
||||
self._load(inventory_path)
|
||||
|
||||
def add_service(self, servicename):
|
||||
if servicename not in self.services:
|
||||
service = Service(servicename)
|
||||
self.services[servicename] = service
|
||||
return self.services[servicename]
|
||||
|
||||
def add_group(self, groupname):
|
||||
if groupname not in self.groups:
|
||||
self.groups.append(groupname)
|
||||
|
||||
def _load(self, inventory_path):
|
||||
"""load an ansible inventory file
|
||||
|
||||
Note: This assumes that there will be a blank line between each
|
||||
section:
|
||||
|
||||
# Mistral
|
||||
[mistral-api:children]
|
||||
mistral
|
||||
|
||||
[mistral-executor:children]
|
||||
mistral
|
||||
"""
|
||||
if not inventory_path:
|
||||
inventory_path = os.path.join(
|
||||
get_kolla_ansible_home(), 'ansible', 'inventory',
|
||||
'all-in-one')
|
||||
with open(inventory_path, 'r') as ain1:
|
||||
ain1_inv = ain1.read()
|
||||
|
||||
lines = [x for x in ain1_inv.split('\n') if not x.startswith('#')]
|
||||
for i in range(0, len(lines)):
|
||||
line = lines[i]
|
||||
if not line.startswith('['):
|
||||
continue
|
||||
line.strip()
|
||||
if ':vars' in line:
|
||||
# this is defining a set of group vars in the next set
|
||||
# of lines.
|
||||
groupname = line.split(':')[0]
|
||||
i = self._process_group_vars(groupname, lines, i)
|
||||
continue
|
||||
if ':children' not in line:
|
||||
groupname = line[1:len(line) - 1]
|
||||
self.add_group(groupname)
|
||||
continue
|
||||
|
||||
servicename = line.split(':children')[0]
|
||||
servicename = servicename[1:]
|
||||
service = self.add_service(servicename)
|
||||
|
||||
# next lines will be parents or groups for service found above
|
||||
has_parents_or_groups = False
|
||||
while True:
|
||||
i += 1
|
||||
line = lines[i]
|
||||
parent_or_group = line.strip()
|
||||
if parent_or_group.startswith('#'):
|
||||
# comment line, skip
|
||||
continue
|
||||
if not parent_or_group:
|
||||
# blank line, done processing parents
|
||||
# if a service has no parent or group associations
|
||||
# we infer that it is not supported and is filtered
|
||||
# from being shown to the client
|
||||
service.set_supported(has_parents_or_groups)
|
||||
break
|
||||
if parent_or_group in self.groups:
|
||||
service.add_groupname(parent_or_group)
|
||||
has_parents_or_groups = True
|
||||
else:
|
||||
service.add_parentname(parent_or_group)
|
||||
has_parents_or_groups = True
|
||||
|
||||
for _, service in self.services.items():
|
||||
for parentname in service.get_parentnames():
|
||||
parent = self.services[parentname]
|
||||
if parent:
|
||||
parent.add_childname(service.name)
|
||||
|
||||
def _process_group_vars(self, groupname, lines, i):
|
||||
# currently group vars are ignored
|
||||
while True:
|
||||
i += 1
|
||||
line = lines[i].strip()
|
||||
if not line:
|
||||
break
|
|
@ -1,35 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
class Host(object):
|
||||
class_version = 1
|
||||
|
||||
def __init__(self, hostname):
|
||||
self.name = hostname
|
||||
self.alias = ''
|
||||
self.is_mgmt = False
|
||||
self.hypervisor = ''
|
||||
self.vars = {}
|
||||
self.version = self.__class__.class_version
|
||||
|
||||
def get_vars(self):
|
||||
return self.vars.copy()
|
||||
|
||||
def set_var(self, name, value):
|
||||
self.vars[name] = value
|
||||
|
||||
def upgrade(self):
|
||||
pass
|
|
@ -1,65 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
from kolla_cli.common.utils import get_admin_user
|
||||
|
||||
ANSIBLE_BECOME = 'ansible_become'
|
||||
ANSIBLE_SSH_USER = 'ansible_ssh_user'
|
||||
ANSIBLE_CONNECTION = 'ansible_connection'
|
||||
|
||||
|
||||
class HostGroup(object):
|
||||
class_version = 1
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.hostnames = []
|
||||
self.vars = {}
|
||||
self.version = self.__class__.class_version
|
||||
|
||||
def upgrade(self):
|
||||
pass
|
||||
|
||||
def add_host(self, host):
|
||||
if host.name is not None and host.name not in self.hostnames:
|
||||
self.hostnames.append(host.name)
|
||||
|
||||
def remove_host(self, host):
|
||||
if host.name in self.hostnames:
|
||||
self.hostnames.remove(host.name)
|
||||
|
||||
def get_hostnames(self):
|
||||
return copy(self.hostnames)
|
||||
|
||||
def get_vars(self):
|
||||
return self.vars.copy()
|
||||
|
||||
def set_var(self, name, value):
|
||||
self.vars[name] = value
|
||||
|
||||
def clear_var(self, name):
|
||||
if name in self.vars:
|
||||
del self.vars[name]
|
||||
|
||||
def set_remote(self, remote_flag):
|
||||
self.set_var(ANSIBLE_BECOME, 'yes')
|
||||
if remote_flag:
|
||||
# set the ssh info for all the servers in the group
|
||||
self.set_var(ANSIBLE_SSH_USER, get_admin_user())
|
||||
self.clear_var(ANSIBLE_CONNECTION)
|
||||
else:
|
||||
# remove ssh info, add local connection type
|
||||
self.set_var(ANSIBLE_CONNECTION, 'local')
|
||||
self.clear_var(ANSIBLE_SSH_USER)
|
|
@ -1,687 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
import json
|
||||
import jsonpickle
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
from kolla_cli.api.exceptions import FailedOperation
|
||||
from kolla_cli.api.exceptions import HostError
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.api.exceptions import InvalidConfiguration
|
||||
from kolla_cli.api.exceptions import MissingArgument
|
||||
from kolla_cli.api.exceptions import NotInInventory
|
||||
from kolla_cli.common.ansible_inventory import AnsibleInventory
|
||||
from kolla_cli.common.host import Host
|
||||
from kolla_cli.common.host_group import HostGroup
|
||||
from kolla_cli.common.service import Service
|
||||
from kolla_cli.common.sshutils import ssh_setup_host
|
||||
from kolla_cli.common.utils import get_admin_uids
|
||||
from kolla_cli.common.utils import get_ansible_command
|
||||
from kolla_cli.common.utils import get_group_vars_dir
|
||||
from kolla_cli.common.utils import get_host_vars_dir
|
||||
from kolla_cli.common.utils import get_kolla_cli_etc
|
||||
from kolla_cli.common.utils import run_cmd
|
||||
from kolla_cli.common.utils import sync_read_file
|
||||
from kolla_cli.common.utils import sync_write_file
|
||||
|
||||
ANSIBLE_SSH_USER = 'ansible_ssh_user'
|
||||
ANSIBLE_CONNECTION = 'ansible_connection'
|
||||
ANSIBLE_BECOME = 'ansible_become'
|
||||
|
||||
INVENTORY_PATH = 'ansible/inventory.json'
|
||||
|
||||
COMPUTE_GRP_NAME = 'compute'
|
||||
|
||||
# these groups cannot be deleted, they are required by kolla
|
||||
PROTECTED_GROUPS = [COMPUTE_GRP_NAME]
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def remove_temp_inventory(path):
|
||||
# type: (str) -> None
|
||||
"""remove temp inventory file and its parent directory"""
|
||||
if path:
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
dirpath = os.path.dirname(path)
|
||||
if os.path.exists(dirpath):
|
||||
os.rmdir(dirpath)
|
||||
|
||||
|
||||
class Inventory(object):
|
||||
class_version = 4
|
||||
"""class version history
|
||||
|
||||
4: (v4.0.1):
|
||||
removed concept of sub-services (not backward compatible)
|
||||
3: (v3.0.1):
|
||||
added aodh, ceph
|
||||
fix to ensure all sub-services have service as parent
|
||||
|
||||
2: (v2.1.1) added ceilometer
|
||||
|
||||
1: (v2.0.1) initial release
|
||||
"""
|
||||
def __init__(self, inventory_path=None):
|
||||
self._groups = {} # kv = name:object
|
||||
self._hosts = {} # kv = name:object
|
||||
self._services = {} # kv = name:object
|
||||
self.vars = {}
|
||||
self.version = self.__class__.class_version
|
||||
self.remote_mode = True
|
||||
|
||||
# initialize the inventory to its defaults as they are
|
||||
# described in the kolla-ansible all-in-one inventory file.
|
||||
self.import_inventory(inventory_path)
|
||||
|
||||
def upgrade(self):
|
||||
# check for new services or subservices in the all-in-one file
|
||||
# leaving in this hook but no upgrade from <4 to 4 is supported
|
||||
# so yanking out all upgrade logic
|
||||
self._upgrade_services()
|
||||
|
||||
# update the version and save upgraded inventory file
|
||||
self.version = self.__class__.class_version
|
||||
Inventory.save(self)
|
||||
|
||||
def _upgrade_services(self):
|
||||
ansible_inventory = AnsibleInventory()
|
||||
|
||||
# add new services
|
||||
for servicename, service in ansible_inventory.services.items():
|
||||
if servicename not in self._services:
|
||||
self._services[servicename] = service
|
||||
else:
|
||||
# If Service newly enabled/disabled, take the latest
|
||||
if self._services[servicename].is_supported() != \
|
||||
service.is_supported():
|
||||
self._services[servicename].set_supported(
|
||||
service.is_supported())
|
||||
|
||||
# If Service group changed, take the latest
|
||||
if sorted(self._services[servicename].get_groupnames()) != \
|
||||
sorted(service.get_groupnames()):
|
||||
self._services[servicename].set_groupnames(
|
||||
service.get_groupnames())
|
||||
|
||||
# remove obsolete services
|
||||
for servicename in copy(self._services).keys():
|
||||
if servicename not in ansible_inventory.services:
|
||||
self.delete_service(servicename)
|
||||
|
||||
@staticmethod
|
||||
def load():
|
||||
"""load the inventory from a pickle file"""
|
||||
inventory_path = os.path.join(get_kolla_cli_etc(), INVENTORY_PATH)
|
||||
data = ''
|
||||
try:
|
||||
if os.path.exists(inventory_path):
|
||||
data = sync_read_file(inventory_path)
|
||||
|
||||
if data.strip():
|
||||
inventory = jsonpickle.decode(data)
|
||||
|
||||
# upgrade version handling
|
||||
if inventory.version != inventory.class_version:
|
||||
inventory.upgrade()
|
||||
else:
|
||||
inventory = Inventory()
|
||||
Inventory.save(inventory)
|
||||
except Exception:
|
||||
raise FailedOperation(
|
||||
u._('Loading inventory failed. : {error}')
|
||||
.format(error=traceback.format_exc()))
|
||||
return inventory
|
||||
|
||||
@staticmethod
|
||||
def save(inventory):
|
||||
"""Save the inventory in a pickle file"""
|
||||
inventory_path = os.path.join(get_kolla_cli_etc(), INVENTORY_PATH)
|
||||
try:
|
||||
# multiple trips thru json to render a readable inventory file
|
||||
data = jsonpickle.encode(inventory)
|
||||
data_str = json.loads(data)
|
||||
pretty_data = json.dumps(data_str, indent=4)
|
||||
sync_write_file(inventory_path, pretty_data)
|
||||
|
||||
except Exception as e:
|
||||
raise FailedOperation(
|
||||
u._('Saving inventory failed. : {error}')
|
||||
.format(error=str(e)))
|
||||
|
||||
def get_hosts(self):
|
||||
return self._hosts.values()
|
||||
|
||||
def get_hostnames(self):
|
||||
return list(self._hosts.keys())
|
||||
|
||||
def get_host(self, hostname):
|
||||
host = None
|
||||
if hostname in self._hosts:
|
||||
host = self._hosts[hostname]
|
||||
return host
|
||||
|
||||
def add_host(self, hostname, groupname=None):
|
||||
"""add host
|
||||
|
||||
if groupname is none, create a new host
|
||||
if group name is not none, add host to group
|
||||
"""
|
||||
if groupname and groupname not in self._groups:
|
||||
raise NotInInventory(u._('Group'), groupname)
|
||||
|
||||
if groupname and hostname not in self._hosts:
|
||||
# if a groupname is specified, the host must already exist
|
||||
raise NotInInventory(u._('Host'), hostname)
|
||||
|
||||
if not groupname and not self.remote_mode and len(self._hosts) >= 1:
|
||||
raise InvalidConfiguration(
|
||||
u._('Cannot have more than one host when in local deploy '
|
||||
'mode.'))
|
||||
|
||||
changed = False
|
||||
# create new host if it doesn't exist
|
||||
host = Host(hostname)
|
||||
if hostname not in self.get_hostnames():
|
||||
# a new host is being added to the inventory
|
||||
changed = True
|
||||
self._hosts[hostname] = host
|
||||
|
||||
# a host is to be added to an existing group
|
||||
elif groupname:
|
||||
group = self._groups[groupname]
|
||||
if hostname not in group.get_hostnames():
|
||||
changed = True
|
||||
group.add_host(host)
|
||||
return changed
|
||||
|
||||
def remove_all_hosts(self):
|
||||
"""remove all hosts."""
|
||||
hostnamess = self.get_hostnames()
|
||||
for hostname in hostnamess:
|
||||
self.remove_host(hostname)
|
||||
|
||||
def remove_host(self, hostname, groupname=None):
|
||||
"""remove host
|
||||
|
||||
if groupname is none, delete host
|
||||
if group name is not none, remove host from group
|
||||
"""
|
||||
changed = False
|
||||
if groupname and groupname not in self._groups:
|
||||
raise NotInInventory(u._('Group'), groupname)
|
||||
|
||||
if hostname not in self._hosts:
|
||||
return changed
|
||||
|
||||
changed = True
|
||||
host = self._hosts[hostname]
|
||||
groups = self.get_groups(host)
|
||||
for group in groups:
|
||||
if not groupname or groupname == group.name:
|
||||
group.remove_host(host)
|
||||
|
||||
if not groupname:
|
||||
host_vars = os.path.join(get_host_vars_dir(), hostname)
|
||||
if os.path.exists(host_vars):
|
||||
os.remove(host_vars)
|
||||
del self._hosts[hostname]
|
||||
return changed
|
||||
|
||||
def setup_hosts(self, hosts_info):
|
||||
"""setup multiple hosts
|
||||
|
||||
hosts_info is a dict of format:
|
||||
{'hostname1': {
|
||||
'password': password
|
||||
'uname': user_name}}
|
||||
|
||||
The uname entry is optional.
|
||||
"""
|
||||
failed_hosts = {}
|
||||
for hostname, host_info in hosts_info.items():
|
||||
host = self.get_host(hostname)
|
||||
if not host:
|
||||
failed_hosts[hostname] = u._("Host doesn't exist.")
|
||||
continue
|
||||
if not host_info or 'password' not in host_info:
|
||||
failed_hosts[hostname] = u._('No password in yml file.')
|
||||
continue
|
||||
passwd = host_info['password']
|
||||
|
||||
# NOTE(caoyuan): The type of password must be string,
|
||||
# convert to string if the password is int.
|
||||
if not isinstance(passwd, str):
|
||||
passwd = str(passwd)
|
||||
uname = None
|
||||
if 'uname' in host_info:
|
||||
uname = host_info['uname']
|
||||
try:
|
||||
self.setup_host(hostname, passwd, uname)
|
||||
except Exception as e:
|
||||
failed_hosts[hostname] = '%s' % e
|
||||
if failed_hosts:
|
||||
summary = '\n'
|
||||
for hostname, err in failed_hosts.items():
|
||||
summary = summary + '- %s: %s\n' % (hostname, err)
|
||||
raise HostError(
|
||||
u._('Not all hosts were set up. : {reasons}')
|
||||
.format(reasons=summary))
|
||||
else:
|
||||
LOG.info(u._LI('All hosts were successfully set up.'))
|
||||
|
||||
def setup_host(self, hostname, password, uname=None):
|
||||
try:
|
||||
LOG.info(
|
||||
u._LI('Starting setup of host ({host}).')
|
||||
.format(host=hostname))
|
||||
check_ok, _ = self.ssh_check_host(hostname)
|
||||
if check_ok:
|
||||
LOG.info(u._LI('Host ({host}) is already setup.')
|
||||
.format(host=hostname))
|
||||
else:
|
||||
# host needs setup
|
||||
ssh_setup_host(hostname, password, uname)
|
||||
check_ok, msg = self.ssh_check_host(hostname)
|
||||
if not check_ok:
|
||||
raise Exception(u._('Post-setup ssh check failed. {err}')
|
||||
.format(err=msg))
|
||||
LOG.info(u._LI('Host ({host}) setup succeeded.')
|
||||
.format(host=hostname))
|
||||
except Exception as e:
|
||||
raise HostError(
|
||||
u._('Host ({host}) setup failed : {error}')
|
||||
.format(host=hostname, error=str(e)))
|
||||
return True
|
||||
|
||||
def ssh_check_hosts(self, hostnames):
|
||||
"""ssh check for hosts
|
||||
|
||||
return {hostname: {'success': True|False,
|
||||
'msg': message}}
|
||||
"""
|
||||
summary = {}
|
||||
for hostname in hostnames:
|
||||
is_ok, msg = self.ssh_check_host(hostname)
|
||||
summary[hostname] = {}
|
||||
summary[hostname]['success'] = is_ok
|
||||
summary[hostname]['msg'] = msg
|
||||
return summary
|
||||
|
||||
def ssh_check_host(self, hostname):
|
||||
err_msg, output = self.run_ansible_command('-m ping', hostname)
|
||||
is_ok = True
|
||||
if err_msg:
|
||||
is_ok = False
|
||||
msg = (
|
||||
u._('Host ({host}) ssh check failed. : {error} {message}')
|
||||
.format(host=hostname, error=err_msg, message=output))
|
||||
else:
|
||||
msg = (u._LI('Host ({host}) ssh check succeeded.')
|
||||
.format(host=hostname))
|
||||
return is_ok, msg
|
||||
|
||||
def run_ansible_command(self, ansible_command, hostname):
|
||||
output = None
|
||||
command_string = '%s -vvv' % \
|
||||
get_ansible_command()
|
||||
gen_file_path = self.create_json_gen_file()
|
||||
cmd = '%s %s -i %s %s' % (command_string, hostname, gen_file_path,
|
||||
ansible_command)
|
||||
try:
|
||||
err_msg, output = run_cmd(cmd, False)
|
||||
except Exception as e:
|
||||
err_msg = str(e)
|
||||
finally:
|
||||
self.remove_json_gen_file(gen_file_path)
|
||||
return err_msg, output
|
||||
|
||||
def add_group(self, groupname):
|
||||
|
||||
# Group names cannot overlap with service names:
|
||||
if groupname in self._services:
|
||||
raise InvalidArgument(
|
||||
u._('Invalid group name. A service name '
|
||||
'cannot be used for a group name.'))
|
||||
|
||||
if groupname not in self._groups:
|
||||
self._groups[groupname] = HostGroup(groupname)
|
||||
|
||||
group = self._groups[groupname]
|
||||
|
||||
group.set_remote(self.remote_mode)
|
||||
|
||||
return group
|
||||
|
||||
def remove_group(self, groupname):
|
||||
if groupname in PROTECTED_GROUPS:
|
||||
raise InvalidArgument(
|
||||
u._('Cannot remove {group} group. It is required by kolla.')
|
||||
.format(group=groupname))
|
||||
|
||||
# remove group from services
|
||||
for service in self._services.values():
|
||||
service.remove_groupname(groupname)
|
||||
|
||||
group_vars = os.path.join(get_group_vars_dir(), groupname)
|
||||
if os.path.exists(group_vars) and groupname != '__GLOBAL__':
|
||||
os.remove(group_vars)
|
||||
|
||||
if groupname in self._groups:
|
||||
del self._groups[groupname]
|
||||
|
||||
def get_group(self, groupname):
|
||||
group = None
|
||||
if groupname in self._groups:
|
||||
group = self._groups[groupname]
|
||||
return group
|
||||
|
||||
def get_groupnames(self):
|
||||
return list(self._groups.keys())
|
||||
|
||||
def get_groups(self, host=None):
|
||||
"""return all groups containing host
|
||||
|
||||
if hosts is none, return all groups in inventory
|
||||
"""
|
||||
groups = []
|
||||
if not host:
|
||||
groups = self._groups.values()
|
||||
|
||||
else:
|
||||
for group in self._groups.values():
|
||||
if host.name in group.get_hostnames():
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
def get_host_groups(self):
|
||||
"""return { hostname : [groupnames] }"""
|
||||
|
||||
host_groups = {}
|
||||
for host in self._hosts.values():
|
||||
host_groups[host.name] = []
|
||||
groups = self.get_groups(host)
|
||||
for group in groups:
|
||||
host_groups[host.name].append(group.name)
|
||||
return host_groups
|
||||
|
||||
def get_group_services(self):
|
||||
"""get groups and their services
|
||||
|
||||
return { groupname: [servicenames] }
|
||||
"""
|
||||
|
||||
group_services = {}
|
||||
|
||||
for group in self.get_groups():
|
||||
group_services[group.name] = []
|
||||
|
||||
for svc in self.get_services():
|
||||
for groupname in svc.get_groupnames():
|
||||
group_services[groupname].append(svc.name)
|
||||
return group_services
|
||||
|
||||
def get_group_hosts(self):
|
||||
"""return { groupname : [hostnames] }"""
|
||||
group_hosts = {}
|
||||
for group in self.get_groups():
|
||||
group_hosts[group.name] = []
|
||||
for hostname in group.get_hostnames():
|
||||
group_hosts[group.name].append(hostname)
|
||||
return group_hosts
|
||||
|
||||
def create_service(self, servicename):
|
||||
if servicename not in self._services:
|
||||
service = Service(servicename)
|
||||
self._services[servicename] = service
|
||||
return self._services[servicename]
|
||||
|
||||
def delete_service(self, servicename):
|
||||
# remove references to this service from all parent / child services
|
||||
if servicename in self._services:
|
||||
service = self._services[servicename]
|
||||
for parentname in service.get_parentnames():
|
||||
parent = self._services[parentname]
|
||||
parent.remove_childname(servicename)
|
||||
for childname in service.get_childnames():
|
||||
child = self._services[childname]
|
||||
child.remove_parentname(servicename)
|
||||
|
||||
# then remove the service itself
|
||||
del self._services[servicename]
|
||||
|
||||
def get_services(self, client_filter=False):
|
||||
if client_filter:
|
||||
services = self._client_filtered_service_dict()
|
||||
else:
|
||||
services = self._services
|
||||
|
||||
return services.values()
|
||||
|
||||
def get_service(self, servicename, client_filter=False):
|
||||
service = None
|
||||
if client_filter:
|
||||
services = self._client_filtered_service_dict()
|
||||
else:
|
||||
services = self._services
|
||||
|
||||
if servicename in services:
|
||||
service = services[servicename]
|
||||
return service
|
||||
|
||||
def add_group_to_service(self, groupname, servicename):
|
||||
if groupname not in self._groups:
|
||||
raise NotInInventory(u._('Group'), groupname)
|
||||
if servicename in self._services:
|
||||
service = self.get_service(servicename)
|
||||
service.add_groupname(groupname)
|
||||
else:
|
||||
raise NotInInventory(u._('Service'), servicename)
|
||||
|
||||
def remove_group_from_service(self, groupname, servicename):
|
||||
if groupname not in self._groups:
|
||||
raise NotInInventory(u._('Group'), groupname)
|
||||
if servicename in self._services:
|
||||
service = self.get_service(servicename)
|
||||
service.remove_groupname(groupname)
|
||||
else:
|
||||
raise NotInInventory(u._('Service'), servicename)
|
||||
|
||||
def set_deploy_mode(self, remote_flag):
|
||||
if not remote_flag and len(self._hosts) > 1:
|
||||
raise InvalidConfiguration(
|
||||
u._('Cannot set local deploy mode when multiple hosts exist.'))
|
||||
self.remote_mode = remote_flag
|
||||
|
||||
for group in self.get_groups():
|
||||
group.set_remote(remote_flag)
|
||||
|
||||
def get_ansible_json(self, inventory_filter=None):
|
||||
"""generate json inventory for ansible
|
||||
|
||||
The hosts and groups added to the json output for ansible will be
|
||||
filtered by the hostnames and groupnames in the deploy filters.
|
||||
This allows a more targeted deploy to a specific set of hosts or
|
||||
groups.
|
||||
|
||||
typical ansible json format:
|
||||
{'group': {
|
||||
'hosts': ['192.168.28.71', '192.168.28.72'],
|
||||
'vars': {
|
||||
|
||||
'ansible_ssh_user': 'johndoe',
|
||||
'ansible_ssh_private_key_file': '~/.ssh/mykey',
|
||||
'example_variable': 'value'},
|
||||
|
||||
'children': [ 'marietta', '5points' ]},
|
||||
|
||||
'_meta': {
|
||||
'hostvars': {
|
||||
|
||||
'192.168.28.71': {
|
||||
'host_specific_var': 'bar'},
|
||||
|
||||
'192.168.28.72': {
|
||||
'host_specific_var': 'foo'}}}}
|
||||
"""
|
||||
|
||||
jdict = {}
|
||||
|
||||
# if no filter provided, use all groups, all hosts
|
||||
deploy_hostnames = self.get_hostnames()
|
||||
deploy_groupnames = self.get_groupnames()
|
||||
if inventory_filter:
|
||||
if 'deploy_hosts' in inventory_filter:
|
||||
deploy_hostnames = inventory_filter['deploy_hosts']
|
||||
if 'deploy_groups' in inventory_filter:
|
||||
deploy_groupnames = inventory_filter['deploy_groups']
|
||||
|
||||
# add hostgroups
|
||||
for group in self.get_groups():
|
||||
jdict[group.name] = {}
|
||||
jdict[group.name]['hosts'] = []
|
||||
|
||||
if group.name in deploy_groupnames:
|
||||
jdict[group.name]['hosts'] = \
|
||||
self._filter_hosts(group.get_hostnames(), deploy_hostnames)
|
||||
jdict[group.name]['children'] = []
|
||||
jdict[group.name]['vars'] = group.get_vars()
|
||||
|
||||
# add all services, what groups they are in and their parents
|
||||
for service in self.get_services():
|
||||
jdict[service.name] = {}
|
||||
groups_and_parents = []
|
||||
groups_and_parents.extend(service.get_groupnames())
|
||||
groups_and_parents.extend(service.get_parentnames())
|
||||
jdict[service.name]['children'] = groups_and_parents
|
||||
|
||||
# temporarily create group containing all hosts. this is needed for
|
||||
# ansible commands that are performed on hosts not yet in groups.
|
||||
group = self.add_group('__GLOBAL__')
|
||||
jdict[group.name] = {}
|
||||
jdict[group.name]['hosts'] = deploy_hostnames
|
||||
jdict[group.name]['vars'] = group.get_vars()
|
||||
self.remove_group(group.name)
|
||||
|
||||
# process hosts vars
|
||||
jdict['_meta'] = {}
|
||||
jdict['_meta']['hostvars'] = {}
|
||||
for hostname in deploy_hostnames:
|
||||
host = self.get_host(hostname)
|
||||
if host:
|
||||
jdict['_meta']['hostvars'][hostname] = host.get_vars()
|
||||
return json.dumps(jdict)
|
||||
|
||||
def _filter_hosts(self, initial_hostnames, deploy_hostnames):
|
||||
"""filter out hosts not in deploy hosts
|
||||
|
||||
Must preserve the ordering of hosts in the group.
|
||||
"""
|
||||
filtered_hostnames = []
|
||||
for hostname in initial_hostnames:
|
||||
if hostname in deploy_hostnames:
|
||||
filtered_hostnames.append(hostname)
|
||||
return filtered_hostnames
|
||||
|
||||
def create_json_gen_file(self, inventory_filter=None):
|
||||
"""create json inventory file using filter ({})
|
||||
|
||||
The inventory will be placed in a directory in /tmp,
|
||||
with the directory name of form kolla_uuid.py,
|
||||
where uuid is a unique deployment id.
|
||||
|
||||
return path to filtered json generator file
|
||||
"""
|
||||
json_out = self.get_ansible_json(inventory_filter)
|
||||
|
||||
deploy_id = str(uuid.uuid4())
|
||||
dirname = 'kolla_%s' % deploy_id
|
||||
dirpath = os.path.join(tempfile.gettempdir(), dirname)
|
||||
os.mkdir(dirpath, 0o775)
|
||||
_, gid = get_admin_uids()
|
||||
json_gen_path = os.path.join(dirpath, 'temp_inventory.py')
|
||||
|
||||
with open(json_gen_path, 'w') as json_gen_file:
|
||||
json_gen_file.write('#!/usr/bin/env python\n')
|
||||
# the quotes here are significant. The json_out has double quotes
|
||||
# embedded in it so single quotes are needed to wrap it.
|
||||
json_gen_file.write("print('%s')" % json_out)
|
||||
|
||||
# set executable by group
|
||||
os.chmod(json_gen_path, 0o555) # nosec
|
||||
return json_gen_path
|
||||
|
||||
def remove_json_gen_file(self, path):
|
||||
remove_temp_inventory(path)
|
||||
|
||||
def validate_hostnames(self, hostnames):
|
||||
if not hostnames:
|
||||
raise MissingArgument(u._('Host name(s)'))
|
||||
invalid_hosts = []
|
||||
for hostname in hostnames:
|
||||
if hostname not in self._hosts:
|
||||
invalid_hosts.append(hostname)
|
||||
if invalid_hosts:
|
||||
raise NotInInventory(u._('Host'), invalid_hosts)
|
||||
|
||||
def validate_groupnames(self, groupnames):
|
||||
if not groupnames:
|
||||
raise MissingArgument(u._('Group name(s)'))
|
||||
invalid_groups = []
|
||||
for groupname in groupnames:
|
||||
if groupname not in self._groups:
|
||||
invalid_groups.append(groupname)
|
||||
if invalid_groups:
|
||||
raise NotInInventory(u._('Group'), invalid_groups)
|
||||
|
||||
def validate_servicenames(self, servicenames, client_filter=False):
|
||||
if not servicenames:
|
||||
raise MissingArgument(u._('Service name(s)'))
|
||||
invalid_services = []
|
||||
if client_filter:
|
||||
services = self._client_filtered_service_dict()
|
||||
else:
|
||||
services = self._services
|
||||
|
||||
for service_name in servicenames:
|
||||
if service_name not in services:
|
||||
invalid_services.append(service_name)
|
||||
if invalid_services:
|
||||
raise NotInInventory(u._('Service'), invalid_services)
|
||||
|
||||
def _client_filtered_service_dict(self):
|
||||
"""Filters out any unsupported services
|
||||
|
||||
:return: filtered dictionary
|
||||
"""
|
||||
filtered_service_dict = {}
|
||||
for service in self._services.values():
|
||||
if service.is_supported():
|
||||
filtered_service_dict[service.name] = service
|
||||
return filtered_service_dict
|
||||
|
||||
def import_inventory(self, inventory_path):
|
||||
ansible_inventory = AnsibleInventory(inventory_path)
|
||||
for groupname in ansible_inventory.groups:
|
||||
self.add_group(groupname)
|
||||
for servicename, service in ansible_inventory.services.items():
|
||||
self._services[servicename] = service
|
|
@ -1,106 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
|
||||
from kolla_cli.api.exceptions import FailedOperation
|
||||
from kolla_cli.common import utils
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
PWDS_FILENAME = 'passwords.yml'
|
||||
|
||||
|
||||
def set_password(pwd_key, pwd_value):
|
||||
"""set a password value
|
||||
|
||||
If the password name exists, it will be changed.
|
||||
If it doesn't exist, a new password will be added.
|
||||
"""
|
||||
value_switch = '-v'
|
||||
if not pwd_value:
|
||||
pwd_value = ''
|
||||
value_switch = ''
|
||||
cmd = '%s -k \'%s\' %s \'%s\'' % (_get_cmd_prefix(), pwd_key, value_switch,
|
||||
pwd_value)
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation(
|
||||
u._('Password set failed. {error} {message}')
|
||||
.format(error=err_msg, message=output))
|
||||
|
||||
|
||||
def set_password_sshkey(pwd_key, private_key, public_key):
|
||||
cmd = '%s -k \'%s\' -r \'%s\' -u \'%s\'' % (_get_cmd_prefix(), pwd_key,
|
||||
private_key, public_key)
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation(
|
||||
u._('Password ssh key set failed. {error} {message}')
|
||||
.format(error=err_msg, message=output))
|
||||
|
||||
|
||||
def clear_password(pwd_key):
|
||||
"""clear a password
|
||||
|
||||
if the password exists, it will be removed from the passwords file
|
||||
"""
|
||||
cmd = '%s -k \'%s\' -c' % (_get_cmd_prefix(), pwd_key)
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation('%s %s' % (err_msg, output))
|
||||
|
||||
|
||||
def get_password_names():
|
||||
"""return a list of password names"""
|
||||
cmd = '%s -l' % (_get_cmd_prefix())
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation('%s %s' % (err_msg, output))
|
||||
|
||||
pwd_names = []
|
||||
if output and ',' in output:
|
||||
pwd_names = output.strip().split(',')
|
||||
return pwd_names
|
||||
|
||||
|
||||
def get_empty_password_values():
|
||||
cmd = '%s -e' % (_get_cmd_prefix())
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
# output of this command is a comma separated string of password keys
|
||||
# that have empty values.
|
||||
if err_msg:
|
||||
raise FailedOperation('%s %s' % (err_msg, output))
|
||||
|
||||
empty_keys = []
|
||||
if output:
|
||||
# password keys exist that have no values
|
||||
empty_keys = output.strip().split(',')
|
||||
return empty_keys
|
||||
|
||||
|
||||
def init_passwords():
|
||||
# init empty passwords & ssh keys to auto-gen'd values
|
||||
cmd = '%s -i' % (_get_cmd_prefix())
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation('%s %s' % (err_msg, output))
|
||||
|
||||
|
||||
def _get_cmd_prefix():
|
||||
actions_path = utils.get_kolla_actions_path()
|
||||
pwd_file_path = os.path.join(utils.get_kolla_etc(),
|
||||
PWDS_FILENAME)
|
||||
prefix = ('%s password -p %s '
|
||||
% (actions_path, pwd_file_path))
|
||||
return prefix
|
|
@ -1,365 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 logging
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from kolla_cli.api.exceptions import NotInInventory
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.utils import change_property
|
||||
from kolla_cli.common.utils import get_group_vars_dir
|
||||
from kolla_cli.common.utils import get_host_vars_dir
|
||||
from kolla_cli.common.utils import get_kolla_ansible_home
|
||||
from kolla_cli.common.utils import sync_read_file
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
ALLVARS_PATH = 'ansible/group_vars/all.yml'
|
||||
GLOBALS_PATH = 'ansible/group_vars/__GLOBAL__'
|
||||
ANSIBLE_ROLES_PATH = 'ansible/roles'
|
||||
ANSIBLE_DEFAULTS_PATH = 'defaults/main.yml'
|
||||
|
||||
|
||||
class AnsibleProperties(object):
|
||||
|
||||
def __init__(self):
|
||||
"""initialize ansible property information
|
||||
|
||||
property information is pulled from the following files
|
||||
(from lowest to highest priority):
|
||||
KOLLA_HOME/ansible/roles/<service>/default/main.yml
|
||||
KOLLA_HOME/ansible/group_vars/all.yml
|
||||
KOLLA_HOME/ansible/group_vars/__GLOBAL__
|
||||
KOLLA_HOME/ansible/group_vars/*
|
||||
KOLLA_HOME/ansible/host_vars/*
|
||||
KOLLA_ETC/passwords.yml
|
||||
"""
|
||||
self.globals_path = os.path.join(get_kolla_ansible_home(),
|
||||
GLOBALS_PATH)
|
||||
self.global_props = []
|
||||
self.unique_global_props = {}
|
||||
self.unique_override_flags = {}
|
||||
self.group_props = {}
|
||||
self.host_props = {}
|
||||
self.properties_loaded = False
|
||||
self._inventory = None
|
||||
|
||||
def _load_properties(self):
|
||||
self._load_inventory()
|
||||
if not self.properties_loaded:
|
||||
self._load_properties_roles()
|
||||
self._load_properties_all()
|
||||
self._load_properties_global()
|
||||
self._load_properties_hostvars()
|
||||
self._load_properties_groupvars()
|
||||
self.properties_loaded = True
|
||||
|
||||
def _load_properties_roles(self):
|
||||
start_dir = os.path.join(get_kolla_ansible_home(), ANSIBLE_ROLES_PATH)
|
||||
services = next(os.walk(start_dir))[1]
|
||||
for service_name in services:
|
||||
file_name = os.path.join(start_dir, service_name,
|
||||
ANSIBLE_DEFAULTS_PATH)
|
||||
if os.path.isfile(file_name):
|
||||
with open(file_name) as service_file:
|
||||
service_contents = yaml.safe_load(service_file)
|
||||
prop_file_name = service_name + ':main.yml'
|
||||
for key, value in service_contents.items():
|
||||
ansible_prop = AnsibleProperty(key, value,
|
||||
prop_file_name)
|
||||
self.global_props.append(ansible_prop)
|
||||
self.unique_global_props[key] = ansible_prop
|
||||
|
||||
def _load_properties_all(self):
|
||||
allvars_path = os.path.join(get_kolla_ansible_home(), ALLVARS_PATH)
|
||||
with open(allvars_path) as allvars_file:
|
||||
allvars_contents = yaml.safe_load(allvars_file)
|
||||
for key, value in allvars_contents.items():
|
||||
overrides = False
|
||||
orig_value = None
|
||||
if key in self.unique_global_props:
|
||||
overrides = True
|
||||
orig_value = self.unique_global_props[key].value
|
||||
ansible_prop = AnsibleProperty(key, value,
|
||||
'group_vars/all.yml',
|
||||
overrides, orig_value)
|
||||
self.global_props.append(ansible_prop)
|
||||
self.unique_global_props[key] = ansible_prop
|
||||
|
||||
def _load_properties_global(self):
|
||||
globals_data = sync_read_file(self.globals_path)
|
||||
globals_contents = yaml.safe_load(globals_data)
|
||||
if not globals_contents:
|
||||
return
|
||||
for key, value in globals_contents.items():
|
||||
overrides = False
|
||||
override_flags = OverrideFlags()
|
||||
orig_value = None
|
||||
if key in self.unique_global_props:
|
||||
overrides = True
|
||||
override_flags.ovr_global = True
|
||||
orig_value = self.unique_global_props[key].value
|
||||
ansible_prop = AnsibleProperty(key, value,
|
||||
'group_vars/__GLOBAL__',
|
||||
overrides, orig_value)
|
||||
ansible_prop.override_flags = override_flags
|
||||
self.global_props.append(ansible_prop)
|
||||
self.unique_global_props[key] = ansible_prop
|
||||
self.unique_override_flags[key] = override_flags
|
||||
|
||||
def _load_properties_hostvars(self):
|
||||
host_dir = get_host_vars_dir()
|
||||
hostnames = self._inventory.get_hostnames()
|
||||
for hostfile in os.listdir(host_dir):
|
||||
if hostfile not in hostnames:
|
||||
# skip any host files that don't match existing hosts
|
||||
continue
|
||||
self.host_props[hostfile] = []
|
||||
with open(os.path.join(host_dir, hostfile)) as host_data:
|
||||
host_contents = yaml.safe_load(host_data)
|
||||
if host_contents is None:
|
||||
continue
|
||||
props = []
|
||||
for key, value in host_contents.items():
|
||||
overrides = False
|
||||
override_flags = OverrideFlags()
|
||||
if key in self.unique_override_flags:
|
||||
override_flags = self.unique_override_flags[key]
|
||||
orig_value = None
|
||||
if key in self.unique_global_props:
|
||||
overrides = True
|
||||
override_flags.ovr_host = True
|
||||
self.unique_override_flags[key] = override_flags
|
||||
orig_value = self.unique_global_props[key].value
|
||||
ansible_prop = AnsibleProperty(key, value,
|
||||
hostfile,
|
||||
overrides, orig_value,
|
||||
'host', hostfile)
|
||||
props.append(ansible_prop)
|
||||
self.host_props[hostfile] = props
|
||||
|
||||
def _load_properties_groupvars(self):
|
||||
group_dir = get_group_vars_dir()
|
||||
groupnames = self._inventory.get_groupnames()
|
||||
for groupfile in os.listdir(group_dir):
|
||||
if groupfile not in groupnames:
|
||||
# skip any files that don't match existing groups
|
||||
continue
|
||||
with open(os.path.join(group_dir, groupfile)) as group_data:
|
||||
group_contents = yaml.safe_load(group_data)
|
||||
if group_contents is None:
|
||||
continue
|
||||
props = []
|
||||
for key, value in group_contents.items():
|
||||
overrides = False
|
||||
override_flags = OverrideFlags()
|
||||
if key in self.unique_override_flags:
|
||||
override_flags = self.unique_override_flags[key]
|
||||
orig_value = None
|
||||
if key in self.unique_global_props:
|
||||
overrides = True
|
||||
override_flags.ovr_group = True
|
||||
self.unique_override_flags[key] = override_flags
|
||||
orig_value = self.unique_global_props[key].value
|
||||
ansible_prop = AnsibleProperty(key, value,
|
||||
groupfile,
|
||||
overrides, orig_value,
|
||||
'group', groupfile)
|
||||
props.append(ansible_prop)
|
||||
self.group_props[groupfile] = props
|
||||
|
||||
def _load_inventory(self):
|
||||
if not self._inventory:
|
||||
self._inventory = Inventory.load() # nosec
|
||||
|
||||
def get_host_list(self, host_list):
|
||||
self._load_properties()
|
||||
prop_list = []
|
||||
if host_list is not None:
|
||||
for host_name in host_list:
|
||||
host = self._inventory.get_host(host_name)
|
||||
if host is None:
|
||||
raise NotInInventory(u._('Host'), host_name)
|
||||
if host_name in self.host_props:
|
||||
prop_list += self.host_props[host_name]
|
||||
else:
|
||||
hosts = self._inventory.get_hosts()
|
||||
for host in hosts:
|
||||
if host.name in self.host_props:
|
||||
prop_list += self.host_props[host.name]
|
||||
return prop_list
|
||||
|
||||
def get_group_list(self, group_list):
|
||||
self._load_properties()
|
||||
prop_list = []
|
||||
if group_list is not None:
|
||||
for group_name in group_list:
|
||||
group = self._inventory.get_group(group_name)
|
||||
if group is None:
|
||||
raise NotInInventory(u._('Group'), group_name)
|
||||
if group_name in self.group_props:
|
||||
prop_list += self.group_props[group_name]
|
||||
else:
|
||||
groups = self._inventory.get_groups()
|
||||
for group in groups:
|
||||
if group.name in self.group_props:
|
||||
prop_list += self.group_props[group.name]
|
||||
return prop_list
|
||||
|
||||
def get_property_value(self, property_name):
|
||||
self._load_properties()
|
||||
prop_val = None
|
||||
if property_name in self.unique_global_props:
|
||||
prop = self.unique_global_props[property_name]
|
||||
prop_val = prop.value
|
||||
return prop_val
|
||||
|
||||
def get_property(self, property_name):
|
||||
self._load_properties()
|
||||
return self.unique_global_props.get(property_name)
|
||||
|
||||
def get_all_unique(self):
|
||||
self._load_properties()
|
||||
unique_list = []
|
||||
for _, value in self.unique_global_props.items():
|
||||
unique_list.append(value)
|
||||
return sorted(unique_list, key=lambda x: x.name)
|
||||
|
||||
def get_all_override_flags(self):
|
||||
self._load_properties()
|
||||
return self.unique_override_flags
|
||||
|
||||
def set_property(self, property_dict):
|
||||
change_property(self.globals_path, property_dict,
|
||||
clear=False)
|
||||
|
||||
def set_host_property(self, property_dict, hosts):
|
||||
# if hosts is None set the property on all hosts
|
||||
self._load_inventory()
|
||||
host_list = []
|
||||
if hosts is None:
|
||||
host_list = self._inventory.get_hosts()
|
||||
else:
|
||||
for host_name in hosts:
|
||||
host = self._inventory.get_host(host_name)
|
||||
if host is None:
|
||||
raise NotInInventory(u._('Host'), host_name)
|
||||
host_list.append(host)
|
||||
try:
|
||||
for host in host_list:
|
||||
file_path = os.path.join(get_host_vars_dir(), host.name)
|
||||
change_property(file_path, property_dict,
|
||||
clear=False)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def set_group_property(self, property_dict, groups):
|
||||
# if groups is None set the property on all hosts
|
||||
self._load_inventory()
|
||||
group_list = []
|
||||
if groups is None:
|
||||
group_list = self._inventory.get_groups()
|
||||
else:
|
||||
for group_name in groups:
|
||||
group = self._inventory.get_group(group_name)
|
||||
if group is None:
|
||||
raise NotInInventory(u._('Group'), group_name)
|
||||
group_list.append(group)
|
||||
try:
|
||||
for group in group_list:
|
||||
tmp_dict = copy.copy(property_dict)
|
||||
file_path = os.path.join(get_group_vars_dir(), group.name)
|
||||
change_property(file_path, tmp_dict,
|
||||
clear=False)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def clear_property(self, property_list):
|
||||
try:
|
||||
change_property(self.globals_path,
|
||||
self._list_to_dict(property_list),
|
||||
clear=True)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def clear_host_property(self, property_list, hosts):
|
||||
# if hosts is None set the property on all hosts
|
||||
self._load_inventory()
|
||||
host_list = []
|
||||
if hosts is None:
|
||||
host_list = self._inventory.get_hosts()
|
||||
else:
|
||||
for host_name in hosts:
|
||||
host = self._inventory.get_host(host_name)
|
||||
if host is None:
|
||||
raise NotInInventory(u._('Host'), host_name)
|
||||
host_list.append(host)
|
||||
try:
|
||||
for host in host_list:
|
||||
file_path = os.path.join(get_host_vars_dir(), host.name)
|
||||
change_property(file_path, self._list_to_dict(property_list),
|
||||
clear=True)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def clear_group_property(self, property_list, groups):
|
||||
# if hosts is None set the property on all hosts
|
||||
self._load_inventory()
|
||||
group_list = []
|
||||
if groups is None:
|
||||
group_list = self._inventory.get_groups()
|
||||
else:
|
||||
for group_name in groups:
|
||||
group = self._inventory.get_group(group_name)
|
||||
if group is None:
|
||||
raise NotInInventory(u._('Group'), group_name)
|
||||
group_list.append(group)
|
||||
try:
|
||||
for group in group_list:
|
||||
file_path = os.path.join(get_group_vars_dir(), group.name)
|
||||
change_property(file_path, self._list_to_dict(property_list),
|
||||
clear=True)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def _list_to_dict(self, property_list):
|
||||
property_dict = {}
|
||||
for key in property_list:
|
||||
property_dict[key] = ''
|
||||
return property_dict
|
||||
|
||||
|
||||
class AnsibleProperty(object):
|
||||
|
||||
def __init__(self, name, value, file_name, overrides=False,
|
||||
orig_value=None, prop_type='global', target=None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.prop_type = prop_type
|
||||
self.file_name = file_name
|
||||
self.overrides = overrides
|
||||
self.orig_value = orig_value
|
||||
self.target = target
|
||||
self.value_type = type(value)
|
||||
|
||||
|
||||
class OverrideFlags(object):
|
||||
|
||||
def __init__(self):
|
||||
self.ovr_global = False
|
||||
self.ovr_group = False
|
||||
self.ovr_host = False
|
|
@ -1,76 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
|
||||
|
||||
class Service(object):
|
||||
class_version = 1
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self._parentnames = []
|
||||
self._childnames = []
|
||||
self._groupnames = []
|
||||
self._supported = True
|
||||
self._vars = {}
|
||||
self.version = self.__class__.class_version
|
||||
|
||||
def upgrade(self):
|
||||
pass
|
||||
|
||||
def add_parentname(self, parentname):
|
||||
if parentname is not None and parentname not in self._parentnames:
|
||||
self._parentnames.append(parentname)
|
||||
|
||||
def remove_parentname(self, parentname):
|
||||
if parentname in self._parentnames:
|
||||
self._parentnames.remove(parentname)
|
||||
|
||||
def get_parentnames(self):
|
||||
return copy(self._parentnames)
|
||||
|
||||
def add_groupname(self, groupname):
|
||||
if groupname is not None and groupname not in self._groupnames:
|
||||
self._groupnames.append(groupname)
|
||||
|
||||
def remove_groupname(self, groupname):
|
||||
if groupname in self._groupnames:
|
||||
self._groupnames.remove(groupname)
|
||||
|
||||
def set_groupnames(self, groupnames):
|
||||
self._groupnames = groupnames
|
||||
|
||||
def get_groupnames(self):
|
||||
return copy(self._groupnames)
|
||||
|
||||
def get_childnames(self):
|
||||
return copy(self._childnames)
|
||||
|
||||
def add_childname(self, childname):
|
||||
if childname not in self._childnames:
|
||||
self._childnames.append(childname)
|
||||
|
||||
def remove_childname(self, childname):
|
||||
if childname in self._childnames:
|
||||
self._childnames.remove(childname)
|
||||
|
||||
def set_supported(self, supported):
|
||||
self._supported = supported
|
||||
|
||||
def is_supported(self):
|
||||
return self._supported
|
||||
|
||||
def get_vars(self):
|
||||
return self._vars.copy()
|
|
@ -1,88 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os.path
|
||||
import paramiko
|
||||
import traceback
|
||||
|
||||
from kolla_cli.common.utils import get_admin_user
|
||||
from kolla_cli.common.utils import get_kolla_cli_etc
|
||||
from kolla_cli.common.utils import get_setup_user
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
MIN_DOCKER_VERSION = '1.10.0'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def ssh_connect(net_addr, username, password):
|
||||
try:
|
||||
ssh_client = paramiko.SSHClient()
|
||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh_client.connect(hostname=net_addr, username=username,
|
||||
password=password)
|
||||
return ssh_client
|
||||
except Exception:
|
||||
_close_ssh_client(ssh_client)
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
def ssh_setup_host(net_addr, password, setup_user=None):
|
||||
admin_user = get_admin_user()
|
||||
if setup_user is None:
|
||||
setup_user = get_setup_user()
|
||||
public_key = ssh_get_public_key()
|
||||
ssh_client = None
|
||||
|
||||
try:
|
||||
ssh_client = ssh_connect(net_addr, setup_user, password)
|
||||
# populate authorized keys file w/ public key
|
||||
key_dir = os.path.join(os.path.expanduser('~%s' % admin_user),
|
||||
'.ssh', 'authorized_keys')
|
||||
cmd = ('/usr/bin/sudo su - %s -c "echo \'%s\' >> %s"'
|
||||
% (admin_user, public_key, key_dir))
|
||||
_exec_ssh_cmd(cmd, ssh_client)
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
_close_ssh_client(ssh_client)
|
||||
|
||||
|
||||
def _close_ssh_client(ssh_client):
|
||||
if ssh_client:
|
||||
try:
|
||||
ssh_client.close()
|
||||
except Exception: # nosec
|
||||
pass
|
||||
|
||||
|
||||
def _exec_ssh_cmd(cmd, ssh_client):
|
||||
LOG.debug(cmd)
|
||||
_, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True) # nosec
|
||||
msg = stdout.read()
|
||||
errmsg = stderr.read()
|
||||
LOG.debug('%s : %s' % (msg, errmsg))
|
||||
if errmsg:
|
||||
LOG.warn(
|
||||
u._LW('WARNING: command : {command})\nmessage : {message}')
|
||||
.format(command=cmd, message=errmsg.strip()))
|
||||
return msg, errmsg
|
||||
|
||||
|
||||
def ssh_get_public_key():
|
||||
keyfile_path = os.path.join(get_kolla_cli_etc(), 'id_rsa.pub')
|
||||
with open(keyfile_path, "r") as public_key_file:
|
||||
public_key = public_key_file.read()
|
||||
return public_key
|
|
@ -1,51 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 copy import copy
|
||||
|
||||
|
||||
class SubService(object):
|
||||
class_version = 1
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
self._groupnames = []
|
||||
self._parent_servicename = None
|
||||
|
||||
self._vars = {}
|
||||
self.version = self.__class__.class_version
|
||||
|
||||
def upgrade(self):
|
||||
pass
|
||||
|
||||
def add_groupname(self, groupname):
|
||||
if groupname is not None and groupname not in self._groupnames:
|
||||
self._groupnames.append(groupname)
|
||||
|
||||
def remove_groupname(self, groupname):
|
||||
if groupname in self._groupnames:
|
||||
self._groupnames.remove(groupname)
|
||||
|
||||
def get_groupnames(self):
|
||||
return copy(self._groupnames)
|
||||
|
||||
def set_parent_servicename(self, parent_svc_name):
|
||||
self._parent_servicename = parent_svc_name
|
||||
|
||||
def get_parent_servicename(self):
|
||||
return copy(self._parent_servicename)
|
||||
|
||||
def get_vars(self):
|
||||
return self.vars.copy()
|
|
@ -1,222 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
from kolla_cli.api.exceptions import FailedOperation
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
from kolla_cli.common.properties import AnsibleProperties
|
||||
from kolla_cli.common.utils import get_kolla_ansible_home
|
||||
from kolla_cli.common.utils import get_kolla_cli_etc
|
||||
from kolla_cli.common.utils import run_cmd
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HostLogs(object):
|
||||
|
||||
def __init__(self, hostname, inventory, servicenames):
|
||||
self.hostname = hostname
|
||||
self.inventory = inventory
|
||||
self.servicenames = servicenames
|
||||
self.container_info = {} # container_id: container_img_name
|
||||
self.filtered_servicenames = {}
|
||||
|
||||
def load_container_info(self):
|
||||
"""get the list of containers on the host"""
|
||||
hostname = self.hostname
|
||||
err_msg, output = \
|
||||
self.inventory.run_ansible_command('-a "docker ps -a"', hostname)
|
||||
if err_msg:
|
||||
msg = 'Error accessing host %s : %s %s' % (hostname, err_msg,
|
||||
output)
|
||||
raise FailedOperation(msg)
|
||||
|
||||
if not output:
|
||||
msg = ('Host %s is not accessible.' % hostname)
|
||||
raise FailedOperation(msg)
|
||||
else:
|
||||
if '>>' not in output:
|
||||
msg = ('Host: %s. Invalid ansible return data: [%s].'
|
||||
% (hostname, output))
|
||||
raise FailedOperation(msg)
|
||||
|
||||
if 'NAMES' not in output:
|
||||
msg = ('Host: %s. Invalid docker ps return data: [%s].'
|
||||
% (hostname, output))
|
||||
raise FailedOperation(msg)
|
||||
|
||||
ansible_properties = AnsibleProperties()
|
||||
base_distro = \
|
||||
ansible_properties.get_property_value('kolla_base_distro')
|
||||
install_type = \
|
||||
ansible_properties.get_property_value('kolla_install_type')
|
||||
# typically this prefix will be "ol-openstack-"
|
||||
container_prefix = base_distro + '-' + install_type + '-'
|
||||
|
||||
# process ps output
|
||||
containers = {}
|
||||
|
||||
# the ps output is after the '>>'
|
||||
output = output.split('>>', 1)[1]
|
||||
LOG.info('docker ps -a on host: %s:\n%s' % (hostname, output))
|
||||
|
||||
lines = output.split('\n')
|
||||
for line in lines:
|
||||
tokens = line.split()
|
||||
if len(tokens) < 2:
|
||||
continue
|
||||
cid = tokens[0]
|
||||
image = tokens[1]
|
||||
if container_prefix not in image:
|
||||
# skip non-kolla containers
|
||||
continue
|
||||
name = image.split(container_prefix)[1]
|
||||
name = name.split(':')[0]
|
||||
containers[cid] = name
|
||||
self.container_info = containers
|
||||
|
||||
def get_log(self, container_id):
|
||||
"""read the container log"""
|
||||
hostname = self.hostname
|
||||
cmd = '-a "docker logs %s"' % container_id
|
||||
err_msg, output = self.inventory.run_ansible_command(cmd, hostname)
|
||||
if err_msg:
|
||||
msg = 'Error accessing host %s : %s ' % (hostname, err_msg)
|
||||
raise FailedOperation(msg)
|
||||
|
||||
if not output:
|
||||
msg = ('Host %s is not accessible.' % hostname)
|
||||
raise FailedOperation(msg)
|
||||
if '>>' not in output:
|
||||
msg = ('Host: %s. Invalid ansible return data: [%s].'
|
||||
% (hostname, output))
|
||||
raise FailedOperation(msg)
|
||||
|
||||
# the log info is after the '>>'
|
||||
output = output.split('>>', 1)[1]
|
||||
return output
|
||||
|
||||
def write_logs(self, dirname):
|
||||
"""write out the log files for all containers"""
|
||||
for container_id, container_name in self.filtered_services.items():
|
||||
logdata = self.get_log(container_id)
|
||||
if logdata:
|
||||
logname = '%s_%s.log' % (container_name, container_id)
|
||||
self.write_logfile(dirname, logname, logdata)
|
||||
else:
|
||||
LOG.warn('No log data found for service %s on host %s'
|
||||
% (container_name, self.hostname))
|
||||
|
||||
def write_logfile(self, dirpath, logname, logdata):
|
||||
"""write out one log file"""
|
||||
hostdir = os.path.join(dirpath, self.hostname)
|
||||
if not os.path.exists(hostdir):
|
||||
os.mkdir(hostdir)
|
||||
fpath = os.path.join(hostdir, logname)
|
||||
with open(fpath, 'w') as logfile:
|
||||
logfile.write(logdata)
|
||||
|
||||
def filter_services(self):
|
||||
"""filter services to only those of interest"""
|
||||
services_subset = {}
|
||||
for host_svcid, host_svcname in self.container_info.items():
|
||||
for servicename in self.servicenames:
|
||||
if (host_svcname == servicename or
|
||||
host_svcname.startswith(servicename + '-')):
|
||||
services_subset[host_svcid] = host_svcname
|
||||
self.filtered_services = services_subset
|
||||
|
||||
|
||||
def get_logs(servicenames, hostname, dirname):
|
||||
inventory = Inventory.load()
|
||||
inventory.validate_hostnames([hostname])
|
||||
inventory.validate_servicenames(servicenames, client_filter=True)
|
||||
|
||||
logs = HostLogs(hostname, inventory, servicenames)
|
||||
logs.load_container_info()
|
||||
logs.filter_services()
|
||||
logs.write_logs(dirname)
|
||||
|
||||
|
||||
def dump(dirpath):
|
||||
"""Dumps configuration data for debugging
|
||||
|
||||
Dumps most files in /etc/kolla and /usr/share/kolla into a
|
||||
tar file so be given to support / development to help with
|
||||
debugging problems.
|
||||
"""
|
||||
kolla_home = get_kolla_ansible_home()
|
||||
kolla_ansible = os.path.join(kolla_home, 'ansible')
|
||||
kollacli_etc = get_kolla_cli_etc().rstrip('/')
|
||||
ketc = 'kolla/etc/'
|
||||
kshare = 'kolla/share/'
|
||||
fd, dump_path = tempfile.mkstemp(dir=dirpath, prefix='kollacli_dump_',
|
||||
suffix='.tgz')
|
||||
os.close(fd) # avoid fd leak
|
||||
with tarfile.open(dump_path, 'w:gz') as tar:
|
||||
# Can't blanket add kolla_home because the .ssh dir is
|
||||
# accessible by the kolla user only (not kolla group)
|
||||
tar.add(kolla_ansible,
|
||||
arcname=kshare + os.path.basename(kolla_ansible))
|
||||
|
||||
# Can't blanket add kolla_etc because the passwords.yml
|
||||
# file is accessible by the kolla user only (not kolla group)
|
||||
tar.add(kollacli_etc,
|
||||
arcname=ketc + os.path.basename(kollacli_etc))
|
||||
|
||||
# add output of various commands
|
||||
_add_cmd_info(tar)
|
||||
|
||||
return dump_path
|
||||
|
||||
|
||||
def _add_cmd_info(tar):
|
||||
# run all the kollacli list commands
|
||||
cmds = ['kolla-cli --version',
|
||||
'kolla-cli service listgroups',
|
||||
'kolla-cli service list',
|
||||
'kolla-cli group listservices',
|
||||
'kolla-cli group listhosts',
|
||||
'kolla-cli host list',
|
||||
'kolla-cli property list',
|
||||
'kolla-cli password list']
|
||||
|
||||
# collect the json inventory output
|
||||
inventory = Inventory.load()
|
||||
inv_path = inventory.create_json_gen_file()
|
||||
cmds.append(inv_path)
|
||||
|
||||
try:
|
||||
fd, path = tempfile.mkstemp(suffix='.tmp')
|
||||
os.close(fd)
|
||||
with open(path, 'w') as tmp_file:
|
||||
for cmd in cmds:
|
||||
err_msg, output = run_cmd(cmd, False)
|
||||
tmp_file.write('\n\n$ %s\n' % cmd)
|
||||
if err_msg:
|
||||
tmp_file.write('Error message: %s\n' % err_msg)
|
||||
for line in output.split('\n'):
|
||||
tmp_file.write(line + '\n')
|
||||
|
||||
tar.add(path, arcname=os.path.join('kolla', 'cmds_output'))
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if path:
|
||||
os.remove(path)
|
||||
inventory.remove_json_gen_file(inv_path)
|
||||
return
|
|
@ -1,665 +0,0 @@
|
|||
# Copyright(c) 2017, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 fcntl
|
||||
import grp
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import subprocess # nosec
|
||||
import time
|
||||
import yaml
|
||||
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
from kolla_cli.api.exceptions import MissingArgument
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
private_key_string = 'private_key'
|
||||
public_key_string = 'public_key'
|
||||
|
||||
|
||||
def get_log_level():
|
||||
evar = os.environ.get('KOLLA_LOG_LEVEL', 'info')
|
||||
if evar.lower() == 'debug':
|
||||
level = logging.DEBUG
|
||||
else:
|
||||
level = logging.INFO
|
||||
return level
|
||||
|
||||
|
||||
def get_kolla_ansible_home():
|
||||
return os.environ.get("KOLLA_HOME", "/usr/share/kolla-ansible/")
|
||||
|
||||
|
||||
def get_kolla_etc():
|
||||
return os.environ.get('KOLLA_ETC', '/etc/kolla/')
|
||||
|
||||
|
||||
def get_kolla_cli_home():
|
||||
return os.path.join(get_kolla_ansible_home(), 'kolla-cli')
|
||||
|
||||
|
||||
def get_kolla_cli_etc():
|
||||
return os.path.join(get_kolla_etc(), 'kolla-cli')
|
||||
|
||||
|
||||
def get_group_vars_dir():
|
||||
return os.path.join(get_kolla_ansible_home(), 'ansible/group_vars')
|
||||
|
||||
|
||||
def get_host_vars_dir():
|
||||
return os.path.join(get_kolla_ansible_home(), 'ansible/host_vars')
|
||||
|
||||
|
||||
def get_tools_path():
|
||||
return os.environ.get(
|
||||
'KOLLA_TOOLS_DIR', os.path.join(get_kolla_cli_home(), 'tools'))
|
||||
|
||||
|
||||
def get_kolla_actions_path():
|
||||
return os.path.join(get_tools_path(), 'kolla_actions.py')
|
||||
|
||||
|
||||
def get_admin_uids():
|
||||
"""get uid and gid of admin user"""
|
||||
user_info = pwd.getpwnam(get_admin_user())
|
||||
uid = user_info.pw_uid
|
||||
gid = user_info.pw_gid
|
||||
return uid, gid
|
||||
|
||||
|
||||
def get_property_list_length():
|
||||
envvar = 'KOLLA_PROP_LIST_LENGTH'
|
||||
length_str = os.environ.get(envvar, '50')
|
||||
try:
|
||||
length = int(length_str)
|
||||
except Exception:
|
||||
raise InvalidArgument(
|
||||
u._('Environmental variable ({env_var}) is not an '
|
||||
'integer ({prop_length}).')
|
||||
.format(env_var=envvar, prop_length=length_str))
|
||||
return length
|
||||
|
||||
|
||||
def get_admin_user():
|
||||
return os.environ.get("KOLLA_CLI_ADMIN_USER", "root")
|
||||
|
||||
|
||||
def get_setup_user():
|
||||
return os.environ.get("KOLLA_CLI_SETUP_USER", "root")
|
||||
|
||||
|
||||
def get_lock_enabled():
|
||||
evar = os.environ.get('KOLLA_CLI_LOCK', 'true')
|
||||
if evar.lower() == 'false':
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def get_ansible_command(playbook=False):
|
||||
"""Get the ansible command"""
|
||||
cmd = 'ansible'
|
||||
if playbook:
|
||||
cmd = 'ansible-playbook'
|
||||
return cmd
|
||||
|
||||
|
||||
def run_cmd(cmd, print_output=True):
|
||||
"""run a system command
|
||||
|
||||
return:
|
||||
err_msg:
|
||||
empty string=command succeeded not None=command failed
|
||||
output:
|
||||
string: all the output of the run command
|
||||
"""
|
||||
output = None
|
||||
try:
|
||||
process = subprocess.Popen(cmd, shell=True, # nosec
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
output, err = process.communicate()
|
||||
except Exception as e:
|
||||
err = str(e)
|
||||
|
||||
err = safe_decode(err)
|
||||
output = safe_decode(output)
|
||||
if process is not None and process.returncode != 0:
|
||||
err = (u._('Command failed. : {error}')
|
||||
.format(error=err))
|
||||
if print_output:
|
||||
LOG.info(output)
|
||||
return err, output
|
||||
|
||||
|
||||
def change_password(file_path, pname, pvalue=None, public_key=None,
|
||||
private_key=None, clear=False):
|
||||
"""change password in passwords.yml file
|
||||
|
||||
file_path: path to passwords file
|
||||
pname: name of password
|
||||
pvalue: value of password when not ssh key
|
||||
public_key: public ssh key
|
||||
private_key: private ssh key
|
||||
clear: flag to clear password
|
||||
|
||||
If key is not found, an error is returned.
|
||||
If clear, and password exists, remove password.
|
||||
If clear, and password is already empty, nothing is done.
|
||||
If not clear, edit password in place.
|
||||
|
||||
The passwords file contains both key-value pairs and key-dictionary
|
||||
pairs. Type is maintained so you cannot change a key-dictionary
|
||||
password to a key-value password or the other way around.
|
||||
"""
|
||||
read_data = sync_read_file(file_path)
|
||||
file_pwds = yaml.safe_load(read_data)
|
||||
# if the password file is empty file_pwds will be None after safe_load
|
||||
if file_pwds is None:
|
||||
file_pwds = {}
|
||||
|
||||
if pname not in file_pwds.keys():
|
||||
raise Exception(
|
||||
u._('unable to update password as it does not exist: {pname}')
|
||||
.format(pname=pname))
|
||||
|
||||
ssh_password_type = is_ssh_password(file_pwds[pname])
|
||||
|
||||
if clear:
|
||||
# clear
|
||||
if pname in file_pwds:
|
||||
if ssh_password_type:
|
||||
file_pwds[pname] = {private_key_string: None,
|
||||
public_key_string: None}
|
||||
else:
|
||||
file_pwds[pname] = None
|
||||
else:
|
||||
# edit
|
||||
if private_key:
|
||||
if not ssh_password_type:
|
||||
raise Exception(
|
||||
u._('unable to set non ssh type password to ssh value'))
|
||||
file_pwds[pname] = {private_key_string: private_key,
|
||||
public_key_string: public_key}
|
||||
else:
|
||||
if ssh_password_type:
|
||||
raise Exception(
|
||||
u._('unable to set ssh password type to non ssh value'))
|
||||
if not pvalue:
|
||||
pvalue = None
|
||||
file_pwds[pname] = pvalue
|
||||
|
||||
# dump Nones as empty strings instead of the value 'null' as this is how
|
||||
# it looks when we read it. also, this will not work with safe_dump
|
||||
yaml.add_representer(type(None), _empty_is_none)
|
||||
write_data = yaml.dump(file_pwds, default_flow_style=False)
|
||||
sync_write_file(file_path, write_data)
|
||||
|
||||
|
||||
def clear_all_passwords():
|
||||
"""clear all passwords in passwords.yml file"""
|
||||
password_path = os.path.join(get_kolla_etc(), 'passwords.yml')
|
||||
read_data = sync_read_file(password_path)
|
||||
file_pwds = yaml.safe_load(read_data)
|
||||
# if the password file is empty file_pwds will be None after safe_load
|
||||
if file_pwds is None:
|
||||
file_pwds = {}
|
||||
|
||||
keys = file_pwds.keys()
|
||||
for key in keys:
|
||||
if is_ssh_password(file_pwds[key]):
|
||||
file_pwds[key] = {private_key_string: None,
|
||||
public_key_string: None}
|
||||
else:
|
||||
file_pwds[key] = None
|
||||
|
||||
yaml.add_representer(type(None), _empty_is_none)
|
||||
write_data = yaml.dump(file_pwds, default_flow_style=False)
|
||||
sync_write_file(password_path, write_data)
|
||||
|
||||
|
||||
def _empty_is_none(self, _):
|
||||
return self.represent_scalar('tag:yaml.org,2002:null', '')
|
||||
|
||||
|
||||
def is_ssh_password(password):
|
||||
if password is not None:
|
||||
if isinstance(password, dict):
|
||||
password_keys = password.keys()
|
||||
if (private_key_string in password_keys and
|
||||
public_key_string in password_keys):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def change_property(file_path, property_dict, clear=False):
|
||||
"""change property with a file
|
||||
|
||||
file_path: path to property file
|
||||
property_dict: dictionary of property names and values
|
||||
clear: flag to remove property
|
||||
|
||||
If clear, and property exists, remove it from the property file.
|
||||
If clear, and property doesn't exists, nothing is done.
|
||||
If not clear, and key is not found, the new property will be appended.
|
||||
If not clear, and key is found, edit property in place.
|
||||
"""
|
||||
cloned_dict = copy.copy(property_dict)
|
||||
if not os.path.exists(file_path):
|
||||
with open(file_path, 'a'):
|
||||
os.utime(file_path, None)
|
||||
try:
|
||||
group_info = grp.getgrnam('kolla')
|
||||
os.chown(file_path, -1, group_info.gr_gid)
|
||||
except KeyError:
|
||||
# ignore error if kolla user not present, needed
|
||||
# for functional test
|
||||
LOG.debug('Ignoring error- kolla user not defined')
|
||||
|
||||
new_contents = []
|
||||
read_data = sync_read_file(file_path)
|
||||
lines = read_data.split('\n')
|
||||
last_line_empty = False
|
||||
for line in lines:
|
||||
line = line.rstrip()
|
||||
|
||||
# yank spurious empty lines
|
||||
if line:
|
||||
last_line_empty = False
|
||||
else:
|
||||
if last_line_empty:
|
||||
continue
|
||||
last_line_empty = True
|
||||
|
||||
split_line = line.split(':', 1)
|
||||
if len(split_line) > 1:
|
||||
split_key = split_line[0]
|
||||
split_key.rstrip()
|
||||
if split_key in cloned_dict:
|
||||
if clear:
|
||||
# clear existing property
|
||||
continue
|
||||
# edit existing property
|
||||
value = cloned_dict[split_key]
|
||||
line = _get_property_line(split_key, value)
|
||||
|
||||
# clear out the key after we are done, all existing keys
|
||||
# will be appended at the end (or for clear, ignored)
|
||||
del cloned_dict[split_key]
|
||||
new_contents.append(line)
|
||||
if not clear:
|
||||
# add new properties to file
|
||||
for key, value in cloned_dict.items():
|
||||
line = _get_property_line(key, value)
|
||||
|
||||
# when we are doing an append we want to avoid
|
||||
# blank lines before the new entry
|
||||
if new_contents[-1:][0] == '':
|
||||
del new_contents[-1]
|
||||
new_contents.append(line)
|
||||
|
||||
# if the last line is blank, trim it off
|
||||
if new_contents[-1:][0] == '':
|
||||
del new_contents[-1]
|
||||
write_data = '\n'.join(new_contents) + '\n'
|
||||
sync_write_file(file_path, write_data)
|
||||
|
||||
|
||||
def _get_property_line(key, value):
|
||||
if type(value) is str:
|
||||
line = '%s: "%s"' % (key, value)
|
||||
else:
|
||||
str_value = yaml.safe_dump(value).strip()
|
||||
value_type = type(value)
|
||||
if value_type is bool or value_type is int:
|
||||
# yaml dump adds a newline and an ellipsis after
|
||||
# a boolean or int value. This needs to be stripped.
|
||||
str_value = str_value.replace('\n...', '')
|
||||
line = '%s: %s' % (key, str_value)
|
||||
return line
|
||||
|
||||
|
||||
def sync_read_file(path, mode='r'):
|
||||
"""synchronously read file
|
||||
|
||||
return file data
|
||||
"""
|
||||
lock = None
|
||||
try:
|
||||
if get_lock_enabled():
|
||||
lock = Lock(path, 'sync_read')
|
||||
locked = lock.wait_acquire()
|
||||
if not locked:
|
||||
raise Exception(
|
||||
u._('unable to read file {path} '
|
||||
'as it was locked.')
|
||||
.format(path=path))
|
||||
with open(path, mode) as data_file:
|
||||
data = data_file.read()
|
||||
finally:
|
||||
if lock:
|
||||
lock.release()
|
||||
return safe_decode(data)
|
||||
|
||||
|
||||
def sync_write_file(path, data, mode='w'):
|
||||
"""synchronously write file"""
|
||||
ansible_lock = None
|
||||
lock = None
|
||||
try:
|
||||
if get_lock_enabled():
|
||||
ansible_lock = Lock(owner='sync_write')
|
||||
locked = ansible_lock.wait_acquire()
|
||||
if not locked:
|
||||
raise Exception(
|
||||
u._('unable to get ansible lock while writing to {path} '
|
||||
'as it was locked.')
|
||||
.format(path=path))
|
||||
|
||||
lock = Lock(lockpath=path, owner='sync_write')
|
||||
locked = lock.wait_acquire()
|
||||
if not locked:
|
||||
raise Exception(
|
||||
u._('unable to write file {path} '
|
||||
'as it was locked.')
|
||||
.format(path=path))
|
||||
with open(path, mode) as data_file:
|
||||
data_file.write(data)
|
||||
finally:
|
||||
if ansible_lock:
|
||||
ansible_lock.release()
|
||||
if lock:
|
||||
lock.release()
|
||||
|
||||
|
||||
def safe_decode(obj_to_decode):
|
||||
"""Convert bytes or strings to unicode string
|
||||
|
||||
Converts strings, lists, or dictionaries to
|
||||
unicode.
|
||||
"""
|
||||
if obj_to_decode is None:
|
||||
return None
|
||||
|
||||
if isinstance(obj_to_decode, list):
|
||||
new_obj = []
|
||||
for text in obj_to_decode:
|
||||
text = safe_decode(text)
|
||||
new_obj.append(text)
|
||||
elif isinstance(obj_to_decode, dict):
|
||||
new_obj = {}
|
||||
for key, value in obj_to_decode.items():
|
||||
key = safe_decode(key)
|
||||
value = safe_decode(value)
|
||||
new_obj[key] = value
|
||||
else:
|
||||
new_obj = obj_to_decode
|
||||
if not isinstance(obj_to_decode, str):
|
||||
# object is not unicode
|
||||
new_obj = obj_to_decode.decode('utf-8')
|
||||
return new_obj
|
||||
|
||||
|
||||
def is_string_true(string):
|
||||
"""Return boolean True if string represents a true value (None is False)"""
|
||||
true_values = ['yes', 'true']
|
||||
if string is not None and string.lower() in true_values:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def convert_lists_to_string(tuples, parsed_args):
|
||||
"""convert lists to strings
|
||||
|
||||
Because of the way cliff processes strings for tables, if a list
|
||||
has non-ascii chars in it, they would display as unicode bytes
|
||||
(\u0414\u0435\u043a\u0430\u0442). By converting
|
||||
the list to string here, the proper non-ascii chars are displayed.
|
||||
|
||||
This will only change the lists when the output is to a user visible
|
||||
medium. It cannot be changed if the display output is json, yaml, etc.
|
||||
"""
|
||||
convert_types = ['table', 'csv', 'html', 'value']
|
||||
if parsed_args.formatter and parsed_args.formatter not in convert_types:
|
||||
# not table output, leave it as-is
|
||||
return tuples
|
||||
|
||||
new_tuples = []
|
||||
for data_tuple in tuples:
|
||||
new_items = []
|
||||
items = list(data_tuple)
|
||||
for item in items:
|
||||
if isinstance(item, list):
|
||||
item = convert_list_to_string(item)
|
||||
new_items.append(item)
|
||||
data_tuple = tuple(new_items)
|
||||
new_tuples.append(data_tuple)
|
||||
return new_tuples
|
||||
|
||||
|
||||
def convert_list_to_string(alist):
|
||||
return '[' + ','.join(alist) + ']'
|
||||
|
||||
|
||||
def check_arg(param, param_name, expected_type, none_ok=False, empty_ok=False,
|
||||
display_param=True):
|
||||
if param is None:
|
||||
if none_ok:
|
||||
return
|
||||
# None arg
|
||||
raise MissingArgument(param_name)
|
||||
|
||||
if ((isinstance(param, str) or
|
||||
isinstance(param, dict) or
|
||||
isinstance(param, list)) and
|
||||
not param and not empty_ok):
|
||||
# empty string, dict or list
|
||||
raise MissingArgument(param_name)
|
||||
|
||||
# if expected type is None, skip the type checking
|
||||
if expected_type is None:
|
||||
return
|
||||
|
||||
if not isinstance(param, expected_type):
|
||||
# wrong type
|
||||
if display_param:
|
||||
raise InvalidArgument(u._('{name} ({param}) is not a {type}')
|
||||
.format(name=param_name, param=param,
|
||||
type=expected_type))
|
||||
else:
|
||||
raise InvalidArgument(u._('{name} is not a {type}')
|
||||
.format(name=param_name,
|
||||
type=expected_type))
|
||||
|
||||
|
||||
def disallow_chars(param, param_name, chars):
|
||||
if param is None:
|
||||
return
|
||||
|
||||
for char in chars:
|
||||
if char in param:
|
||||
raise InvalidArgument(
|
||||
u._('{name} contains invalid character {chars}')
|
||||
.format(name=param_name, chars=chars))
|
||||
|
||||
|
||||
def handers_action_result(job, status, verbose_level):
|
||||
if verbose_level > 2:
|
||||
LOG.info('\n\n' + 80 * '=')
|
||||
LOG.info(u._('DEBUG command output:\n{out}')
|
||||
.format(out=job.get_console_output()))
|
||||
if status == 0:
|
||||
if verbose_level > 1:
|
||||
# log any ansible warnings
|
||||
msg = job.get_error_message()
|
||||
if msg:
|
||||
LOG.warn(msg)
|
||||
LOG.info(u._('Success'))
|
||||
else:
|
||||
raise CommandError(u._('Job failed:\n{msg}')
|
||||
.format(msg=job.get_error_message()))
|
||||
|
||||
|
||||
class Lock(object):
|
||||
"""Object which represents an exclusive resource lock
|
||||
|
||||
flock usage is the default behavior but a separate pidfile mechanism
|
||||
is also available. flock doesn't have the same orphaned lock issue
|
||||
that pidfile usage does. both need to be tests on NFS. if flock
|
||||
works then it seems better / less complicated for our needs.
|
||||
"""
|
||||
|
||||
def __init__(self, lockpath=None, owner='unknown owner', use_flock=True):
|
||||
self.lockpath = lockpath or self.get_lockpath()
|
||||
self.pid = str(os.getpid())
|
||||
self.fd = None
|
||||
self.owner = owner
|
||||
self.current_pid = -1
|
||||
self.current_owner = ''
|
||||
self.use_flock = use_flock
|
||||
|
||||
def get_lockpath(self):
|
||||
return os.path.join(get_kolla_cli_home(), 'ansible.lock')
|
||||
|
||||
def acquire(self):
|
||||
try:
|
||||
if self.use_flock:
|
||||
return self._acquire_flock()
|
||||
else:
|
||||
return self._acquire_pidfile()
|
||||
except IOError as e:
|
||||
# IOError is the error you get when the file is
|
||||
# already locked. (No such file returns an OSError.)
|
||||
# This may be OK and is handled by the caller.
|
||||
LOG.debug('Exception in acquiring lock. '
|
||||
'path: %s pid: %s owner: %s error: %s' %
|
||||
(self.lockpath, self.pid, self.owner, str(e)))
|
||||
return False
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def _acquire_pidfile(self):
|
||||
if not self.is_owned_by_me():
|
||||
fd = os.open(self.lockpath, os.O_CREAT | os.O_EXCL | os.O_RDWR)
|
||||
with os.fdopen(fd, 'a') as f:
|
||||
f.write(self.pid + '\n' + self.owner)
|
||||
return self.is_owned_by_me()
|
||||
|
||||
def _acquire_flock(self):
|
||||
self.fd = os.open(self.lockpath, os.O_RDWR)
|
||||
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
return True
|
||||
|
||||
def wait_acquire(self, wait_duration=3, interval=0.1):
|
||||
wait_time = 0
|
||||
while (wait_time < wait_duration):
|
||||
if not self.acquire():
|
||||
time.sleep(interval)
|
||||
wait_time += interval
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_owned_by_me(self):
|
||||
"""Returns True if we own the lock or False otherwise"""
|
||||
try:
|
||||
if self.use_flock:
|
||||
raise Exception(u._('Invalid use of is_owned_by_me while'
|
||||
'using flock'))
|
||||
|
||||
if not os.path.exists(self.lockpath):
|
||||
# lock doesn't exist, just return
|
||||
return False
|
||||
fd = os.open(self.lockpath, os.O_RDONLY)
|
||||
with os.fdopen(fd, 'r') as f:
|
||||
contents = f.read(2048).strip().split('\n')
|
||||
if len(contents) > 0:
|
||||
self.current_pid = contents[0]
|
||||
if len(contents) > 1:
|
||||
self.current_owner = contents[1]
|
||||
|
||||
if contents[0] == str(self.pid):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
# it is ok to fail to acquire, we just return that we failed
|
||||
LOG.debug('Exception in is_owned_by_me lock check. '
|
||||
'path: %s pid: %s owner: %s error: %s' %
|
||||
(self.lockpath, self.pid, self.owner, str(e)))
|
||||
return False
|
||||
|
||||
def release(self):
|
||||
try:
|
||||
if self.use_flock:
|
||||
self._release_flock()
|
||||
else:
|
||||
self._release_pidfile()
|
||||
except OSError:
|
||||
# ignore release of an already released lock
|
||||
pass
|
||||
except Exception:
|
||||
# this really shouldn't happen unless for some reason
|
||||
# two areas in the same process try to release the lock
|
||||
# at the same time and if that happens you want to see
|
||||
# an error about it
|
||||
LOG.error('Error releasing lock', exc_info=True)
|
||||
return False
|
||||
|
||||
def _release_pidfile(self):
|
||||
if self.is_owned_by_me():
|
||||
os.remove(self.lockpath)
|
||||
return True
|
||||
|
||||
def _release_flock(self):
|
||||
try:
|
||||
fcntl.flock(self.fd, fcntl.LOCK_UN)
|
||||
except Exception as e:
|
||||
LOG.debug('Exception while releasing lock: %s' % str(e))
|
||||
finally:
|
||||
os.close(self.fd)
|
||||
return True
|
||||
|
||||
|
||||
class PidManager(object):
|
||||
@staticmethod
|
||||
def get_child_pids(pid, child_pids=[]):
|
||||
"""get child pids of parent pid"""
|
||||
# This ps command will return child pids of parent pid, separated by
|
||||
# newlines.
|
||||
err_msg, output = run_cmd('ps --ppid %s -o pid=""' % pid,
|
||||
print_output=False)
|
||||
|
||||
# err_msg is expected when pid has no children
|
||||
if not err_msg:
|
||||
output = output.strip()
|
||||
|
||||
if '\n' in output:
|
||||
ps_pids = output.split('\n')
|
||||
else:
|
||||
ps_pids = [output]
|
||||
|
||||
if ps_pids:
|
||||
child_pids.extend(ps_pids)
|
||||
|
||||
# recurse through children to get all child pids
|
||||
for ps_pid in ps_pids:
|
||||
PidManager.get_child_pids(ps_pid, child_pids)
|
||||
return child_pids
|
|
@ -1,31 +0,0 @@
|
|||
# Copyright 2012-2013 OpenStack Foundation
|
||||
#
|
||||
# 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 oslo_i18n
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='kolla-cli')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
|
@ -1,70 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 Kolla"""
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cliff.app import App
|
||||
from cliff.commandmanager import CommandManager
|
||||
|
||||
import kolla_cli.i18n as u
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.commands.exceptions import CommandError
|
||||
from kolla_cli.common.inventory import INVENTORY_PATH
|
||||
from kolla_cli.common.utils import get_kolla_cli_etc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
VERSION = '4.0'
|
||||
|
||||
|
||||
class KollaCli(App):
|
||||
def __init__(self):
|
||||
super(KollaCli, self).__init__(
|
||||
description=u._('Command-Line Client for OpenStack Kolla'),
|
||||
version=VERSION,
|
||||
command_manager=CommandManager('kolla.cli'),
|
||||
)
|
||||
|
||||
inventory_path = os.path.join(get_kolla_cli_etc(),
|
||||
INVENTORY_PATH)
|
||||
if not self._is_inventory_present(inventory_path):
|
||||
err_string = u._(
|
||||
'Required file ({inventory}) does not exist.\n'
|
||||
'Please re-install the kollacli to '
|
||||
'recreate the file.').format(inventory=inventory_path)
|
||||
raise CommandError(err_string)
|
||||
|
||||
# set up logging and test that user running shell is part
|
||||
# of kolla group
|
||||
ClientApi()
|
||||
|
||||
# paramiko log is very chatty, tune it down
|
||||
logging.getLogger('paramiko').setLevel(logging.WARNING)
|
||||
|
||||
self.dump_stack_trace = False
|
||||
|
||||
def _is_inventory_present(self, inventory_path):
|
||||
return os.path.isfile(inventory_path)
|
||||
|
||||
|
||||
def main(argv=sys.argv[1:]):
|
||||
shell = KollaCli()
|
||||
return shell.run(argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
|
@ -1,19 +0,0 @@
|
|||
- As of change id: Id11cc1abcac6ac5b94176a1f17a8f5f82b6f00d5
|
||||
removed all tests which expected remote systems
|
||||
to be available / configured. These tests should be
|
||||
revived at some point using Tempest or something similar to run
|
||||
more complete functional tests.
|
||||
|
||||
- To run a single functional test, you will need to setup these
|
||||
environmental variables and the needed file structure:
|
||||
|
||||
export KOLLA_ETC=/tmp/kollaclitest/etc/kolla/
|
||||
export KOLLA_HOME=/tmp/kollaclitest/usr/share/kolla-ansible/
|
||||
export KOLLA_TOOLS_DIR=./tools/
|
||||
|
||||
./kolla_cli/tests/functional/functional_test_setup.sh
|
||||
|
||||
Then you can run a single test, for eg:
|
||||
|
||||
source .tox/functional/bin/activate
|
||||
stestr run -n kolla_cli.tests.functional.test_deploy.TestFunctional.test_deploy
|
|
@ -1,207 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import testtools
|
||||
|
||||
import kolla_cli.common.utils as utils
|
||||
|
||||
from copy import copy
|
||||
from shutil import copyfile
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.api.exceptions import InvalidArgument
|
||||
|
||||
CLIENT = ClientApi()
|
||||
CLIENT.enable_console_logging(logging.DEBUG)
|
||||
|
||||
ARG_LIST = {
|
||||
bool: False,
|
||||
list: [1, 2, 3],
|
||||
str: 'qwerty',
|
||||
dict: {'a': 1},
|
||||
int: 0,
|
||||
}
|
||||
|
||||
KOLLACLI_CMD_PATH = 'kolla_cli/shell.py'
|
||||
|
||||
|
||||
class KollaCliTest(testtools.TestCase):
|
||||
|
||||
saved_kolla_etc = ''
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def setUp(self):
|
||||
super(KollaCliTest, self).setUp()
|
||||
|
||||
logging.basicConfig(stream=sys.stderr)
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
self.log.info('\nStarting test: %s ***********************************'
|
||||
% self._testMethodName)
|
||||
|
||||
# switch to test path
|
||||
etc_path = utils.get_kolla_cli_etc()
|
||||
self.log.debug('etc for tests: %s' % etc_path)
|
||||
|
||||
self._save_config()
|
||||
|
||||
def tearDown(self):
|
||||
super(KollaCliTest, self).tearDown()
|
||||
self._restore_config()
|
||||
|
||||
def run_cli_cmd(self, cmd, expect_error=False):
|
||||
full_cmd = ('python %s %s' % (KOLLACLI_CMD_PATH, cmd))
|
||||
self.log.debug('running command: %s' % cmd)
|
||||
(retval, msg) = self.run_command(full_cmd)
|
||||
|
||||
if not expect_error:
|
||||
self.assertEqual(0, retval, ('command failed: (%s), cmd: %s'
|
||||
% (msg, full_cmd)))
|
||||
return msg
|
||||
|
||||
def run_command(self, cmd):
|
||||
"""run bash command
|
||||
|
||||
return (retval, msg)
|
||||
"""
|
||||
# self.log.debug('run cmd: %s' % cmd)
|
||||
msg = ''
|
||||
process = subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=True)
|
||||
(out, err) = process.communicate()
|
||||
retval = process.returncode
|
||||
|
||||
# the py dev debugger adds a string at the line start, remove it
|
||||
if err:
|
||||
msg = utils.safe_decode(err)
|
||||
if out:
|
||||
msg = msg + '\n' + utils.safe_decode(out)
|
||||
if msg.startswith('pydev debugger'):
|
||||
msg = msg.split('\n', 1)[1]
|
||||
return (retval, msg)
|
||||
|
||||
def check_types(self, method, expected_types):
|
||||
# expected type is a list:
|
||||
args = []
|
||||
for arg_type in expected_types:
|
||||
args.append((arg_type, ARG_LIST[arg_type]))
|
||||
for i in range(0, len(args)):
|
||||
arg_type, _ = args[i]
|
||||
for new_arg in ARG_LIST.values():
|
||||
new_args = copy(args)
|
||||
if isinstance(new_arg, arg_type):
|
||||
# this new type is the correct arg type, skip
|
||||
continue
|
||||
# substitute a valid type for an invalid one
|
||||
new_args[i] = new_arg
|
||||
self._check_invalid_arg(method, new_args)
|
||||
|
||||
def _check_invalid_arg(self, method, args):
|
||||
assert(len(args) <= 5)
|
||||
try:
|
||||
if len(args) == 1:
|
||||
method(args[0])
|
||||
elif len(args) == 2:
|
||||
method(args[0], args[1])
|
||||
elif len(args) == 3:
|
||||
method(args[0], args[1], args[2])
|
||||
elif len(args) == 4:
|
||||
method(args[0], args[1], args[2], args[3])
|
||||
elif len(args) == 5:
|
||||
method(args[0], args[1], args[2], args[3], args[4])
|
||||
except InvalidArgument:
|
||||
# success
|
||||
return
|
||||
except Exception as e:
|
||||
self.assertTrue(False,
|
||||
'method: %s, arg: %s ' % (method, args) +
|
||||
'failed with the wrong exception: '
|
||||
'%s' % str(e))
|
||||
self.assertTrue(False, 'method: %s, arg: %s did not fail'
|
||||
% (method, args))
|
||||
|
||||
# PRIVATE FUNCTIONS ----------------------------------------------------
|
||||
def _save_config(self):
|
||||
"""save config"""
|
||||
# save inventory
|
||||
src_path = os.path.join(utils.get_kolla_cli_etc(),
|
||||
'ansible', 'inventory.json')
|
||||
dst_path = os.path.join('/tmp', 'inventory.json.utest.save')
|
||||
copyfile(src_path, dst_path)
|
||||
|
||||
# save group vars
|
||||
ansible_dir = os.path.join(utils.get_kolla_ansible_home(), 'ansible')
|
||||
groupdir = os.path.join(ansible_dir, 'group_vars')
|
||||
self._save_dir(groupdir)
|
||||
|
||||
# save host vars
|
||||
hostdir = os.path.join(ansible_dir, 'host_vars')
|
||||
self._save_dir(hostdir)
|
||||
|
||||
def _restore_config(self):
|
||||
"""restore config"""
|
||||
# restore inventory
|
||||
dst_path = os.path.join(utils.get_kolla_cli_etc(),
|
||||
'ansible', 'inventory.json')
|
||||
src_path = os.path.join('/tmp', 'inventory.json.utest.save')
|
||||
copyfile(src_path, dst_path)
|
||||
|
||||
# restore group vars
|
||||
ansible_dir = os.path.join(utils.get_kolla_ansible_home(), 'ansible')
|
||||
groupdir = os.path.join(ansible_dir, 'group_vars')
|
||||
self._restore_dir(groupdir)
|
||||
|
||||
# restore host vars
|
||||
hostdir = os.path.join(ansible_dir, 'host_vars')
|
||||
self._restore_dir(hostdir)
|
||||
|
||||
def _save_dir(self, src_dir):
|
||||
if not os.path.exists(src_dir):
|
||||
return
|
||||
dirname = os.path.basename(src_dir)
|
||||
save_dir = os.path.join('/tmp', dirname + '.utest.save')
|
||||
if os.path.exists(save_dir):
|
||||
shutil.rmtree(save_dir)
|
||||
os.mkdir(save_dir)
|
||||
fnames = os.listdir(src_dir)
|
||||
for fname in fnames:
|
||||
src_path = os.path.join(src_dir, fname)
|
||||
dst_path = os.path.join(save_dir, fname)
|
||||
copyfile(src_path, dst_path)
|
||||
|
||||
def _restore_dir(self, dst_dir):
|
||||
# we do not have privs to write these files
|
||||
ignore_list = ['all.yml']
|
||||
|
||||
dirname = os.path.basename(dst_dir)
|
||||
save_dir = os.path.join('/tmp', dirname + '.utest.save')
|
||||
sv_fnames = os.listdir(save_dir)
|
||||
fnames = os.listdir(dst_dir)
|
||||
# remove any new var files created by tests
|
||||
for fname in fnames:
|
||||
if fname not in sv_fnames:
|
||||
os.remove(os.path.join(dst_dir, fname))
|
||||
# restore saved files
|
||||
for sv_fname in sv_fnames:
|
||||
if sv_fname in ignore_list:
|
||||
continue
|
||||
src_path = os.path.join(save_dir, sv_fname)
|
||||
dst_path = os.path.join(dst_dir, sv_fname)
|
||||
copyfile(src_path, dst_path)
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Setup script for running kolla-cli functional tests.
|
||||
#
|
||||
# clean up files from last run
|
||||
rm -f $KOLLA_ETC/kolla-cli/ansible/inventory.json
|
||||
rm -f $KOLLA_HOME/ansible/group_vars/__GLOBAL__
|
||||
rm -f $KOLLA_HOME/kolla-cli/ansible.lock
|
||||
rm -f $KOLLA_ETC/passwords.yml
|
||||
|
||||
# setup the various files needed for the cli to run
|
||||
mkdir -p $KOLLA_ETC/kolla-cli/ansible
|
||||
touch $KOLLA_ETC/kolla-cli/ansible/inventory.json
|
||||
mkdir -p $KOLLA_HOME/kolla-cli
|
||||
touch $KOLLA_HOME/kolla-cli/ansible.lock
|
||||
|
||||
# If it's not there, clone the kolla-ansible repo to get its ansible directory
|
||||
# and then copy it over
|
||||
mkdir -p $KOLLA_HOME/git
|
||||
if [ ! -d $KOLLA_HOME/ansible ]; then
|
||||
git clone https://github.com/openstack/kolla-ansible $KOLLA_HOME/git
|
||||
cp -rf $KOLLA_HOME/git/ansible $KOLLA_HOME/ansible/
|
||||
fi
|
||||
|
||||
# setup needed kolla-ansible files
|
||||
cp $KOLLA_HOME/git/etc/kolla/passwords.yml $KOLLA_ETC/passwords.yml
|
||||
mkdir -p $KOLLA_HOME/ansible/host_vars
|
||||
touch $KOLLA_HOME/ansible/group_vars/__GLOBAL__
|
|
@ -1,5 +0,0 @@
|
|||
# Test ansible inventory
|
||||
[aardvark]
|
||||
|
||||
[chipmunk]
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.common.ansible_inventory import AnsibleInventory
|
||||
from kolla_cli.common.utils import get_kolla_cli_etc
|
||||
|
||||
INV_NAME = 'inventory.json'
|
||||
|
||||
CLIENT = ClientApi()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
"""Test description
|
||||
|
||||
This test will look for old version inventory files in the local current
|
||||
working directory. If none are found, it will look in the user's home
|
||||
directory. If none are found there too, it will print a warning and skip
|
||||
the test.
|
||||
|
||||
Old version inventory files must be named inventory.json.v1,
|
||||
inventory.json.v2, etc.
|
||||
|
||||
An upgrade test will be run on each old version inventory file that is
|
||||
found.
|
||||
"""
|
||||
def test_upgrade(self):
|
||||
inv_fpaths = self._find_inv_fpaths()
|
||||
if not inv_fpaths:
|
||||
LOG.warning('No old version inventory files were found. '
|
||||
'Skipping test.')
|
||||
for inv_fpath in inv_fpaths:
|
||||
version = self._get_version(inv_fpath)
|
||||
self._replace_inventory(inv_fpath)
|
||||
self._test_upgrade(version)
|
||||
|
||||
def _get_version(self, inv_fpath):
|
||||
try:
|
||||
version = int(inv_fpath.split('.v')[1])
|
||||
except Exception:
|
||||
raise Exception('Invalid version number on old inventory file: %s'
|
||||
% inv_fpath)
|
||||
return version
|
||||
|
||||
def _find_inv_fpaths(self):
|
||||
"""find old version inventories
|
||||
|
||||
Look in order at these locations:
|
||||
- current working directory
|
||||
- home directory
|
||||
"""
|
||||
fpaths = []
|
||||
search_dirs = [os.getcwd(), os.path.expanduser('~')]
|
||||
for search_dir in search_dirs:
|
||||
fpaths = self._get_inv_fpaths(search_dir)
|
||||
if fpaths:
|
||||
break
|
||||
return fpaths
|
||||
|
||||
def _get_inv_fpaths(self, inv_dir):
|
||||
upg_inventory_paths = []
|
||||
fnames = os.listdir(inv_dir)
|
||||
for fname in fnames:
|
||||
if fname.startswith(INV_NAME + '.v'):
|
||||
path = os.path.join(inv_dir, fname)
|
||||
upg_inventory_paths.append(path)
|
||||
return upg_inventory_paths
|
||||
|
||||
def _replace_inventory(self, old_version_inv_path):
|
||||
inv_path = os.path.join(get_kolla_cli_etc(),
|
||||
'ansible', 'inventory.json')
|
||||
shutil.copyfile(old_version_inv_path, inv_path)
|
||||
|
||||
def _test_upgrade(self, version):
|
||||
hostname = 'test_host_upg'
|
||||
|
||||
# This host add will cause the inventory to be upgraded
|
||||
CLIENT.host_add([hostname])
|
||||
CLIENT.host_remove([hostname])
|
||||
|
||||
# run tests for each version:
|
||||
if version <= 1:
|
||||
self._test_v1_upgrade()
|
||||
|
||||
if version <= 2:
|
||||
self._test_v2_upgrade()
|
||||
|
||||
def _test_v1_upgrade(self):
|
||||
# this is a v1 inventory
|
||||
# in v1 > v2, ceilometer was added, check that it's there
|
||||
# and verify that all ceilometer groups are in the same groups
|
||||
# as heat.
|
||||
ansible_inventory = AnsibleInventory()
|
||||
heat = CLIENT.service_get(['heat'])[0]
|
||||
expected_groups = sorted(heat.get_groups())
|
||||
ceilometer = ansible_inventory.services['ceilometer']
|
||||
expected_services = ceilometer.get_sub_servicenames()
|
||||
expected_services.append('ceilometer')
|
||||
expected_services = sorted(expected_services)
|
||||
services = CLIENT.service_get_all()
|
||||
services_found = []
|
||||
|
||||
for service in services:
|
||||
servicename = service.get_name()
|
||||
if servicename.startswith('ceilometer'):
|
||||
groups = sorted(service.get_groups())
|
||||
if servicename == 'ceilometer':
|
||||
self.assertEqual(expected_groups, groups,
|
||||
'groups mismatch between '
|
||||
'ceilometer '
|
||||
'and %s' % servicename)
|
||||
elif servicename == 'ceilometer-compute':
|
||||
self.assertEqual(['compute'], groups,
|
||||
'groups mismatch between '
|
||||
'ceilometer-compute '
|
||||
'and %s' % servicename)
|
||||
else:
|
||||
# sub-services should have no groups (they inherit)
|
||||
self.assertEqual([], groups,
|
||||
'%s has unexpected groups'
|
||||
% servicename)
|
||||
services_found.append(servicename)
|
||||
|
||||
services_found = sorted(services_found)
|
||||
self.assertEqual(expected_services, services_found,
|
||||
'ceilometer subservices mismatch')
|
||||
|
||||
def _test_v2_upgrade(self):
|
||||
# this is a v2 inventory. In the v2 to v3 upgrade, all subservices were
|
||||
# fixed up to have a parent service
|
||||
services = CLIENT.service_get_all()
|
||||
servicenames = []
|
||||
for service in services:
|
||||
servicenames.append(service.name)
|
||||
if '-' in service.name:
|
||||
# this is a subservice
|
||||
parent = service.get_parent()
|
||||
self.assertIsNotNone(parent,
|
||||
'subservice: %s, is missing its parent'
|
||||
% service.name)
|
||||
|
||||
# ceilometer-alarms were removed in kolla v3.0.1,
|
||||
# check that they're gone
|
||||
self.assertNotIn('ceilometer-alarm-evaluator', servicenames,
|
||||
'ceilometer-alarm-evaluator still exists.')
|
||||
self.assertNotIn('ceilometer-alarm-notifier', servicenames,
|
||||
'ceilometer-alarm-notifier still exists.')
|
||||
|
||||
# aodh and ceph were added in 3.0.1
|
||||
self.assertIn('aodh', servicenames, 'aodh not in inventory')
|
||||
self.assertIn('ceph', servicenames, 'ceph not in inventory')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,149 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
import unittest
|
||||
|
||||
import kolla_cli.api.properties
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_config_reset(self):
|
||||
# test global property reset
|
||||
# set a property and make sure it was set correctly
|
||||
property_dict = {'test': 'test'}
|
||||
CLIENT.property_set(property_dict)
|
||||
fetched_properties = CLIENT.property_get()
|
||||
fetched_dict = self._properties_to_dict(fetched_properties)
|
||||
self.assertIs(self._in_dict(property_dict, fetched_dict), True,
|
||||
'property set failed')
|
||||
|
||||
# now clear the config and make sure the property we just
|
||||
# set is now gone
|
||||
CLIENT.config_reset()
|
||||
fetched_properties = CLIENT.property_get()
|
||||
fetched_dict = self._properties_to_dict(fetched_properties)
|
||||
self.assertIs(self._in_dict(property_dict, fetched_dict), False,
|
||||
'global property reset config failed')
|
||||
|
||||
# test host property reset
|
||||
host_list = ['test']
|
||||
CLIENT.host_add(host_list)
|
||||
CLIENT.property_set(property_dict,
|
||||
kolla_cli.api.properties.HOST_TYPE)
|
||||
fetched_properties = CLIENT.property_get(
|
||||
kolla_cli.api.properties.HOST_TYPE, host_list)
|
||||
fetched_dict = self._properties_to_dict(fetched_properties)
|
||||
self.assertIs(self._in_dict(property_dict, fetched_dict), True,
|
||||
'host property set failed')
|
||||
|
||||
CLIENT.config_reset()
|
||||
# need to add back in the host 'test' or the property
|
||||
# get call will fail after a reset
|
||||
CLIENT.host_add(host_list)
|
||||
fetched_properties = CLIENT.property_get(
|
||||
kolla_cli.api.properties.HOST_TYPE, host_list)
|
||||
fetched_dict = self._properties_to_dict(fetched_properties)
|
||||
self.assertIs(self._in_dict(property_dict, fetched_dict), False,
|
||||
'host property reset config failed')
|
||||
|
||||
# test group property reset
|
||||
group_list = ['control']
|
||||
CLIENT.property_set(property_dict,
|
||||
kolla_cli.api.properties.GROUP_TYPE)
|
||||
fetched_properties = CLIENT.property_get(
|
||||
kolla_cli.api.properties.GROUP_TYPE, group_list)
|
||||
fetched_dict = self._properties_to_dict(fetched_properties)
|
||||
self.assertIs(self._in_dict(property_dict, fetched_dict), True,
|
||||
'group property set failed')
|
||||
|
||||
CLIENT.config_reset()
|
||||
fetched_properties = CLIENT.property_get(
|
||||
kolla_cli.api.properties.GROUP_TYPE, group_list)
|
||||
fetched_dict = self._properties_to_dict(fetched_properties)
|
||||
self.assertIs(self._in_dict(property_dict, fetched_dict), False,
|
||||
'group property reset config failed')
|
||||
|
||||
# test host reset
|
||||
# add a host and make sure it was added correctly
|
||||
host_list = ['test']
|
||||
CLIENT.host_add(host_list)
|
||||
fetched_hosts = CLIENT.host_get_all()
|
||||
fetched_list = self._hosts_to_list(fetched_hosts)
|
||||
self.assertIs(set(host_list).issubset(fetched_list), True,
|
||||
'host set failed')
|
||||
|
||||
# now clear the config and make sure the host we just
|
||||
# added is now gone
|
||||
CLIENT.config_reset()
|
||||
fetched_hosts = CLIENT.host_get_all()
|
||||
fetched_list = self._hosts_to_list(fetched_hosts)
|
||||
self.assertIs(set(host_list).issubset(fetched_list), False,
|
||||
'inventory reset config failed')
|
||||
|
||||
# need to populate the password file or many other tests will fail
|
||||
CLIENT.password_init()
|
||||
|
||||
def test_config_import_inventory(self):
|
||||
# test config import of a different inventory file
|
||||
expected_group_names = ['chipmunk', 'aardvark']
|
||||
test_inventory_path = os.path.join(
|
||||
os.getcwd(), 'kolla_cli', 'tests', 'functional',
|
||||
'inventory_test_file')
|
||||
CLIENT.config_import_inventory(file_path=test_inventory_path)
|
||||
groups = CLIENT.group_get_all()
|
||||
self.assertEqual(len(groups), len(expected_group_names))
|
||||
for group in groups:
|
||||
self.assertIn(group.name, expected_group_names)
|
||||
|
||||
# need to reset the inventory back to its defaults
|
||||
CLIENT.config_reset()
|
||||
|
||||
@staticmethod
|
||||
def _properties_to_dict(props):
|
||||
property_dict = {}
|
||||
for prop in props:
|
||||
property_dict[prop.name] = prop.value
|
||||
return property_dict
|
||||
|
||||
@staticmethod
|
||||
def _hosts_to_list(hosts):
|
||||
host_list = []
|
||||
for host in hosts:
|
||||
host_list.append(host.name)
|
||||
return host_list
|
||||
|
||||
@staticmethod
|
||||
def _in_dict(base, target):
|
||||
base_keys = base.keys()
|
||||
target_keys = target.keys()
|
||||
if set(base_keys).issubset(target_keys) is False:
|
||||
return False
|
||||
|
||||
for key in base.keys():
|
||||
target_value = target.get(key, None)
|
||||
if target_value != base[key]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,210 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.common.ansible_inventory import AnsibleInventory
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
CLIENT = ClientApi()
|
||||
UNREACHABLE = 'UNREACHABLE!'
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_json_generator(self):
|
||||
self.run_cli_cmd('setdeploy local')
|
||||
|
||||
host1 = 'host_test1'
|
||||
self.run_cli_cmd('host add %s' % host1)
|
||||
|
||||
inventory = Inventory.load()
|
||||
|
||||
path = inventory.create_json_gen_file()
|
||||
(retval, msg) = self.run_command(path)
|
||||
inventory.remove_json_gen_file(path)
|
||||
self.assertEqual(0, retval, 'json generator command failed: %s' % msg)
|
||||
|
||||
self.assertNotEqual('', msg, 'json generator returned no data: %s'
|
||||
% msg)
|
||||
|
||||
self.assertIn(host1, msg, '%s not in json_gen output: %s'
|
||||
% (host1, msg))
|
||||
|
||||
ansible_inventory = AnsibleInventory()
|
||||
services = ansible_inventory.services
|
||||
for servicename, service in services.items():
|
||||
self.assertIn(servicename, msg, '%s not in json_gen output: %s'
|
||||
% (servicename, msg))
|
||||
|
||||
# verify that json output is valid. This will throw if invalid json
|
||||
try:
|
||||
json.loads(msg)
|
||||
except Exception:
|
||||
self.assertTrue(False, 'invalid json: %s' % msg)
|
||||
remote_msg = '"ansible_ssh_user": "root"'
|
||||
local_msg = '"ansible_connection": "local"'
|
||||
|
||||
# verify that setdeploy local worked:
|
||||
self.assertIn(local_msg, msg, '%s not in local json_gen output: %s'
|
||||
% (local_msg, msg))
|
||||
self.assertNotIn(remote_msg, msg, '%s in local json_gen output: %s'
|
||||
% (remote_msg, msg))
|
||||
|
||||
# verify that setdeploy remote worked:
|
||||
CLIENT.set_deploy_mode(remote_mode=True)
|
||||
inventory = Inventory.load()
|
||||
path = inventory.create_json_gen_file()
|
||||
(retval, msg) = self.run_command(path)
|
||||
inventory.remove_json_gen_file(path)
|
||||
self.assertEqual(0, retval, 'json generator command failed: %s' % msg)
|
||||
|
||||
self.assertIn(remote_msg, msg, '%s not in remote json_gen output: %s'
|
||||
% (remote_msg, msg))
|
||||
self.assertNotIn(local_msg, msg, '%s in remote json_gen output: %s'
|
||||
% (local_msg, msg))
|
||||
|
||||
def test_json_filtering(self):
|
||||
|
||||
hosts = ['host_test1', 'host_test2', 'host_test3']
|
||||
groups = ['control', 'network', 'storage']
|
||||
|
||||
for host in hosts:
|
||||
self.run_cli_cmd('host add %s' % host)
|
||||
for groupname in groups:
|
||||
group = CLIENT.group_get([groupname])[0]
|
||||
group.add_host(host)
|
||||
|
||||
inventory = Inventory.load()
|
||||
|
||||
# filter by host- include all hosts
|
||||
inv_filter = {'deploy_hosts': hosts}
|
||||
path = inventory.create_json_gen_file(inv_filter)
|
||||
self.log.info('run command: %s' % path)
|
||||
(retval, msg) = self.run_command(path)
|
||||
inventory.remove_json_gen_file(path)
|
||||
self.assertEqual(0, retval, 'json generator command failed: %s' % msg)
|
||||
|
||||
self.check_json(msg, groups, hosts, groups, hosts)
|
||||
|
||||
# filter by host- to first host
|
||||
included_host = hosts[0]
|
||||
inv_filter = {'deploy_hosts': [included_host]}
|
||||
path = inventory.create_json_gen_file(inv_filter)
|
||||
self.log.info('run command: %s' % path)
|
||||
(retval, msg) = self.run_command(path)
|
||||
inventory.remove_json_gen_file(path)
|
||||
self.assertEqual(0, retval, 'json generator command failed: %s' % msg)
|
||||
self.check_json(msg, groups, hosts, groups, [included_host])
|
||||
|
||||
# filter by group- include all groups
|
||||
inv_filter = {'deploy_groups': groups}
|
||||
path = inventory.create_json_gen_file(inv_filter)
|
||||
self.log.info('run command: %s' % path)
|
||||
(retval, msg) = self.run_command(path)
|
||||
inventory.remove_json_gen_file(path)
|
||||
self.assertEqual(0, retval, 'json generator command failed: %s' % msg)
|
||||
self.check_json(msg, groups, hosts, groups, hosts)
|
||||
|
||||
# filter by group- to first group
|
||||
included_group = groups[0]
|
||||
inv_filter = {'deploy_groups': [included_group]}
|
||||
path = inventory.create_json_gen_file(inv_filter)
|
||||
self.log.info('run command: %s' % path)
|
||||
(retval, msg) = self.run_command(path)
|
||||
inventory.remove_json_gen_file(path)
|
||||
self.assertEqual(0, retval, 'json generator command failed: %s' % msg)
|
||||
self.check_json(msg, groups, hosts, [included_group], hosts)
|
||||
|
||||
def test_deploy(self):
|
||||
# test will start with no hosts in the inventory
|
||||
# deploy will throw an exception if it fails
|
||||
# disable all services first as without it empty groups cause errors
|
||||
enable_service_props = {}
|
||||
for service in CLIENT.service_get_all():
|
||||
service_name = service.name.replace('-', '_')
|
||||
enable_service_props['enable_%s' % service_name] = 'no'
|
||||
CLIENT.property_set(enable_service_props)
|
||||
|
||||
msg = ''
|
||||
CLIENT.set_deploy_mode(remote_mode=False)
|
||||
job = CLIENT.deploy(hostnames=[])
|
||||
job.wait()
|
||||
msg = job.get_console_output()
|
||||
self.assertEqual(job.get_status(), 0,
|
||||
'error performing whole host deploy %s' % msg)
|
||||
|
||||
# test deploy with timeout
|
||||
msg = self.run_cli_cmd('action deploy --timeout .001',
|
||||
expect_error=True)
|
||||
self.assertIn('timed out', msg)
|
||||
|
||||
def test_upgrade(self):
|
||||
# test will upgrade an environment with no hosts, mostly a NOP,
|
||||
# but it will go through the client code paths.
|
||||
self.run_cli_cmd('action upgrade -v')
|
||||
|
||||
msg = ''
|
||||
# run rabbitmq service deploy
|
||||
try:
|
||||
CLIENT.host_add(['dummy_host'])
|
||||
CLIENT.set_deploy_mode(remote_mode=True)
|
||||
job = CLIENT.upgrade()
|
||||
job.wait()
|
||||
msg = job.get_console_output()
|
||||
self.assertEqual(job.get_status(), 1,
|
||||
'upgrade succeeded unexpectedly: %s'
|
||||
% msg)
|
||||
self.assertIn(UNREACHABLE, msg)
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
CLIENT.host_remove(['dummy_host'])
|
||||
|
||||
def test_pull(self):
|
||||
# test will upgrade an environment with no hosts, mostly a NOP,
|
||||
# but it will go through the client code paths.
|
||||
self.run_cli_cmd('action pull -v')
|
||||
|
||||
CLIENT.host_add(['test_pull_host'])
|
||||
CLIENT.set_deploy_mode(remote_mode=True)
|
||||
job = CLIENT.pull()
|
||||
job.wait()
|
||||
msg = job.get_console_output()
|
||||
self.assertEqual(job.get_status(), 1,
|
||||
'pull succeeded unexpectedly: %s'
|
||||
% msg)
|
||||
self.assertIn(UNREACHABLE, msg)
|
||||
|
||||
def check_json(self, msg, groups, hosts, included_groups, included_hosts):
|
||||
err_msg = ('included groups: %s\n' % included_groups +
|
||||
'included hosts: %s\n' % included_hosts)
|
||||
inv_dict = json.loads(msg)
|
||||
for group in groups:
|
||||
group_hosts = inv_dict[group]['hosts']
|
||||
for host in hosts:
|
||||
if group in included_groups and host in included_hosts:
|
||||
self.assertIn(host, group_hosts, err_msg +
|
||||
'%s not in %s' % (host, group_hosts))
|
||||
else:
|
||||
self.assertNotIn(host, group_hosts, err_msg +
|
||||
'%s still in %s' % (host, group_hosts))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,97 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
|
||||
import random
|
||||
import time
|
||||
import unittest
|
||||
|
||||
TEST_GROUP_NAME = 'test_group'
|
||||
CLIENT = ClientApi()
|
||||
|
||||
NOT_KNOWN = 'Name or service not known'
|
||||
UNREACHABLE = 'UNREACHABLE!'
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_destroy(self):
|
||||
|
||||
# use a non-existent host.
|
||||
# This will generate expected exceptions in all host access
|
||||
# commands.
|
||||
hostnames = ['test_deploy_host1']
|
||||
CLIENT.host_add(hostnames)
|
||||
|
||||
# add host to a new deploy group
|
||||
CLIENT.group_add([TEST_GROUP_NAME])
|
||||
group = CLIENT.group_get([TEST_GROUP_NAME])[0]
|
||||
for hostname in hostnames:
|
||||
group.add_host(hostname)
|
||||
|
||||
# destroy services, initialize server
|
||||
self.log.info('Start destroy #1')
|
||||
job = CLIENT.host_destroy(hostnames, destroy_type='kill',
|
||||
include_data=True, verbose_level=2)
|
||||
self._process_job(job, 'destroy #1')
|
||||
|
||||
self.log.info('updating various properties for the test')
|
||||
|
||||
# the following deploy will fail if we don't disable all the
|
||||
# services first
|
||||
enable_service_props = {}
|
||||
for service in CLIENT.service_get_all():
|
||||
service_name = service.name.replace('-', '_')
|
||||
enable_service_props['enable_%s' % service_name] = 'no'
|
||||
CLIENT.property_set(enable_service_props)
|
||||
|
||||
# test killing a deploy
|
||||
self.log.info('Kill a deployment')
|
||||
job = CLIENT.deploy()
|
||||
time.sleep(random.randint(5, 8))
|
||||
job.kill()
|
||||
self._process_job(job, 'deploy-kill',
|
||||
expect_kill=True)
|
||||
|
||||
# do a deploy of a limited set of services
|
||||
self.log.info('Start a deployment')
|
||||
job = CLIENT.deploy()
|
||||
self._process_job(job, 'deploy')
|
||||
|
||||
self.log.info('Start destroy #3, include data')
|
||||
job = CLIENT.host_destroy(hostnames, destroy_type='stop',
|
||||
include_data=True)
|
||||
self._process_job(job, 'destroy #3')
|
||||
|
||||
def _process_job(self, job, descr, expect_kill=False):
|
||||
status = job.wait()
|
||||
output = job.get_console_output()
|
||||
self.log.info('job is complete. status: %s, err: %s'
|
||||
% (status, output))
|
||||
if expect_kill:
|
||||
self.assertEqual(2, status, 'Job %s does not have killed status %s'
|
||||
% (descr, output))
|
||||
else:
|
||||
self.assertEqual(1, status, 'Job %s ' % descr +
|
||||
'succeeded when it should have failed')
|
||||
self.assertIn(UNREACHABLE, output,
|
||||
'Job %s: No hosts, but got wrong error: %s'
|
||||
% (descr, output))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,246 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.api.exceptions import NotInInventory
|
||||
from kolla_cli.common.ansible_inventory import AnsibleInventory
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_group_add_remove(self):
|
||||
groups = self.get_default_groups()
|
||||
|
||||
# check default group list
|
||||
self.check_group(groups)
|
||||
|
||||
tg1 = 'test_group_t1'
|
||||
tg2 = 'test_group_t2'
|
||||
|
||||
self.run_cli_cmd('group add %s' % tg1)
|
||||
groups[tg1] = {
|
||||
'Services': [],
|
||||
'Hosts': []}
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('group add %s' % tg2)
|
||||
groups[tg2] = {
|
||||
'Services': [],
|
||||
'Hosts': []}
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('group remove %s' % tg2)
|
||||
del groups[tg2]
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('group remove %s' % tg1)
|
||||
del groups[tg1]
|
||||
self.check_group(groups)
|
||||
|
||||
def test_group_add_host(self):
|
||||
groups = self.get_default_groups()
|
||||
|
||||
host1 = 'test_host1'
|
||||
host2 = 'test_host2'
|
||||
groupname = 'control'
|
||||
|
||||
group = groups[groupname]
|
||||
hosts = group['Hosts']
|
||||
|
||||
self.run_cli_cmd('host add %s' % host1)
|
||||
|
||||
hosts.append(host1)
|
||||
self.run_cli_cmd('group addhost %s %s' % (groupname, host1))
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('host add %s' % host2)
|
||||
|
||||
hosts.append(host2)
|
||||
self.run_cli_cmd('group addhost %s %s' % (groupname, host2))
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('group removehost %s %s' % (groupname, host2))
|
||||
hosts.remove(host2)
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('group removehost %s %s' % (groupname, host1))
|
||||
hosts.remove(host1)
|
||||
self.check_group(groups)
|
||||
|
||||
def test_add_group_to_service(self):
|
||||
groups = self.get_default_groups()
|
||||
|
||||
groupname = 'network'
|
||||
service1 = 'keystone'
|
||||
service2 = 'heat-api'
|
||||
|
||||
self.run_cli_cmd('service addgroup %s %s' % (service1, groupname))
|
||||
groups[groupname]['Services'].append(service1)
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('service addgroup %s %s' % (service2, groupname))
|
||||
groups[groupname]['Services'].append(service2)
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('service removegroup %s %s'
|
||||
% (service2, groupname))
|
||||
groups[groupname]['Services'].remove(service2)
|
||||
self.check_group(groups)
|
||||
|
||||
self.run_cli_cmd('service removegroup %s %s'
|
||||
% (service1, groupname))
|
||||
groups[groupname]['Services'].remove(service1)
|
||||
self.check_group(groups)
|
||||
|
||||
def test_group_api(self):
|
||||
# check some of the api not exercised by the CLI
|
||||
groupname1 = 'group_test1'
|
||||
groupname2 = 'group_test2'
|
||||
exp_groups = sorted([groupname1, groupname1, groupname2])
|
||||
CLIENT.group_add(exp_groups)
|
||||
groups = CLIENT.group_get([groupname1])
|
||||
groupnames = []
|
||||
for group in groups:
|
||||
groupnames.append(group.name)
|
||||
self.assertIn(groupname1, groupnames, 'group %s is missing in %s'
|
||||
% (groupname1, groupnames))
|
||||
self.assertNotIn(groupname2, groupnames, 'group %s is unexpectedly in '
|
||||
'%s' % (groupname2, groupnames))
|
||||
groups = CLIENT.group_get(exp_groups)
|
||||
groupnames = []
|
||||
for group in groups:
|
||||
groupnames.append(group.name)
|
||||
self.assertEqual(exp_groups, sorted(groupnames), 'groups mismatch')
|
||||
|
||||
CLIENT.group_remove(exp_groups)
|
||||
try:
|
||||
CLIENT.group_get(exp_groups)
|
||||
self.assertTrue(False, 'Failed to raise NotInInventory exception')
|
||||
except NotInInventory:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
hostname1 = 'testhost1'
|
||||
CLIENT.group_add([groupname1])
|
||||
group1 = CLIENT.group_get([groupname1])[0]
|
||||
CLIENT.host_add([hostname1])
|
||||
group1.add_host(hostname1)
|
||||
hostnames = group1.get_hosts()
|
||||
self.assertIn(hostname1, hostnames, 'missing hostname')
|
||||
group1.add_service('nova')
|
||||
servicenames = group1.get_services()
|
||||
self.assertIn('nova', servicenames, 'missing servicename')
|
||||
|
||||
# check the type checking logic
|
||||
self.check_types(CLIENT.group_add, [list])
|
||||
self.check_types(CLIENT.group_remove, [list])
|
||||
|
||||
def check_group(self, groups):
|
||||
"""check groups
|
||||
|
||||
group listhosts -f json:
|
||||
[{"Group Name": "compute", "Hosts": []},
|
||||
{"Group Name": "control", "Hosts": ["ub-target1"]},
|
||||
{"Group Name": "network", "Hosts": []}]
|
||||
|
||||
group listservices -f json:
|
||||
[{"Group Name": "compute", "Services": []},
|
||||
{"Group Name": "control",
|
||||
"Services": ["glance", "keystone", "mysqlcluster",
|
||||
"nova", "rabbitmq"]},
|
||||
{"Group Name": "network",
|
||||
"Services": ["haproxy", "neutron"]}]
|
||||
"""
|
||||
# check hosts in groups
|
||||
msg = self.run_cli_cmd('group listhosts -f json')
|
||||
cli_groups = json.loads(msg)
|
||||
self.assertEqual(len(cli_groups), len(groups),
|
||||
'# of groups in cli not equal to expected groups.' +
|
||||
'\n\nexpected: %s, \n\ncli: %s'
|
||||
% (groups, cli_groups))
|
||||
|
||||
for cli_group in cli_groups:
|
||||
cli_hosts = cli_group['Hosts']
|
||||
for group_name, info in groups.items():
|
||||
if group_name != cli_group['Group']:
|
||||
continue
|
||||
group_hosts = info['Hosts']
|
||||
self.assertEqual(len(cli_hosts), len(group_hosts),
|
||||
'Group: %s. # of hosts in cli ' % group_name +
|
||||
'not equal to expected hosts, ' +
|
||||
'\n\nexpected: %s, \n\ncli: %s'
|
||||
% (group_hosts, cli_hosts))
|
||||
|
||||
for group_host in group_hosts:
|
||||
self.assertIn(group_host, cli_hosts,
|
||||
'Group: %s' % group_name +
|
||||
'\n\nexpected_hosts: %s, \n\nnot in cli: %s '
|
||||
% (group_host, cli_hosts))
|
||||
|
||||
# check services in group
|
||||
msg = self.run_cli_cmd('group listservices -f json')
|
||||
cli_groups = json.loads(msg)
|
||||
|
||||
for cli_group in cli_groups:
|
||||
cli_services = cli_group['Services']
|
||||
for group_name, info in groups.items():
|
||||
if group_name != cli_group['Group']:
|
||||
continue
|
||||
group_services = info['Services']
|
||||
self.assertEqual(len(cli_services), len(group_services),
|
||||
'Group: %s. # of services in cli'
|
||||
% group_name +
|
||||
' not equal to expected services,' +
|
||||
'\nexpected: %s, \ncli: %s'
|
||||
% (sorted(group_services),
|
||||
sorted(cli_services)))
|
||||
for group_service in group_services:
|
||||
self.assertIn(group_service, cli_services,
|
||||
'Group: %s' % group_name +
|
||||
'\nexpected_services: %s, \nnot in cli: %s '
|
||||
% (sorted(group_services),
|
||||
sorted(cli_services)))
|
||||
|
||||
def get_default_groups(self):
|
||||
"""get default groups
|
||||
|
||||
return a dict:
|
||||
{groupname: {
|
||||
Services: [svc1, svc2...],
|
||||
Hosts: []}}
|
||||
"""
|
||||
ansible_inventory = AnsibleInventory()
|
||||
groupnames = ansible_inventory.groups
|
||||
groups = {}
|
||||
for groupname in groupnames:
|
||||
groups[groupname] = {'Services': [],
|
||||
'Hosts': []}
|
||||
|
||||
for servicename, service in ansible_inventory.services.items():
|
||||
if groupname in service.get_groupnames():
|
||||
groups[groupname]['Services'].append(servicename)
|
||||
|
||||
return groups
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,196 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
import yaml
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.api.exceptions import NotInInventory
|
||||
from kolla_cli.api.host import Host
|
||||
|
||||
TEST_YML_FNAME = 'unittest_hosts_setup.yml'
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_host_add_remove(self):
|
||||
hosts = {}
|
||||
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
host1 = 'host_test1'
|
||||
host2 = 'host_test2'
|
||||
|
||||
group1 = 'control'
|
||||
|
||||
hosts[host1] = Host(host1)
|
||||
self.run_cli_cmd('host add %s' % host1)
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
hosts[host2] = Host(host2)
|
||||
self.run_cli_cmd('host add %s' % host2)
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
del hosts[host2]
|
||||
self.run_cli_cmd('host remove %s' % host2)
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
del hosts[host1]
|
||||
self.run_cli_cmd('host remove %s' % host1)
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
# check remove all
|
||||
hosts[host1] = Host(host1)
|
||||
self.run_cli_cmd('host add %s' % host1)
|
||||
hosts[host2] = Host(host2)
|
||||
self.run_cli_cmd('host add %s' % host2)
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
hosts.clear()
|
||||
self.run_cli_cmd('host remove all')
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
# check groups in host list
|
||||
hosts[host1] = Host(host1, [group1])
|
||||
self.run_cli_cmd('host add %s' % host1)
|
||||
self.run_cli_cmd('group addhost %s %s' % (group1, host1))
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
# removing group by resetting host1 to new Host with no groups
|
||||
hosts[host1] = Host(host1)
|
||||
self.run_cli_cmd('group removehost %s %s' % (group1, host1))
|
||||
msg = self.run_cli_cmd('host list -f json')
|
||||
self._check_cli_output(hosts, msg)
|
||||
|
||||
def test_host_api(self):
|
||||
# check some of the api not exercised by the CLI
|
||||
host1 = 'host_test1'
|
||||
host2 = 'host_test2'
|
||||
exp_hosts = sorted([host1, host2])
|
||||
CLIENT.host_add(exp_hosts)
|
||||
hosts = CLIENT.host_get([host1])
|
||||
hostnames = []
|
||||
for host in hosts:
|
||||
hostnames.append(host.name)
|
||||
self.assertIn(host1, hostnames, 'host %s is missing in %s'
|
||||
% (host1, hostnames))
|
||||
self.assertNotIn(host2, hostnames, 'host %s is unexpectedly in %s'
|
||||
% (host2, hostnames))
|
||||
hosts = CLIENT.host_get(exp_hosts)
|
||||
hostnames = []
|
||||
for host in hosts:
|
||||
hostnames.append(host.name)
|
||||
self.assertEqual(exp_hosts, sorted(hostnames), 'hosts mismatch')
|
||||
|
||||
CLIENT.host_remove(exp_hosts)
|
||||
try:
|
||||
CLIENT.host_get(exp_hosts)
|
||||
self.assertTrue(False, 'Failed to raise NotInInventory exception')
|
||||
except NotInInventory:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
# check the type checking logic
|
||||
self.check_types(CLIENT.host_add, [list])
|
||||
self.check_types(CLIENT.host_remove, [list])
|
||||
self.check_types(CLIENT.host_setup, [dict])
|
||||
self.check_types(CLIENT.host_ssh_check, [list])
|
||||
self.check_types(CLIENT.host_destroy, [list, str, int, bool])
|
||||
|
||||
def test_host_list_nonascii(self):
|
||||
hostname = 'host_test1'
|
||||
CLIENT.host_add([hostname])
|
||||
|
||||
# this is a groupname in cyrillic chars
|
||||
groupname1 = u'\u0414\u0435\u043a\u0430\u0442'
|
||||
groupname2 = 'test_group2' # ascii groupname
|
||||
groupnames = [groupname1, groupname2]
|
||||
CLIENT.group_add(groupnames)
|
||||
groups = CLIENT.group_get(groupnames)
|
||||
for group in groups:
|
||||
group.add_host(hostname)
|
||||
|
||||
# TODO(bmace) -- test currently broken
|
||||
# msg = self.run_cli_cmd('host list')
|
||||
# self.assertIn(groupname1, msg)
|
||||
# self.assertNotIn("u'\u0414\u0435\u043a\u0430\u0442'", msg, 'groupname '
|
||||
# 'incorrectly appearing as unicode bytes in output')
|
||||
# self.assertNotIn("u'test_group2'", msg, 'unicode escape text is '
|
||||
# 'incorrectly displayed in host list output')
|
||||
|
||||
def _check_cli_output(self, exp_hosts, cli_output):
|
||||
"""Verify cli data against model data
|
||||
|
||||
The host list cli output looks like this:
|
||||
|
||||
$ host list -f json
|
||||
[{"Host": "foo", "Groups": ["control", "network"]}]
|
||||
"""
|
||||
# check for any host in cli output that shouldn't be there
|
||||
cli_hosts = json.loads(cli_output)
|
||||
|
||||
if not exp_hosts:
|
||||
if len(cli_hosts) == 1:
|
||||
cli_hostname = cli_hosts[0]['Host']
|
||||
if not cli_hostname:
|
||||
# both cli and expected hosts are None
|
||||
return
|
||||
|
||||
for cli_host in cli_hosts:
|
||||
cli_hostname = cli_host['Host']
|
||||
self.assertIn(cli_hostname, exp_hosts,
|
||||
'unexpected host: %s, found in cli output: %s'
|
||||
% (cli_hostname, cli_output))
|
||||
|
||||
# check that all expected hosts are in the output
|
||||
for exp_host in exp_hosts.values():
|
||||
exp_host_found = False
|
||||
for cli_host in cli_hosts:
|
||||
if exp_host.get_name() == cli_host['Host']:
|
||||
exp_host_found = True
|
||||
cli_groups = cli_host['Groups']
|
||||
exp_groups = exp_host.get_groups()
|
||||
self.assertEqual(exp_groups, cli_groups)
|
||||
|
||||
self.assertTrue(exp_host_found,
|
||||
'hostname: %s not in cli output: \n%s'
|
||||
% (exp_host.get_name(), cli_output))
|
||||
|
||||
def write_yml(self, yml_dict):
|
||||
yml = yaml.dump(yml_dict)
|
||||
with open(self.get_yml_path(), 'w') as yml_file:
|
||||
yml_file.write(yml)
|
||||
|
||||
def get_yml_path(self):
|
||||
home = os.path.expanduser('~')
|
||||
return os.path.join(home, TEST_YML_FNAME)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,136 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 os
|
||||
import unittest
|
||||
|
||||
from kolla_cli.api import client
|
||||
from kolla_cli.common.utils import get_kolla_etc
|
||||
from kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
CLIENT = client.ClientApi()
|
||||
|
||||
PUBLIC_KEY = (
|
||||
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqusDp5jpkbng3sRue8gZV6/PCQp9'
|
||||
'ogUd5/OZ3sh9VgdaigHoYUfXZElTZLlkL71tD9WZJr69PDwmG/nE4quba8rLcDY2wC0'
|
||||
'qjq+r06ExhlRu4ivy7OxT29s8FSe8Uht9Pz8ahnXxddLF55yTbC81XrSXDBFc6Nnogz'
|
||||
'+g6GgXVKtwTkm5g3K+qix5zVECu8zzawBR/s+v0dkDxKwSY8XOG6JZlMUndaDaikZZi'
|
||||
'qp8KAOJpajM77aCfDkY3VZGFBCJiEGLVDhFrtXuBI9I0YzX4j9pZZWpSzkM/FwlPjDR'
|
||||
'SW1C9MAAFLoEQTN4j1Z5hkDNXDsr49wJBi+jjQ0FPMMvfJktrznRuO2fUa9W2iilOrv'
|
||||
'1PyrknssmW1iYXiWJ5Bq8A9sKE1r7Nbdjhcjskp77X57tNjtarRUcj3FqGjC8pv+k92'
|
||||
'9Y+FvkXbjpBsHpdMFh8BlM+EnwnsjkiQpmjLv8bpeeQooLyQQmZn94zY73bbGrsjzXe'
|
||||
'OhOTDnKAS14hxCnBlEbudHB4erp/5Nj+A8UVAT0KXPM+mkDrum/dsvV0wnvBicAVt/a'
|
||||
'tmkwDKJqXDmj4elNe8/jTXSYHpTDo29xtcGpka9AtWarmnt8QkRuieD1xSXsEUQswjq'
|
||||
'aQD2ikitKt/hEyCmT+7fy4yYKK35kukUj5qV85A8O/hOYf5vFjtRw==')
|
||||
|
||||
PRIVATE_KEY = (
|
||||
'-----BEGIN RSA PRIVATE KEY-----\n'
|
||||
'MIIJKAIBAAKCAgEAqrrA6eY6ZG54N7EbnvIGVevzwkKfaIFHefzmd7IfVYHWooB6\n'
|
||||
'GFH12RJU2S5ZC+9bQ/VmSa+vTw8Jhv5xOKrm2vKy3A2NsAtKo6vq9OhMYZUbuIr8\n'
|
||||
'uzsU9vbPBUnvFIbfT8/GoZ18XXSxeeck2wvNV60lwwRXOjZ6IPoOhoF1SrcE5JuY\n'
|
||||
'Nyvqosec1RArvM82sAUf7Pr9HZA8SsEmPFzhuiWZTFJ3Wg2opGWYqqfCgDiaWozO\n'
|
||||
'+2gnw5GN1WRhQQiYhBi1Q4Ra7V7gSPSNGM1+I/aWWVqUs5DPxcJT4w0UltQvTAAB\n'
|
||||
'S6BEEzeI9WeYZAzVw7K+PcCQYvo40NBTzDL3yZLa850bjtn1GvVtoopTq79T8q5J\n'
|
||||
'7LJltYmF4lieQavAPbChNa+zW3Y4XI7JKe+1+e7TY7Wq0VHI9xahowvKb/pPdvWP\n'
|
||||
'hb5F246QbB6XTBYfAZTPhJ8J7I5IkKZoy7/G6XnkKKC8kEJmZ/eM2O922xq7I813\n'
|
||||
'joTkw5ygEteIcQpwZRG7nRweHq6f+TY/gPFFQE9ClzzPppA67pv3bL1dMJ7wYnAF\n'
|
||||
'69VedCYMSoYIHpcN80w9it/6Cfm8niAy3v9e0icSVEsvkzcV6eFjLggY1DQ9WBPN\n'
|
||||
'MR4LKGNDuxEWeZAQi+A6Ejclx1KKBhL/E4SNj3ev4/5glaMjzSIUpA4415o=\n'
|
||||
'-----END RSA PRIVATE KEY-----'
|
||||
)
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_password_set_clear(self):
|
||||
# test list
|
||||
msg = self.run_cli_cmd('password list')
|
||||
key = 'database_password'
|
||||
value = '-'
|
||||
ok = self._password_value_exists(key, value, msg)
|
||||
self.assertTrue(ok, 'list failed. Password (%s/%s) not in output: %s'
|
||||
% (key, value, msg))
|
||||
|
||||
# test setting empty password
|
||||
self.run_cli_cmd('password set %s --insecure' % key)
|
||||
msg = self.run_cli_cmd('password list')
|
||||
ok = self._password_value_exists(key, '-', msg)
|
||||
self.assertTrue(ok, 'set empty password failed. Password ' +
|
||||
'(%s/-) not in output: %s' %
|
||||
(key, msg))
|
||||
|
||||
# test setting None password
|
||||
CLIENT.password_set(key, None)
|
||||
msg = self.run_cli_cmd('password list')
|
||||
ok = self._password_value_exists(key, '-', msg)
|
||||
self.assertTrue(ok, 'set None password failed. Password ' +
|
||||
'(%s/-) not in output: %s' %
|
||||
(key, msg))
|
||||
|
||||
# test clear
|
||||
key = 'database_password'
|
||||
value = '-'
|
||||
self.run_cli_cmd('password clear %s' % key)
|
||||
msg = self.run_cli_cmd('password list')
|
||||
ok = self._password_value_exists(key, value, msg)
|
||||
self.assertTrue(ok, 'clear password failed. Password ' +
|
||||
'(%s/%s) not in output: %s' %
|
||||
(key, value, msg))
|
||||
|
||||
# test setting an ssh key
|
||||
key = 'nova_ssh_key'
|
||||
CLIENT.password_set_sshkey(key, PRIVATE_KEY, PUBLIC_KEY)
|
||||
keynames = CLIENT.password_get_names()
|
||||
self.assertIn(key, keynames, 'ssh key not in passwords')
|
||||
|
||||
# test modify non-ssh password
|
||||
key = 'database_password'
|
||||
value = '-'
|
||||
self.run_cli_cmd('password set %s --insecure %s' % (key, value))
|
||||
msg = self.run_cli_cmd('password list')
|
||||
ok = self._password_value_exists(key, value, msg)
|
||||
self.assertTrue(ok, 'set modify password failed. Password ' +
|
||||
'(%s/%s) not in output: %s' %
|
||||
(key, value, msg))
|
||||
|
||||
# test to make sure that saves / loads aren't doing something
|
||||
# bad to the password file size
|
||||
CLIENT.password_clear(key)
|
||||
# snapshot file size with key cleared
|
||||
password_file_path = os.path.join(get_kolla_etc(), 'passwords.yml')
|
||||
size_start = os.path.getsize(password_file_path)
|
||||
# set and clear password
|
||||
CLIENT.password_set(key, value)
|
||||
CLIENT.password_clear(key)
|
||||
size_end = os.path.getsize(password_file_path)
|
||||
self.assertEqual(size_start, size_end, 'password file size changed ' +
|
||||
'during set/clear (%s/%s)' % (size_start, size_end))
|
||||
|
||||
# make sure to end the test with the password init, as some other
|
||||
# non-password related tests require that all passwords in the file
|
||||
# be populated
|
||||
CLIENT.password_init()
|
||||
|
||||
def _password_value_exists(self, key, value, cli_output):
|
||||
"""Verify cli data against model data"""
|
||||
# check for any host in cli output that shouldn't be there
|
||||
cli_lines = cli_output.split('\n')
|
||||
for cli_line in cli_lines:
|
||||
if key in cli_line and value in cli_line:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,294 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from kolla_cli.common.utils import get_group_vars_dir
|
||||
from kolla_cli.common.utils import get_host_vars_dir
|
||||
from kolla_cli.common.utils import get_kolla_ansible_home
|
||||
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_properties(self):
|
||||
# test global properties
|
||||
self._properties_test()
|
||||
|
||||
# test single group vars
|
||||
group = 'prop_test_group1'
|
||||
self.run_cli_cmd('group add %s' % group)
|
||||
self._properties_test(groups=['control'])
|
||||
|
||||
# test single host vars
|
||||
host = 'prop_test_host1'
|
||||
self.run_cli_cmd('host add %s' % host)
|
||||
self._properties_test(hosts=[host])
|
||||
|
||||
# test multiple group vars
|
||||
groups = [group]
|
||||
group = 'prop_test_group2'
|
||||
groups.append(group)
|
||||
self.run_cli_cmd('group add %s' % group)
|
||||
self._properties_test(groups=groups)
|
||||
|
||||
# test multiple host vars
|
||||
hosts = [host]
|
||||
host = 'prop_test_host2'
|
||||
hosts.append(host)
|
||||
self.run_cli_cmd('host add %s' % host)
|
||||
self._properties_test(hosts=hosts)
|
||||
|
||||
# test all group vars
|
||||
self._properties_test(groups=['all'])
|
||||
|
||||
# test all host vars
|
||||
self._properties_test(hosts=['all'])
|
||||
|
||||
# test property override output
|
||||
ovr_key = 'enable_haproxy'
|
||||
ovr_value = 'no'
|
||||
|
||||
# clear property values before test
|
||||
self.run_cli_cmd('property clear %s' % ovr_key)
|
||||
self.run_cli_cmd('property clear %s --host=all' % ovr_key)
|
||||
self.run_cli_cmd('property clear %s --group=all' % ovr_key)
|
||||
|
||||
# global property override test
|
||||
self.run_cli_cmd('property set %s %s' % (ovr_key, ovr_value))
|
||||
json_str = self.run_cli_cmd('property list -f json')
|
||||
msg = self._override_test(json_str, ovr_key, ovr_value, '*--')
|
||||
self.assertEqual(msg, '', 'override check failed: %s' % msg)
|
||||
|
||||
# host property override test
|
||||
self.run_cli_cmd('property set %s %s --host=%s' %
|
||||
(ovr_key, ovr_value, host))
|
||||
json_str = self.run_cli_cmd('property list -f json --host=%s' % host)
|
||||
msg = self._override_test(json_str, ovr_key,
|
||||
ovr_value, '*-H', host=host)
|
||||
self.assertEqual(msg, '', 'host override check failed: %s' % msg)
|
||||
|
||||
# group property override test
|
||||
self.run_cli_cmd('property set %s %s --group=%s' %
|
||||
(ovr_key, ovr_value, group))
|
||||
json_str = self.run_cli_cmd('property list -f json --group=%s' % group)
|
||||
msg = self._override_test(json_str, ovr_key,
|
||||
ovr_value, '*GH', group=group)
|
||||
self.assertEqual(msg, '', 'group override check failed: %s' % msg)
|
||||
|
||||
# check that group_var files are deleted
|
||||
# when groups are deleted
|
||||
for group in groups:
|
||||
path = os.path.join(get_group_vars_dir(), group)
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.run_cli_cmd('group remove %s' % group)
|
||||
self.assertFalse(os.path.exists(path))
|
||||
|
||||
# check that host_var files are deleted
|
||||
# when hosts are deleted
|
||||
for host in hosts:
|
||||
path = os.path.join(get_host_vars_dir(), host)
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.run_cli_cmd('host remove %s' % host)
|
||||
self.assertFalse(os.path.exists(path))
|
||||
|
||||
def _properties_test(self, groups=[], hosts=[]):
|
||||
switch = ''
|
||||
if groups:
|
||||
switch = '--groups'
|
||||
dir_name = 'group_vars'
|
||||
elif hosts:
|
||||
switch = '--hosts'
|
||||
dir_name = 'host_vars'
|
||||
|
||||
key = 'TeStKeY'
|
||||
value = 'TeStVaLuE:123:abc'
|
||||
|
||||
# initialize keys
|
||||
targets_csv = ''
|
||||
targets = groups + hosts
|
||||
if 'all' in groups:
|
||||
inv = Inventory.load()
|
||||
targets = inv.get_groupnames()
|
||||
targets_csv = 'all'
|
||||
elif 'all' in hosts:
|
||||
inv = Inventory.load()
|
||||
targets = inv.get_hostnames()
|
||||
targets_csv = 'all'
|
||||
|
||||
comma = ''
|
||||
sizes = {} # key = path, value = [size1, size2, etc]
|
||||
for target in targets:
|
||||
self.run_cli_cmd('property clear %s %s %s'
|
||||
% (switch, target, key))
|
||||
if targets_csv != 'all':
|
||||
targets_csv += comma + target
|
||||
comma = ','
|
||||
path = os.path.join(get_kolla_ansible_home(),
|
||||
'ansible', dir_name, target)
|
||||
sizes[path] = [os.path.getsize(path)]
|
||||
if not switch:
|
||||
self.run_cli_cmd('property clear %s' % key)
|
||||
path = os.path.join(get_kolla_ansible_home(),
|
||||
'ansible/group_vars/__GLOBAL__')
|
||||
sizes[path] = [os.path.getsize(path)]
|
||||
|
||||
# test append
|
||||
self.run_cli_cmd('property set %s %s %s %s'
|
||||
% (switch, targets_csv, key, value))
|
||||
if switch:
|
||||
msg = self.run_cli_cmd('property list -f json %s all'
|
||||
% (switch))
|
||||
else:
|
||||
msg = self.run_cli_cmd('property list -f json')
|
||||
err_msg = self._check_property_values(key, value, msg, targets)
|
||||
self.assertEqual(err_msg, '',
|
||||
'set failed property not in output: %s, %s (%s %s)'
|
||||
% (key, value, switch, targets_csv))
|
||||
|
||||
bad_path = self._is_size_ok(sizes, 0, '<', 1)
|
||||
self.assertIsNone(bad_path, 'Size of file %s did not ' % bad_path +
|
||||
'increase after append (%s %s)'
|
||||
% (switch, targets_csv))
|
||||
|
||||
# test modify existing
|
||||
value += '2'
|
||||
self.run_cli_cmd('property set %s %s %s %s'
|
||||
% (switch, targets_csv, key, value))
|
||||
msg = self.run_cli_cmd('property list --all -f json %s %s'
|
||||
% (switch, targets_csv))
|
||||
err_msg = self._check_property_values(key, value, msg, targets)
|
||||
self.assertEqual(err_msg, '',
|
||||
'set failed property not in output: %s, %s (%s %s)'
|
||||
% (key, value, switch, targets_csv))
|
||||
bad_path = self._is_size_ok(sizes, 1, '<', 2)
|
||||
self.assertIsNone(bad_path, 'Size of file %s did not ' % bad_path +
|
||||
'increase after modify (%s %s)'
|
||||
% (switch, targets_csv))
|
||||
|
||||
# test clear
|
||||
self.run_cli_cmd('property clear %s %s %s'
|
||||
% (switch, targets_csv, key))
|
||||
msg = self.run_cli_cmd('property list --long -f json %s %s'
|
||||
% (switch, targets_csv))
|
||||
err_msg = self._check_property_values(key, value, msg, targets)
|
||||
self.assertTrue('missing' in err_msg,
|
||||
'clear failed, property still in output: ' +
|
||||
'%s, %s (%s %s)'
|
||||
% (key, value, switch, targets_csv))
|
||||
bad_path = self._is_size_ok(sizes, 0, '=', 3)
|
||||
self.assertIsNone(bad_path, 'Size of file %s is ' % bad_path +
|
||||
'different from initial size '
|
||||
'(%s %s %s)'
|
||||
% (switch, targets_csv, str(sizes)))
|
||||
|
||||
# test setting empty string
|
||||
value = '""'
|
||||
self.run_cli_cmd('property set %s %s %s %s'
|
||||
% (switch, targets_csv, key, value))
|
||||
msg = self.run_cli_cmd('property list --all -f json %s %s'
|
||||
% (switch, targets_csv))
|
||||
err_msg = self._check_property_values(key, value, msg, targets)
|
||||
self.assertTrue('missing' in err_msg,
|
||||
'clear failed, property still in output: ' +
|
||||
'%s, %s (%s %s)'
|
||||
% (key, value, switch, targets_csv))
|
||||
self.run_cli_cmd('property clear %s %s %s'
|
||||
% (switch, targets_csv, key))
|
||||
|
||||
def _check_property_values(self, key, value, json_str,
|
||||
targets=[]):
|
||||
"""Verify cli data against model data"""
|
||||
error_msg = ''
|
||||
props = json.loads(json_str.strip())
|
||||
if not targets:
|
||||
# simple property check
|
||||
ok = False
|
||||
for prop in props:
|
||||
if (prop['Property Name'] == key and
|
||||
prop['Property Value'] == value):
|
||||
ok = True
|
||||
if not ok:
|
||||
error_msg = '%s:%s is missing in __GLOBAL__'
|
||||
else:
|
||||
target_map = {}
|
||||
for target in targets:
|
||||
target_map[target] = 'missing'
|
||||
for prop in props:
|
||||
if 'Group' in prop and prop['Group'] == target:
|
||||
if (prop['Property Name'] == key and
|
||||
prop['Property Value'] == value):
|
||||
target_map[target] = 'ok'
|
||||
elif 'Host' in prop and prop['Host'] == target:
|
||||
if (prop['Property Name'] == key and
|
||||
prop['Property Value'] == value):
|
||||
target_map[target] = 'ok'
|
||||
for target, state in target_map.items():
|
||||
if state == 'missing':
|
||||
error_msg += ('%s:%s is missing in %s\n output:%s\n'
|
||||
% (key, value, target, json_str))
|
||||
return error_msg
|
||||
|
||||
def _is_size_ok(self, sizes, idx0, comparator, idx1):
|
||||
bad_path = None
|
||||
for path, path_sizes in sizes.items():
|
||||
if idx1 > len(path_sizes) - 1:
|
||||
# get new sizes
|
||||
sizes[path].append(os.path.getsize(path))
|
||||
if comparator == '=':
|
||||
if sizes[path][idx0] != sizes[path][idx1]:
|
||||
bad_path = path
|
||||
break
|
||||
elif comparator == '<':
|
||||
if sizes[path][idx0] >= sizes[path][idx1]:
|
||||
bad_path = path
|
||||
break
|
||||
return bad_path
|
||||
|
||||
def _override_test(self, json_str, key, value, ovr_string,
|
||||
host=None, group=None):
|
||||
error_msg = ''
|
||||
props = json.loads(json_str.strip())
|
||||
for prop in props:
|
||||
if group is not None:
|
||||
if prop['Group'] == group:
|
||||
error_msg = self._check_override_value(prop, key,
|
||||
value, ovr_string)
|
||||
elif host is not None:
|
||||
if prop['Host'] == host:
|
||||
error_msg = self._check_override_value(prop, key,
|
||||
value, ovr_string)
|
||||
else:
|
||||
error_msg = self._check_override_value(prop, key,
|
||||
value, ovr_string)
|
||||
return error_msg
|
||||
|
||||
def _check_override_value(self, prop, key, value, ovr_string):
|
||||
error_msg = ''
|
||||
if(prop['Property Name'] == key and
|
||||
prop['Property Value'] == value and
|
||||
prop['OVR'] != ovr_string):
|
||||
error_msg = ('override value mismatch for '
|
||||
'key:%s value:%s ovr:%s target:%s' %
|
||||
(key, value, prop['OVR'], ovr_string))
|
||||
return error_msg
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,50 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
|
||||
import unittest
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_reconfigure(self):
|
||||
# test will start with no hosts in the inventory
|
||||
# reconfigure will throw an exception if it fails
|
||||
# disable all services first as without it empty groups cause errors
|
||||
enable_service_props = {}
|
||||
for service in CLIENT.service_get_all():
|
||||
service_name = service.name.replace('-', '_')
|
||||
enable_service_props['enable_%s' % service_name] = 'no'
|
||||
CLIENT.property_set(enable_service_props)
|
||||
|
||||
msg = ''
|
||||
try:
|
||||
job = CLIENT.reconfigure()
|
||||
job.wait()
|
||||
msg = job.get_console_output()
|
||||
self.assertEqual(job.get_status(), 0,
|
||||
'error performing reconfigure: %s' % msg)
|
||||
except Exception as e:
|
||||
self.assertEqual(0, 1,
|
||||
'unexpected exception in reconfigure %s, %s'
|
||||
% (e.message, msg))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,202 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.common.ansible_inventory import AnsibleInventory
|
||||
from kolla_cli.common.inventory import Inventory
|
||||
|
||||
CLIENT = ClientApi()
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_service_api(self):
|
||||
service1 = 'nova'
|
||||
service2 = 'nova-api'
|
||||
exp_services = sorted([service1, service1, service2])
|
||||
servicenames = []
|
||||
services = CLIENT.service_get(exp_services)
|
||||
for service in services:
|
||||
servicenames.append(service.name)
|
||||
servicenames = sorted(servicenames)
|
||||
self.assertEqual(exp_services, servicenames, 'services mis-match')
|
||||
|
||||
def test_service_list(self):
|
||||
"""$ kolla-cli service list -f json
|
||||
|
||||
[{"Service": "barbican", "Children": ["barbican-keystone-listener"
|
||||
"barbican-worker", "barbican-api"]}, {"Service": "barbican-api"
|
||||
"Children": []}, {"Service": "barbican-keystone-listener",
|
||||
"Children": []}, {"Service": "barbican-worker"...]
|
||||
"""
|
||||
msg = self.run_cli_cmd('service list -f json')
|
||||
cli_services = json.loads(msg)
|
||||
ansible_inventory = AnsibleInventory()
|
||||
ansible_inventory_services = []
|
||||
ansible_inventory_service_names = []
|
||||
for service in ansible_inventory.services.values():
|
||||
if service.is_supported():
|
||||
ansible_inventory_services.append(service)
|
||||
ansible_inventory_service_names.append(service.name)
|
||||
num_services = len(ansible_inventory_services)
|
||||
self.assertEqual(num_services, len(cli_services),
|
||||
'# of cli services != expected services.' +
|
||||
'\n\nexpected services: %s'
|
||||
% ansible_inventory_service_names +
|
||||
'\n\ncli services: %s' % cli_services)
|
||||
|
||||
def test_listgroups(self):
|
||||
"""$ kolla-cli service listgroups
|
||||
|
||||
+------------------------+-------------------------+ \
|
||||
| Service | Groups | \
|
||||
+------------------------+-------------------------+ \
|
||||
| cinder | ['control', 'control2'] | \
|
||||
| cinder-api | | \
|
||||
| cinder-backup | ['storage'] | \
|
||||
| cinder-scheduler | | \
|
||||
| cinder-volume | ['storage'] | \
|
||||
| glance | ['control', 'control2'] | \
|
||||
| glance-api | | \
|
||||
| glance-registry | | \
|
||||
...
|
||||
|
||||
"""
|
||||
msg = self.run_cli_cmd('service listgroups -f json')
|
||||
cli_services = json.loads(msg)
|
||||
|
||||
ansible_inventory = AnsibleInventory()
|
||||
ansible_inventory_services = []
|
||||
ansible_inventory_service_names = []
|
||||
for service in ansible_inventory.services.values():
|
||||
if service.is_supported():
|
||||
ansible_inventory_services.append(service)
|
||||
ansible_inventory_service_names.append(service.name)
|
||||
num_services = len(ansible_inventory_services)
|
||||
self.assertEqual(num_services, len(cli_services),
|
||||
'# of cli services (%s) ' % len(cli_services) +
|
||||
'!= # of expected services (%s).' % num_services +
|
||||
'\n\ncli services: %s' % cli_services)
|
||||
|
||||
def test_service_add_group(self):
|
||||
servicename = 'cinder'
|
||||
new_group = 'network'
|
||||
|
||||
inventory = Inventory.load()
|
||||
service = inventory.get_service(servicename)
|
||||
groupnames = service.get_groupnames()
|
||||
|
||||
# add new group to a service
|
||||
self.run_cli_cmd('service addgroup %s %s' % (servicename, new_group))
|
||||
|
||||
msg = self.run_cli_cmd('service listgroups -f json')
|
||||
cli_services = json.loads(msg)
|
||||
cli_service = ''
|
||||
for svc in cli_services:
|
||||
if svc['Service'] == servicename:
|
||||
cli_service = svc
|
||||
break
|
||||
self.assertNotEqual(cli_service, '',
|
||||
'service: %s, ' % servicename +
|
||||
'not found in cli_services: \n%s'
|
||||
% cli_service)
|
||||
cli_groups = cli_service['Groups']
|
||||
groupnames.append(new_group)
|
||||
self.assertEqual(groupnames, cli_groups,
|
||||
'service: %s, ' % service +
|
||||
'expected groups: %s, ' % groupnames +
|
||||
'cli_groups: %s' % cli_groups)
|
||||
|
||||
# remove that group
|
||||
self.run_cli_cmd('service removegroup %s %s' % (servicename,
|
||||
new_group))
|
||||
groupnames.remove(new_group)
|
||||
|
||||
msg = self.run_cli_cmd('service listgroups -f json')
|
||||
cli_services = json.loads(msg)
|
||||
for svc in cli_services:
|
||||
if svc['Service'] == servicename:
|
||||
cli_service = svc
|
||||
break
|
||||
self.assertNotEqual(cli_service, '',
|
||||
'service: %s, ' % servicename +
|
||||
'not found in cli_services: \n%s'
|
||||
% cli_service)
|
||||
cli_groups = cli_service['Groups']
|
||||
expected_groups = groupnames
|
||||
self.assertEqual(expected_groups, cli_groups,
|
||||
'service: %s, ' % service +
|
||||
'expected groups: %s, ' % expected_groups +
|
||||
'cli_groups: %s' % cli_groups)
|
||||
|
||||
# add new group to a service which has a parent
|
||||
servicename = 'glance-api'
|
||||
new_group = 'control'
|
||||
self.run_cli_cmd('service addgroup %s %s' % (servicename, new_group))
|
||||
|
||||
msg = self.run_cli_cmd('service listgroups -f json')
|
||||
cli_services = json.loads(msg)
|
||||
for svc in cli_services:
|
||||
if svc['Service'] == servicename:
|
||||
cli_service = svc
|
||||
break
|
||||
self.assertNotEqual(cli_service, '',
|
||||
'service: %s, ' % servicename +
|
||||
'not found in cli_services: \n%s'
|
||||
% cli_service)
|
||||
cli_groups = cli_service['Groups']
|
||||
expected_groups = ['%s' % new_group]
|
||||
self.assertEqual(expected_groups, cli_groups,
|
||||
'service: %s, ' % service +
|
||||
'expected groups: %s, ' % expected_groups +
|
||||
'cli_groups: %s' % cli_groups)
|
||||
|
||||
# remove that group
|
||||
self.run_cli_cmd('service removegroup %s %s' % (servicename,
|
||||
new_group))
|
||||
|
||||
msg = self.run_cli_cmd('service listgroups -f json')
|
||||
cli_services = json.loads(msg)
|
||||
for svc in cli_services:
|
||||
if svc['Service'] == servicename:
|
||||
cli_service = svc
|
||||
break
|
||||
self.assertNotEqual(cli_service, '',
|
||||
'service: %s, ' % service +
|
||||
'not found in cli_services: \n%s'
|
||||
% cli_service)
|
||||
cli_groups = cli_service['Groups']
|
||||
expected_groups = []
|
||||
self.assertEqual(expected_groups, cli_groups,
|
||||
'service: %s, ' % servicename +
|
||||
'expected groups: %s, ' % expected_groups +
|
||||
'cli_groups: %s' % cli_groups)
|
||||
|
||||
test_group = 'testgroup'
|
||||
self.run_cli_cmd('group add %s' % test_group)
|
||||
self.run_cli_cmd('service addgroup cinder %s' % test_group)
|
||||
self.run_cli_cmd('group remove %s' % test_group)
|
||||
msg = self.run_cli_cmd('service listgroups -f json')
|
||||
self.assertNotIn(test_group, msg,
|
||||
'Group: %s, still listed in services: %s'
|
||||
% (test_group, msg))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,79 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
|
||||
import unittest
|
||||
|
||||
TEST_GROUP_NAME = 'test_group'
|
||||
CLIENT = ClientApi()
|
||||
|
||||
NOT_KNOWN = 'Name or service not known'
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_stop(self):
|
||||
# No physical hosts in config, use a non-existent host.
|
||||
# This will generate expected exceptions in all host access
|
||||
# commands.
|
||||
hostnames = ['test_deploy_host1']
|
||||
CLIENT.host_add(hostnames)
|
||||
|
||||
# add host to a new deploy group
|
||||
CLIENT.group_add([TEST_GROUP_NAME])
|
||||
group = CLIENT.group_get([TEST_GROUP_NAME])[0]
|
||||
for hostname in hostnames:
|
||||
group.add_host(hostname)
|
||||
|
||||
# stop services, initialize server
|
||||
self.log.info('Start stop #1')
|
||||
job = CLIENT.stop(1, hostnames)
|
||||
self._process_job(job, 'stop #1')
|
||||
|
||||
self.log.info('updating various properties for the test')
|
||||
|
||||
# disable services so the test is quicker
|
||||
enable_service_props = {}
|
||||
for service in CLIENT.service_get_all():
|
||||
service_name = service.name.replace('-', '_')
|
||||
enable_service_props['enable_%s' % service_name] = 'no'
|
||||
CLIENT.property_set(enable_service_props)
|
||||
|
||||
# do a deploy of a limited set of services
|
||||
self.log.info('Start a deployment')
|
||||
job = CLIENT.deploy()
|
||||
self._process_job(job, 'deploy')
|
||||
|
||||
self.log.info('Start stop #2')
|
||||
job = CLIENT.stop(1, hostnames)
|
||||
self._process_job(job, 'stop #2')
|
||||
|
||||
def _process_job(self, job, descr, expect_kill=False):
|
||||
status = job.wait()
|
||||
output = job.get_console_output()
|
||||
self.log.info('job is complete. status: %s, err: %s'
|
||||
% (status, output))
|
||||
if expect_kill:
|
||||
self.assertEqual(2, status, 'Job %s does not have killed status %s'
|
||||
% (descr, output))
|
||||
else:
|
||||
self.assertEqual(1, status, 'Job %s ' % descr +
|
||||
'succeeded when it should have failed')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,111 +0,0 @@
|
|||
# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# 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 kolla_cli.tests.functional.common import KollaCliTest
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
import unittest
|
||||
|
||||
from kolla_cli.api.client import ClientApi
|
||||
from kolla_cli.common.utils import get_tools_path
|
||||
from kolla_cli.common.utils import safe_decode
|
||||
|
||||
LOGS_PREFIX = '/tmp/kolla_support_logs_'
|
||||
CLIENT = ClientApi()
|
||||
|
||||
LOGDIR = '/tmp/utest_kolla_logs'
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
def test_log_collector(self):
|
||||
hostnames = ['test_host1']
|
||||
CLIENT.host_add(hostnames)
|
||||
|
||||
zip_path = ''
|
||||
try:
|
||||
path = os.path.join(get_tools_path(),
|
||||
'log_collector.py')
|
||||
|
||||
# run the log_collector tool
|
||||
retval, msg = self.run_command('/usr/bin/python %s %s'
|
||||
% (path, 'all'))
|
||||
|
||||
# no host, this should fail
|
||||
self.assertIn('error', msg.lower())
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if zip_path and os.path.exists(zip_path):
|
||||
os.remove(zip_path)
|
||||
|
||||
def test_log_collector_api(self):
|
||||
if os.path.exists(LOGDIR):
|
||||
shutil.rmtree(LOGDIR)
|
||||
os.mkdir(LOGDIR)
|
||||
|
||||
hostnames = ['test_host1']
|
||||
CLIENT.host_add(hostnames)
|
||||
|
||||
services = CLIENT.service_get_all()
|
||||
service_names = []
|
||||
for service in services:
|
||||
service_names.append(service.name)
|
||||
try:
|
||||
for hostname in hostnames:
|
||||
CLIENT.support_get_logs(service_names, safe_decode(hostname),
|
||||
LOGDIR)
|
||||
raise Exception('get_logs command succeeded without physical '
|
||||
'hosts')
|
||||
except Exception as e:
|
||||
self.assertIn('UNREACHABLE', str(e),
|
||||
'unexpected failure in get_logs: %s' % str(e))
|
||||
finally:
|
||||
if os.path.exists(LOGDIR):
|
||||
shutil.rmtree(LOGDIR)
|
||||
|
||||
def test_dump(self):
|
||||
check_files = [
|
||||
'kolla/etc/kolla-cli/ansible/inventory.json',
|
||||
'kolla/share/ansible/site.yml',
|
||||
]
|
||||
# dump success output is:
|
||||
# dump successful to /tmp/kollacli_dump_Umxu6d.tgz
|
||||
dump_path = None
|
||||
try:
|
||||
msg = self.run_cli_cmd('dump')
|
||||
self.assertIn('/', msg, 'path not found in dump output: %s' % msg)
|
||||
|
||||
dump_path = '/' + msg.strip().split('/', 1)[1]
|
||||
is_file = os.path.isfile(dump_path)
|
||||
self.assertTrue(is_file,
|
||||
'dump file not found at %s' % dump_path)
|
||||
with tarfile.open(dump_path, 'r') as tar:
|
||||
file_paths = tar.getnames()
|
||||
|
||||
for check_file in check_files:
|
||||
self.assertIn(check_file, file_paths,
|
||||
'dump: check file: %s, not in files:\n%s'
|
||||
% (check_file, file_paths))
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if dump_path and os.path.exists(dump_path):
|
||||
os.remove(dump_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 testtools
|
||||
|
||||
from kolla_cli.api.group import Group
|
||||
from kolla_cli.api.host import Host
|
||||
from kolla_cli.api.service import Service
|
||||
from kolla_cli.common.ansible.job import AnsibleJob
|
||||
from kolla_cli import shell
|
||||
|
||||
|
||||
class KollaCliUnitTest(testtools.TestCase):
|
||||
|
||||
def run_cli_command(self, command_string):
|
||||
# return 0 if command succeeded, non-0 if failed
|
||||
args = command_string.split()
|
||||
return shell.main(args)
|
||||
|
||||
def get_fake_job(self):
|
||||
return AnsibleJob(None, None, None, None)
|
||||
|
||||
def get_fake_host(self, hostname='foo'):
|
||||
return Host(hostname)
|
||||
|
||||
def get_fake_group(self, groupname='group1', servicenames=[],
|
||||
hostnames=[]):
|
||||
return Group(groupname, servicenames, hostnames)
|
||||
|
||||
def get_fake_service(self, servicename='service1', parentnames=[],
|
||||
childnames=[], groupnames=[]):
|
||||
return Service(servicename, parentnames, childnames, groupnames)
|
|
@ -1,43 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.deploy')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_deploy(self, _, mock_get_status, mock_deploy):
|
||||
mock_get_status.return_value = 0
|
||||
mock_deploy.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action deploy')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_deploy.assert_called_once_with(None, False, 1, None)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.deploy')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_deploy_with_services(self, _, mock_get_status, mock_deploy):
|
||||
mock_get_status.return_value = 0
|
||||
mock_deploy.return_value = self.get_fake_job()
|
||||
services = ['foo', 'bar']
|
||||
ret = self.run_cli_command(
|
||||
'action deploy --services {}'.format(','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_deploy.assert_called_once_with(None, False, 1, services)
|
|
@ -1,82 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.genconfig')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_genconfig(self, _, mock_get_status, mock_genconfig):
|
||||
mock_get_status.return_value = 0
|
||||
mock_genconfig.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action genconfig')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_genconfig.assert_called_once_with(1, [], [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.genconfig')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_genconfig_with_hosts(self,
|
||||
_,
|
||||
mock_get_status,
|
||||
mock_genconfig):
|
||||
mock_get_status.return_value = 0
|
||||
mock_genconfig.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
ret = self.run_cli_command(
|
||||
'action genconfig --hosts {hosts}'.format(
|
||||
hosts=','.join(hostnames)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_genconfig.assert_called_once_with(1, hostnames, [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.genconfig')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_genconfig_with_services(self,
|
||||
_,
|
||||
mock_get_status,
|
||||
mock_genconfig):
|
||||
mock_get_status.return_value = 0
|
||||
mock_genconfig.return_value = self.get_fake_job()
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action genconfig --services {services}'.format(
|
||||
services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_genconfig.assert_called_once_with(1, [], services)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.genconfig')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_genconfig_with_hosts_and_services(self,
|
||||
_,
|
||||
mock_get_status,
|
||||
mock_genconfig):
|
||||
mock_get_status.return_value = 0
|
||||
mock_genconfig.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action genconfig --hosts {hosts} --services {services}'.format(
|
||||
hosts=','.join(hostnames), services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_genconfig.assert_called_once_with(1, hostnames, services)
|
|
@ -1,95 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_add')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_group_add(self, _, mock_add):
|
||||
groupname = 'group1'
|
||||
ret = self.run_cli_command('group add %s' % groupname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_add.assert_called_once_with([groupname])
|
||||
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_remove')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_group_remove(self, _, mock_remove):
|
||||
groupname = 'group1'
|
||||
ret = self.run_cli_command('group remove %s' % groupname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_remove.assert_called_once_with([groupname])
|
||||
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_get_all')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_group_listhosts(self, _, mock_group_get_all):
|
||||
# list all groups and their hosts
|
||||
hostname = 'foo'
|
||||
groupname = 'group1'
|
||||
fake_group = self.get_fake_group(groupname, hostnames=[hostname])
|
||||
mock_group_get_all.return_value = [fake_group]
|
||||
ret = self.run_cli_command('group listhosts')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_group_get_all.assert_called_once_with()
|
||||
|
||||
@mock.patch('kolla_cli.api.group.Group.add_host')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_get')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_group_addhost(self, _, mock_group_get, mock_group_add_host):
|
||||
hostname = 'foo'
|
||||
groupname = 'group1'
|
||||
fake_group = self.get_fake_group(groupname)
|
||||
mock_group_get.return_value = [fake_group]
|
||||
ret = self.run_cli_command('group addhost %s %s'
|
||||
% (groupname, hostname))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_group_get.assert_called_once_with([groupname])
|
||||
mock_group_add_host.assert_called_once_with(hostname)
|
||||
|
||||
@mock.patch('kolla_cli.api.group.Group.remove_host')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_get')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_group_removehost(self, _, mock_group_get,
|
||||
mock_group_remove_host):
|
||||
hostname = 'foo'
|
||||
groupname = 'group1'
|
||||
fake_group = self.get_fake_group(groupname, hostnames=[hostname])
|
||||
mock_group_get.return_value = [fake_group]
|
||||
ret = self.run_cli_command('group removehost %s %s'
|
||||
% (groupname, hostname))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_group_get.assert_called_once_with([groupname])
|
||||
mock_group_remove_host.assert_called_once_with(hostname)
|
||||
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_get_all')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_group_listservices(self, _, mock_group_get_all):
|
||||
# list all groups and their services
|
||||
servicename = 'service1'
|
||||
groupname = 'group1'
|
||||
fake_group = self.get_fake_group(groupname,
|
||||
servicenames=[servicename])
|
||||
mock_group_get_all.return_value = [fake_group]
|
||||
ret = self.run_cli_command('group listservices')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_group_get_all.assert_called_once_with()
|
|
@ -1,179 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_add')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_host_add(self, _, mock_add):
|
||||
hostname = 'foo'
|
||||
ret = self.run_cli_command('host add %s' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_add.assert_called_once_with([hostname])
|
||||
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_remove')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_host_remove(self, _, mock_remove):
|
||||
hostname = 'foo'
|
||||
ret = self.run_cli_command('host remove %s' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_remove.assert_called_once_with([hostname])
|
||||
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_get_all')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_get')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_host_list(self, _, mock_get, mock_get_all):
|
||||
# get all hosts
|
||||
mock_get_all.return_value = []
|
||||
ret = self.run_cli_command('host list')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_get_all.assert_called_once_with()
|
||||
|
||||
# get a specific host
|
||||
hostname = 'foo'
|
||||
mock_get.return_value = []
|
||||
ret = self.run_cli_command('host list %s' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_get.assert_called_once_with([hostname])
|
||||
|
||||
@mock.patch('kolla_cli.commands.host.HostDestroy._is_ok_to_delete_data',
|
||||
return_value='y')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_get_all')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_destroy')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_host_destroy(self, _, mock_destroy, mock_get_all,
|
||||
mock_get_status, mock_prompt):
|
||||
hostname = 'foo'
|
||||
mock_get_all.return_value = [self.get_fake_host(hostname)]
|
||||
mock_destroy.return_value = self.get_fake_job()
|
||||
mock_get_status.return_value = 0
|
||||
|
||||
# default destroy hostname
|
||||
ret = self.run_cli_command('host destroy %s' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_destroy.assert_called_once_with([hostname], 'kill', 1,
|
||||
False, False)
|
||||
# destroy all
|
||||
mock_destroy.reset_mock()
|
||||
ret = self.run_cli_command('host destroy all')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_destroy.assert_called_once_with([hostname], 'kill', 1,
|
||||
False, False)
|
||||
# destroy --stop
|
||||
mock_destroy.reset_mock()
|
||||
ret = self.run_cli_command('host destroy %s --stop' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_destroy.assert_called_once_with([hostname], 'stop', 1,
|
||||
False, False)
|
||||
# destroy --includedata
|
||||
mock_destroy.reset_mock()
|
||||
ret = self.run_cli_command('host destroy %s --includedata' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_destroy.assert_called_once_with([hostname], 'kill', 1,
|
||||
True, False)
|
||||
|
||||
# destroy --removeimages
|
||||
mock_destroy.reset_mock()
|
||||
ret = self.run_cli_command('host destroy %s --removeimages'
|
||||
% hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_destroy.assert_called_once_with([hostname], 'kill', 1,
|
||||
False, True)
|
||||
|
||||
@mock.patch('kolla_cli.commands.host.LOG.info')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_get_all')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_ssh_check')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_host_ssh_check(self, _, mock_ssh_check, mock_get_all, mock_log):
|
||||
hostname = 'foo'
|
||||
check_ok_response = {hostname: {'success': True}}
|
||||
check_bad_response = {hostname: {'success': False, 'msg': 'FAILED'}}
|
||||
mock_get_all.return_value = [self.get_fake_host(hostname)]
|
||||
|
||||
# host check hostname (success)
|
||||
mock_ssh_check.return_value = check_ok_response
|
||||
ret = self.run_cli_command('host check %s' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_ssh_check.assert_called_once_with([hostname])
|
||||
mock_log.assert_called_once_with('Host %s: success ' % hostname)
|
||||
|
||||
# host check all (success)
|
||||
mock_ssh_check.reset_mock()
|
||||
mock_log.reset_mock()
|
||||
mock_ssh_check.return_value = check_ok_response
|
||||
ret = self.run_cli_command('host check all')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_ssh_check.assert_called_once_with([hostname])
|
||||
mock_log.assert_called_once_with('Host %s: success ' % hostname)
|
||||
|
||||
# host check hostname (fail)
|
||||
mock_ssh_check.reset_mock()
|
||||
mock_log.reset_mock()
|
||||
mock_ssh_check.return_value = check_bad_response
|
||||
ret = self.run_cli_command('host check %s' % hostname)
|
||||
self.assertEqual(ret, 1)
|
||||
mock_ssh_check.assert_called_once_with([hostname])
|
||||
mock_log.assert_called_once_with('Host %s: failed- FAILED' % hostname)
|
||||
|
||||
@mock.patch('kolla_cli.commands.host.HostSetup._get_yml_data')
|
||||
@mock.patch('getpass.getpass')
|
||||
@mock.patch('kolla_cli.commands.host.ClientApi.host_ssh_check')
|
||||
@mock.patch('kolla_cli.commands.host.ClientApi.host_setup')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.host_get_all')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_host_setup(self, _, mock_get_all, mock_setup, mock_ssh_check,
|
||||
mock_passwd, mock_yml):
|
||||
password = 'PASSWORD'
|
||||
hostname = 'foo'
|
||||
mock_get_all.return_value = [self.get_fake_host(hostname)]
|
||||
mock_passwd.return_value = password
|
||||
|
||||
# single host setup (host not yet setup)
|
||||
mock_ssh_check.return_value = {hostname: {'success': False}}
|
||||
ret = self.run_cli_command('host setup %s' % hostname)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_ssh_check.assert_called_once_with([hostname])
|
||||
mock_setup.assert_called_once_with({hostname: {'password': password}})
|
||||
|
||||
# single host setup --insecure (host already setup)
|
||||
mock_ssh_check.reset_mock()
|
||||
mock_setup.reset_mock()
|
||||
mock_ssh_check.return_value = {hostname: {'success': True}}
|
||||
ret = self.run_cli_command('host setup %s --insecure %s'
|
||||
% (hostname, password))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_ssh_check.assert_called_once_with([hostname])
|
||||
mock_setup.assert_not_called()
|
||||
|
||||
# multi-host setup
|
||||
mock_ssh_check.reset_mock()
|
||||
mock_setup.reset_mock()
|
||||
fake_path = '/bogus'
|
||||
mock_yml.return_value = {hostname: {'password': password}}
|
||||
ret = self.run_cli_command('host setup --file %s' % fake_path)
|
||||
self.assertEqual(ret, 0)
|
||||
mock_setup.assert_called_once_with({hostname: {'password': password}})
|
||||
mock_yml.assert_called_once_with(fake_path)
|
||||
mock_ssh_check.assert_not_called()
|
|
@ -1,74 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.prechecks')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_prechecks(self, _, mock_get_status, mock_prechecks):
|
||||
mock_get_status.return_value = 0
|
||||
mock_prechecks.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action prechecks')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_prechecks.assert_called_once_with(1, [], [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.prechecks')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_prechecks_with_hosts(self, _, mock_get_status, mock_prechecks):
|
||||
mock_get_status.return_value = 0
|
||||
mock_prechecks.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
ret = self.run_cli_command(
|
||||
'action prechecks --hosts {hosts}'.format(
|
||||
hosts=','.join(hostnames)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_prechecks.assert_called_once_with(1, hostnames, [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.prechecks')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_prechecks_with_services(self, _, mock_get_status, mock_prechecks):
|
||||
mock_get_status.return_value = 0
|
||||
mock_prechecks.return_value = self.get_fake_job()
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action prechecks --service {services}'.format(
|
||||
services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_prechecks.assert_called_once_with(1, [], services)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.prechecks')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_prechecks_with_hosts_and_services(self, _, mock_get_status,
|
||||
mock_prechecks):
|
||||
mock_get_status.return_value = 0
|
||||
mock_prechecks.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action prechecks --hosts {hosts} --service {services}'.format(
|
||||
hosts=','.join(hostnames), services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_prechecks.assert_called_once_with(1, hostnames, services)
|
|
@ -1,79 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.pull')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_pull(self, _, mock_get_status, mock_pull):
|
||||
mock_get_status.return_value = 0
|
||||
mock_pull.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action pull')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_pull.assert_called_once_with(1, [], [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.pull')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_pull_with_hosts(self, _,
|
||||
mock_get_status,
|
||||
mock_pull):
|
||||
mock_get_status.return_value = 0
|
||||
mock_pull.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
ret = self.run_cli_command(
|
||||
'action pull --hosts {hosts}'.format(
|
||||
hosts=','.join(hostnames)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_pull.assert_called_once_with(1, hostnames, [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.pull')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_pull_with_services(self, _,
|
||||
mock_get_status,
|
||||
mock_pull):
|
||||
mock_get_status.return_value = 0
|
||||
mock_pull.return_value = self.get_fake_job()
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action pull --services {services}'.format(
|
||||
services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_pull.assert_called_once_with(1, [], services)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.pull')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_pull_with_hosts_and_services(self, _,
|
||||
mock_get_status,
|
||||
mock_pull):
|
||||
mock_get_status.return_value = 0
|
||||
mock_pull.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action pull --hosts {hosts} --services {services}'.format(
|
||||
hosts=','.join(hostnames), services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_pull.assert_called_once_with(1, hostnames, services)
|
|
@ -1,82 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.reconfigure')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_reconfigure(self, _, mock_get_status, mock_reconfigure):
|
||||
mock_get_status.return_value = 0
|
||||
mock_reconfigure.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action reconfigure')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_reconfigure.assert_called_once_with(1, [], [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.reconfigure')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_reconfigure_with_hosts(self,
|
||||
_,
|
||||
mock_get_status,
|
||||
mock_reconfigure):
|
||||
mock_get_status.return_value = 0
|
||||
mock_reconfigure.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
ret = self.run_cli_command(
|
||||
'action reconfigure --hosts {hosts}'.format(
|
||||
hosts=','.join(hostnames)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_reconfigure.assert_called_once_with(1, hostnames, [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.reconfigure')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_reconfigure_with_services(self,
|
||||
_,
|
||||
mock_get_status,
|
||||
mock_reconfigure):
|
||||
mock_get_status.return_value = 0
|
||||
mock_reconfigure.return_value = self.get_fake_job()
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action reconfigure --services {services}'.format(
|
||||
services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_reconfigure.assert_called_once_with(1, [], services)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.reconfigure')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_reconfigure_with_hosts_and_services(self,
|
||||
_,
|
||||
mock_get_status,
|
||||
mock_reconfigure):
|
||||
mock_get_status.return_value = 0
|
||||
mock_reconfigure.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action reconfigure --hosts {hosts} --services {services}'.format(
|
||||
hosts=','.join(hostnames), services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_reconfigure.assert_called_once_with(1, hostnames, services)
|
|
@ -1,92 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
|
||||
@mock.patch('cliff.lister.Lister.produce_output')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.service_get_all')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_service_list(self, _, mock_service_get_all, mock_cliff):
|
||||
# list all services, check that output is sorted properly
|
||||
servicename1 = 'service1'
|
||||
servicename2 = 'service2'
|
||||
childnames = ['child2', 'child1']
|
||||
fake_service1 = self.get_fake_service(servicename1,
|
||||
childnames=childnames)
|
||||
fake_service2 = self.get_fake_service(servicename2,
|
||||
childnames=childnames)
|
||||
mock_service_get_all.return_value = [fake_service2, fake_service1]
|
||||
ret = self.run_cli_command('service list')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_service_get_all.assert_called_once_with()
|
||||
|
||||
expected_childnames = '[child1,child2]'
|
||||
mock_cliff.assert_called_once_with(
|
||||
mock.ANY, ('Service', 'Children'),
|
||||
[(servicename1, expected_childnames),
|
||||
(servicename2, expected_childnames)
|
||||
])
|
||||
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.service_get_all')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_service_grouplist(self, _, mock_service_get_all):
|
||||
# list all services with their groups
|
||||
servicename = 'service1'
|
||||
groupname = 'group1'
|
||||
fake_service = self.get_fake_service(servicename,
|
||||
groupnames=[groupname])
|
||||
mock_service_get_all.return_value = [fake_service]
|
||||
ret = self.run_cli_command('service listgroups')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_service_get_all.assert_called_once_with()
|
||||
|
||||
@mock.patch('kolla_cli.api.group.Group.add_service')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_get')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_service_addgroup(self, _, mock_group_get,
|
||||
mock_group_add_service):
|
||||
servicename = 'service1'
|
||||
groupname = 'group1'
|
||||
fake_group = self.get_fake_group(groupname)
|
||||
mock_group_get.return_value = [fake_group]
|
||||
ret = self.run_cli_command('service addgroup %s %s'
|
||||
% (servicename, groupname))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_group_get.assert_called_once_with([groupname])
|
||||
mock_group_add_service.assert_called_once_with(servicename)
|
||||
|
||||
@mock.patch('kolla_cli.api.group.Group.remove_service')
|
||||
@mock.patch('kolla_cli.api.client.ClientApi.group_get')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_service_removegroup(self, _, mock_group_get,
|
||||
mock_group_remove_service):
|
||||
servicename = 'service1'
|
||||
groupname = 'group1'
|
||||
fake_group = self.get_fake_group(groupname,
|
||||
servicenames=[servicename])
|
||||
mock_group_get.return_value = [fake_group]
|
||||
ret = self.run_cli_command('service removegroup %s %s'
|
||||
% (servicename, groupname))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_group_get.assert_called_once_with([groupname])
|
||||
mock_group_remove_service.assert_called_once_with(servicename)
|
|
@ -1,73 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.stop')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_stop(self, _, mock_get_status, mock_stop):
|
||||
mock_get_status.return_value = 0
|
||||
mock_stop.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action stop')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_stop.assert_called_once_with(1, [], [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.stop')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_stop_with_hosts(self, _, mock_get_status, mock_stop):
|
||||
mock_get_status.return_value = 0
|
||||
mock_stop.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
ret = self.run_cli_command('action stop --hosts {hosts}'.format(
|
||||
hosts=','.join(hostnames)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_stop.assert_called_once_with(1, hostnames, [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.stop')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_stop_with_services(self, _, mock_get_status, mock_stop):
|
||||
mock_get_status.return_value = 0
|
||||
mock_stop.return_value = self.get_fake_job()
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action stop --service {services}'.format(
|
||||
services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_stop.assert_called_once_with(1, [], services)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.stop')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_stop_with_hosts_and_services(self, _, mock_get_status,
|
||||
mock_stop):
|
||||
mock_get_status.return_value = 0
|
||||
mock_stop.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action stop --hosts {hosts} --service {services}'.format(
|
||||
hosts=','.join(hostnames), services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_stop.assert_called_once_with(1, hostnames, services)
|
|
@ -1,74 +0,0 @@
|
|||
# Copyright (c) 2018 OpenStack Foundation
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from kolla_cli.tests.unit.common import KollaCliUnitTest
|
||||
|
||||
|
||||
class TestUnit(KollaCliUnitTest):
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.upgrade')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_upgrade(self, _, mock_get_status, mock_upgrade):
|
||||
mock_get_status.return_value = 0
|
||||
mock_upgrade.return_value = self.get_fake_job()
|
||||
ret = self.run_cli_command('action upgrade')
|
||||
self.assertEqual(ret, 0)
|
||||
mock_upgrade.assert_called_once_with(1, [], [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.upgrade')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_upgrade_with_hosts(self, _, mock_get_status, mock_upgrade):
|
||||
mock_get_status.return_value = 0
|
||||
mock_upgrade.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
ret = self.run_cli_command(
|
||||
'action upgrade --hosts {hosts}'.format(
|
||||
hosts=','.join(hostnames)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_upgrade.assert_called_once_with(1, hostnames, [])
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.upgrade')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_upgrade_with_services(self, _, mock_get_status, mock_upgrade):
|
||||
mock_get_status.return_value = 0
|
||||
mock_upgrade.return_value = self.get_fake_job()
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action upgrade --service {services}'.format(
|
||||
services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_upgrade.assert_called_once_with(1, [], services)
|
||||
|
||||
@mock.patch('kolla_cli.api.control_plane.ControlPlaneApi.upgrade')
|
||||
@mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status')
|
||||
@mock.patch('kolla_cli.shell.KollaCli._is_inventory_present',
|
||||
return_value=True)
|
||||
def test_upgrade_with_hosts_and_services(self, _, mock_get_status,
|
||||
mock_upgrade):
|
||||
mock_get_status.return_value = 0
|
||||
mock_upgrade.return_value = self.get_fake_job()
|
||||
hostnames = ['host1', 'host2']
|
||||
services = ['service1', 'service2']
|
||||
ret = self.run_cli_command(
|
||||
'action upgrade --hosts {hosts} --service {services}'.format(
|
||||
hosts=','.join(hostnames), services=','.join(services)))
|
||||
self.assertEqual(ret, 0)
|
||||
mock_upgrade.assert_called_once_with(1, hostnames, services)
|
|
@ -1,102 +0,0 @@
|
|||
alabaster==0.7.10
|
||||
ansible==2.5.0
|
||||
appdirs==1.4.3
|
||||
asn1crypto==0.24.0
|
||||
bandit==1.1.0
|
||||
bashate==0.5.1
|
||||
beautifulsoup4==4.6.0
|
||||
certifi==2018.1.18
|
||||
cffi==1.11.5
|
||||
chardet==3.0.4
|
||||
cliff==2.11.0
|
||||
cliff-tablib==2.0
|
||||
cmd2==0.8.1
|
||||
coverage==4.0
|
||||
cryptography==2.1
|
||||
debtcollector==1.19.0
|
||||
decorator==4.2.1
|
||||
deprecation==2.0
|
||||
doc8==0.6.0
|
||||
docker-pycreds==0.2.2
|
||||
docker==2.4.2
|
||||
docutils==0.14
|
||||
dogpile.cache==0.6.5
|
||||
dulwich==0.19.0
|
||||
extras==1.0.0
|
||||
fixtures==3.0.0
|
||||
gitdb2==2.0.3
|
||||
GitPython==2.1.8
|
||||
idna==2.6
|
||||
imagesize==1.0.0
|
||||
iso8601==0.1.12
|
||||
Jinja2==2.10
|
||||
jmespath==0.9.3
|
||||
jsonpatch==1.21
|
||||
jsonpickle==0.9
|
||||
jsonpointer==2.0
|
||||
jsonschema==2.6.0
|
||||
keystoneauth1==3.4.0
|
||||
linecache2==1.0.0
|
||||
MarkupSafe==1.0
|
||||
monotonic==1.4
|
||||
mox3==0.25.0
|
||||
mypy==0.600
|
||||
msgpack==0.5.6
|
||||
munch==2.2.0
|
||||
netaddr==0.7.18
|
||||
netifaces==0.10.6
|
||||
openstackdocstheme==1.18.1
|
||||
openstacksdk==0.12.0
|
||||
os-client-config==1.29.0
|
||||
os-service-types==1.2.0
|
||||
osc-lib==1.10.0
|
||||
oslo.config==5.2.0
|
||||
oslo.context==2.20.0
|
||||
oslo.i18n==3.20.0
|
||||
oslo.log==3.36.0
|
||||
oslo.serialization==2.25.0
|
||||
oslo.utils==3.33.0
|
||||
oslotest==3.2.0
|
||||
packaging==17.1
|
||||
paramiko==2.6.0
|
||||
pbr==2.0.0
|
||||
prettytable==0.7.1
|
||||
pycparser==2.18
|
||||
Pygments==2.2.0
|
||||
pyinotify==0.9.6
|
||||
pyOpenSSL==17.5.0
|
||||
pyparsing==2.2.0
|
||||
pyperclip==1.6.0
|
||||
python-ceilometerclient==2.5.0
|
||||
python-dateutil==2.7.0
|
||||
python-glanceclient==2.9.1
|
||||
python-keystoneclient==3.15.0
|
||||
python-mimeparse==1.6.0
|
||||
python-neutronclient==6.7.0
|
||||
python-novaclient==10.1.0
|
||||
python-openstackclient==3.12.0
|
||||
python-subunit==1.2.0
|
||||
pytz==2013.6
|
||||
PyYAML==3.12
|
||||
reno==2.5.0
|
||||
requests==2.18.4
|
||||
requestsexceptions==1.4.0
|
||||
restructuredtext-lint==1.1.3
|
||||
rfc3986==1.2.0
|
||||
setuptools==21.0.0
|
||||
simplejson==3.13.2
|
||||
smmap2==2.0.3
|
||||
snowballstemmer==1.2.1
|
||||
Sphinx==1.6.2
|
||||
sphinxcontrib-websupport==1.0.1
|
||||
stevedore==1.28.0
|
||||
stestr==2.0.0
|
||||
testscenarios==0.4
|
||||
testtools==2.2.0
|
||||
traceback2==1.4.0
|
||||
typing==3.6.6
|
||||
unittest2==1.1.0
|
||||
urllib3==1.22
|
||||
warlock==1.3.0
|
||||
websocket-client==0.47.0
|
||||
wrapt==1.10.11
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
features:
|
||||
- Start using reno.
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
prelude: >
|
||||
Kolla CLI is deprecated in the Ussuri cycle due to general lack of interest.
|
||||
The deliverable is expected to leave OpenStack governance in the next
|
||||
(Victoria) cycle. If you use this project and are able to help support it,
|
||||
please contact the Kolla team.
|
|
@ -1,5 +0,0 @@
|
|||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of kolla-cli
|
||||
to support py2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by kolla-cli is Python 3.6.
|
|
@ -1,189 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# 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.
|
||||
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = u'kolla-cli'
|
||||
copyright = u'2019, Kolla developers'
|
||||
author = u'Kolla-cli developers'
|
||||
|
||||
# The short X.Y version
|
||||
version = u'1.0'
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = u'1.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# The default sidebars (for documents that don't match any pattern) are
|
||||
# defined by theme itself. Builtin themes are using these templates by
|
||||
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
||||
# 'searchbox.html']``.
|
||||
#
|
||||
# html_sidebars = {}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'KollacliReleaseNotesdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'kolla.tex', u'kolla Documentation',
|
||||
u'Kolla developers', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'kolla', u'kolla Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'kolla', u'kolla Documentation',
|
||||
author, 'kolla', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Epub output -------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#
|
||||
# epub_uid = ''
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
|
@ -1,21 +0,0 @@
|
|||
.. kolla documentation master file, created by
|
||||
sphinx-quickstart on Tue Aug 6 14:08:15 2019.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Kolla-cli Release Notes documentation
|
||||
================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
ussuri
|
||||
train
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue