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:
wu.chunyang 2020-08-31 20:29:53 +08:00 committed by Radosław Piliszek
parent 7ba715987a
commit 3b56a5e0f7
110 changed files with 10 additions and 12107 deletions

27
.gitignore vendored
View File

@ -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/

View File

@ -1,3 +0,0 @@
[DEFAULT]
test_path=${OS_TEST_PATH:-./kolla_cli/tests/unit}
top_dir=./

View File

@ -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
View File

@ -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.

View File

@ -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.

View File

@ -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()

View File

@ -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."

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -1,11 +0,0 @@
tests Package
=============
Subpackages
-----------
.. toctree::
kolla_cli.tests.functional
kolla_cli.tests.unit

View File

@ -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:

View File

@ -1,7 +0,0 @@
kolla_cli
=========
.. toctree::
:maxdepth: 4
kolla_cli

View File

@ -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 ""

View File

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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())

View File

@ -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)

View File

@ -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())

View File

@ -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

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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

View File

@ -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())

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:]))

View File

@ -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

View File

@ -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)

View File

@ -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__

View File

@ -1,5 +0,0 @@
# Test ansible inventory
[aardvark]
[chipmunk]

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -1,3 +0,0 @@
---
features:
- Start using reno.

View File

@ -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.

View File

@ -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.

View File

@ -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/']

View File

@ -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