Retire sushy-cli - Step 2

The sushy-cli project has been inactive for a while and as announced in
the ML[1] it will be retired.

[1] http://lists.openstack.org/pipermail/openstack-discuss/2021-May/022405.html

Change-Id: Ifcaf56d2eac6b827f1b6f00d38d91d51ee3f547f
This commit is contained in:
Riccardo Pittau 2021-05-18 15:35:09 +02:00
parent 8b6e416b94
commit a6780093ab
60 changed files with 8 additions and 3212 deletions

108
.gitignore vendored
View File

@ -1,108 +0,0 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
.stestr/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# Others
AUTHORS
ChangeLog

View File

@ -1,3 +0,0 @@
[DEFAULT]
test_path=./sushycli/tests
top_dir=.

View File

@ -1,29 +0,0 @@
# Config file for automatic testing at travis-ci.org
language: python
python:
- 3.8
- 3.7
- 3.6
- 3.5
# Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install: pip install -U tox-travis
# Command to run tests, e.g. python setup.py test
script: tox
# Assuming you have installed the travis-ci CLI tool, after you
# create the Github repo and add it to Travis, run the
# following command to finish PyPI deployment setup:
# $ travis encrypt --add deploy.password
deploy:
provider: pypi
distributions: sdist bdist_wheel
user: khansaAmrouni
password:
secure: PLEASE_REPLACE_ME
on:
tags: true
repo: khansaAmrouni/sushycli
python: 3.8

View File

@ -1,17 +0,0 @@
If you would like to contribute to the development of OpenStack, you must
follow the steps in this page:
http://docs.openstack.org/infra/manual/developers.html
If you already have a good understanding of how the system works and your
OpenStack accounts are set up, you can skip to the development workflow
section of this documentation to learn how changes to OpenStack should be
submitted for review via the Gerrit tool:
http://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed in StoryBoard, not GitHub:
https://storyboard.openstack.org/#!/project/960

View File

@ -1,4 +0,0 @@
Sushy CLI Style Commandments
============================
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/

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,85 +0,0 @@
.PHONY: clean clean-test clean-pyc clean-build docs help
.DEFAULT_GOAL := help
define BROWSER_PYSCRIPT
import os, webbrowser, sys
from urllib.request import pathname2url
webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
endef
export BROWSER_PYSCRIPT
define PRINT_HELP_PYSCRIPT
import re, sys
for line in sys.stdin:
match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
if match:
target, help = match.groups()
print("%-20s %s" % (target, help))
endef
export PRINT_HELP_PYSCRIPT
BROWSER := python -c "$$BROWSER_PYSCRIPT"
help:
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
clean-build: ## remove build artifacts
rm -fr build/
rm -fr dist/
rm -fr .eggs/
find . -name '*.egg-info' -exec rm -fr {} +
find . -name '*.egg' -exec rm -f {} +
clean-pyc: ## remove Python file artifacts
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
find . -name '__pycache__' -exec rm -fr {} +
clean-test: ## remove test and coverage artifacts
rm -fr .tox/
rm -f .coverage
rm -fr htmlcov/
rm -fr .pytest_cache
lint: ## check style with flake8
flake8 sushycli tests
test: ## run tests quickly with the default Python
python setup.py test
test-all: ## run tests on every Python version with tox
tox
coverage: ## check code coverage quickly with the default Python
coverage run --source sushycli setup.py test
coverage report -m
coverage html
$(BROWSER) htmlcov/index.html
docs: ## generate Sphinx HTML documentation, including API docs
rm -f docs/sushycli.rst
rm -f docs/modules.rst
sphinx-apidoc -o docs/ sushycli
$(MAKE) -C docs clean
$(MAKE) -C docs html
$(BROWSER) docs/_build/html/index.html
servedocs: docs ## compile the docs watching for changes
watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .
release: dist ## package and upload a release
twine upload dist/*
dist: clean ## builds source and wheel package
python setup.py sdist
python setup.py bdist_wheel
ls -l dist
install: clean ## install the package to the active Python's site-packages
python setup.py install

View File

@ -1,22 +1,12 @@
About Sushy CLI
===============
This project is no longer maintained.
Sushy CLI tool is intended to help human beings to communicate with `Redfish`_
based baseboard management controllers (BMC).
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".
BMCs offer powerful means of controlling bare metal machines. For example, one
can flip machine power, change BIOS configuration or even flash the firmware.
For any further questions, please email
openstack-discuss@lists.openstack.org or join #openstack-ironic on
OFTC.
The `sushycli` tool offers a friendly command-line interface to some of the BMC
features. It's main audience include software developers working on bare metal
management automation and system administrators handling the raw iron with bare
hands.
* Free software: Apache license
* Documentation: https://docs.openstack.org/sushy-cli/latest/
* Usage: https://docs.openstack.org/sushy-cli/latest/reference/usage.html
* Source: https://opendev.org/openstack/sushy-cli
* Bugs: https://storyboard.openstack.org/#!/project/960
.. _Redfish: http://www.dmtf.org/standards/redfish

View File

@ -1,4 +0,0 @@
openstackdocstheme>=2.2.1 # Apache-2.0
reno>=3.1.0 # Apache-2.0
sphinx>=2.0.0,!=2.1.0 # BSD

View File

@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXPROJ = sushycli
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 os
import sys
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
'openstackdocstheme'
]
# autodoc generation is a bit aggressive and a nuisance when doing heavy
# text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
copyright = '2020, OpenStack Foundation'
# openstackdocstheme options
openstackdocs_repo_name = 'openstack/sushy-cli'
openstackdocs_use_storyboard = True
openstackdocs_pdf_link = True
# 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
# 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. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
html_theme = 'openstackdocs'
# html_static_path = ['static']
# Output file base name for HTML help builder.
htmlhelp_basename = 'sushyclidoc'
latex_use_xindy = False
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
('index',
'doc-sushycli.tex',
'Sushy CLI Documentation',
'OpenStack Foundation', 'manual'),
]

View File

@ -1,10 +0,0 @@
.. _contributing:
=========================
Contributing to Sushy CLI
=========================
How to contribute
=================
.. include:: ../../../CONTRIBUTING.rst

View File

@ -1,16 +0,0 @@
Welcome to sushycli's documentation!
====================================
.. toctree::
:maxdepth: 2
:caption: Contents:
install/index
reference/usage
contributor/index
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,12 +0,0 @@
====================
Installing Sushy CLI
====================
At the command line::
$ pip install sushy-cli
Or, if you have virtualenvwrapper installed::
$ mkvirtualenv sushy-cli
$ pip install sushy-cli

View File

@ -1,7 +0,0 @@
=====
Usage
=====
To use sushycli in a project::
import sushycli

View File

@ -1,6 +0,0 @@
---
features:
- |
Adds ``manager vmedia`` family of commands. These commands allow to list
available virtual media devices as exposed by the BMC, as well as
mount/umount images served over HTTP.

View File

@ -1,5 +0,0 @@
---
features:
- |
Adds ``system bios`` family of commands. These commands allow to list
BIOS attribues, as exposed by the BMC, and change them.

View File

@ -1,5 +0,0 @@
---
features:
- |
Adds `--insecure` and `--tls-certificates` options to work around
self-signed or invalid TLS certificates sometimes found in BMCs.

View File

@ -1,6 +0,0 @@
---
features:
- |
Adds ``--show-traffic`` command-line option that makes ``sushycli``
pretty-printing full HTTP exchange with Redfish agent. This feature
can be handy for Redfish issues troubleshooting.

View File

@ -1,283 +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.
# Glance Release Notes documentation build configuration file, created by
# sphinx-quickstart on Tue Nov 3 17:40:50 2015.
#
# 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.
# 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',
'reno.sphinxext',
]
# 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 = 'sushy-cli Release Notes'
copyright = '2020, OpenStack Foundation'
# openstackdocstheme options
openstackdocs_repo_name = 'openstack/sushy-cli'
openstackdocs_use_storyboard = True
openstackdocs_auto_name = False
# 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.
# The full version, including alpha/beta/rc tags.
release = ''
# The short X.Y version.
version = ''
# 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 = 'SushyCliReleaseNotesdoc'
# -- 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': '',
}
# 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 = [
('index', 'SushyCliReleaseNotes.tex',
'Sushy CLI Release Notes Documentation',
'Ironic Developers', 'manual'),
]
# 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', 'sushyclireleasenotes',
'Sushy CLI Release Notes Documentation',
['Ironic Developers'], 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', 'SushyCliReleaseNotes',
'Sushy CLI Release Notes Documentation',
'Ironic Developers', 'SushyCliReleaseNotes',
'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
# -- Options for Internationalization output ------------------------------
locale_dirs = ['locale/']

View File

@ -1,11 +0,0 @@
=======================
sushy-cli Release Notes
=======================
.. toctree::
:maxdepth: 1
unreleased
wallaby
victoria
ussuri

View File

@ -1,5 +0,0 @@
============================
Current Series Release Notes
============================
.. release-notes::

View File

@ -1,6 +0,0 @@
===========================
Ussuri Series Release Notes
===========================
.. release-notes::
:branch: stable/ussuri

View File

@ -1,6 +0,0 @@
=============================
Victoria Series Release Notes
=============================
.. release-notes::
:branch: stable/victoria

View File

@ -1,6 +0,0 @@
============================
Wallaby Series Release Notes
============================
.. release-notes::
:branch: stable/wallaby

View File

@ -1,7 +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.
sushy>=3.2.0 # Apache-2.0
cliff>=2.17.0 # Apache-2.0
urllib3>=1.22 # MIT

View File

@ -1,63 +0,0 @@
[metadata]
name = sushy-cli
summary = Redfish CLI tool based on sushy Redfish client library
description_file =
README.rst
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
home_page = https://docs.openstack.org/sushy-cli/latest/
python_requires = >=3.6
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
[files]
packages =
sushycli
[entry_points]
console_scripts =
sushycli = sushycli.cmd.sushycli:main
sushycli =
chassis_inventory_show = sushycli.chassis_inventory:ChassisInventoryShow
chassis_list = sushycli.chassis_list:ChassisList
manager_inventory_show = sushycli.manager_inventory:ManagerInventoryShow
manager_list = sushycli.manager_list:ManagerList
manager_vmedia_eject = sushycli.manager_vmedia:ManagerVmediaEject
manager_vmedia_insert = sushycli.manager_vmedia:ManagerVmediaInsert
manager_vmedia_list = sushycli.manager_vmedia:ManagerVmediaList
system_bios_reset = sushycli.system_bios:SystemBiosReset
system_bios_set = sushycli.system_bios:SystemBiosSet
system_bios_show = sushycli.system_bios:SystemBiosShow
system_boot_set = sushycli.system_boot:SystemBootSet
system_boot_show = sushycli.system_boot:SystemBootShow
system_inventory_show = sushycli.system_inventory:SystemInventoryShow
system_list = sushycli.system_list:SystemList
system_power = sushycli.system_power:SystemPowerSet
system_power_show = sushycli.system_power:SystemPowerShow
version_show = sushycli.version:VersionShow
[compile_catalog]
directory = sushycli/locale
domain = sushycli
[update_catalog]
domain = sushycli
output_dir = sushycli/locale
input_file = sushycli/locale/sushycli.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = sushycli/locale/sushycli.pot

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
setuptools.setup(
setup_requires=['pbr>=2.0.0'],
pbr=True)

View File

@ -1,5 +0,0 @@
"""Top-level package for sushycli."""
__author__ = """khansa amrouni"""
__email__ = 'khansa.amrouni@gmail.com'
__version__ = '0.1.0'

View File

@ -1,191 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 argparse
import json
import sys
from urllib.parse import urlsplit
from urllib.parse import urlunsplit
from cliff import command
from cliff import lister
import sushy
from sushy import connector
import urllib3
class BaseParserMixIn(object):
"""Common bits and pieces of all `sushycli` commands.
Does not implement any CLI command by its own.
"""
def add_parser_options(self, parser):
parser.add_argument(
'--username',
help='Redfish BMC username')
parser.add_argument(
'--password',
help='Redfish BMC user password')
parser.add_argument(
'--service-endpoint',
required=True,
help='Redfish BMC service endpoint URL (e.g. '
'http://localhost:8000). If location part of the URL is not '
'specified, it defaults to `/redfish/v1`.')
parser.add_argument(
'--insecure', action='store_true',
help='Do not verify server TLS certificate')
parser.add_argument(
'--tls-certificates', metavar='<FILE|DIR>',
help='Path to a CA bundle or a directory containing trusted '
'TLS certificates.')
parser.add_argument(
'--show-traffic',
action='store_true',
help='Show Redfish HTTP message exchange.')
return parser
def _to_json(self, data):
"""Turn input object into JSON when possible.
:param data: object to jsonize
:type data: dict or str or None
"""
if not data:
data = {}
elif isinstance(data, (str, bytes)):
try:
data = json.loads(data)
except json.JSONDecodeError as exc:
self.app.LOG.error(
'Malformed JSON document %(doc)s: %(error)s',
{'doc': data, 'error': exc})
data = {}
else:
data = dict(data)
return json.dumps(data, indent=2)
def observer(self, response):
"""Pretty print HTTP request and response details.
:param response: HTTP response object
:type response: requests.Response
"""
sys.stdout.write(
'%s %s\n%s\n%s\n' % (
response.request.method, response.request.url,
self._to_json(response.request.headers),
self._to_json(response.request.body)))
sys.stdout.write(
'%s %s\n%s\n%s\n' % (
response.reason, response.status_code,
self._to_json(response.headers),
self._to_json(response.text)))
sys.stdout.flush()
def take_action(self, args):
"""Common base for all command actions
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
url = urlsplit(args.service_endpoint, scheme='http')
address = []
path = []
for idx, component in enumerate(url):
if idx < 2:
address.append(component)
path.append('')
else:
address.append('')
path.append(component)
base_url = urlunsplit(address)
kwargs = {
'base_url': base_url,
'username': args.username,
'password': args.password,
}
verify = args.tls_certificates or not args.insecure
if verify is not True:
kwargs.update(verify=verify)
path = urlunsplit(path)
if path:
kwargs.update(root_prefix=path)
if args.insecure:
urllib3.disable_warnings(
urllib3.exceptions.InsecureRequestWarning)
conn = connector.Connector(
base_url, verify=verify,
response_callback=args.show_traffic and self.observer)
kwargs.update(connector=conn)
return sushy.Sushy(**kwargs)
class BaseCommand(BaseParserMixIn, command.Command):
"""Common base for all sushycli status commands"""
def get_parser(self, prog_name):
"""Common base for all status command parsers.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(BaseCommand, self).get_parser(prog_name)
# Get correctly formatted help description
parser.formatter_class = argparse.RawDescriptionHelpFormatter
return self.add_parser_options(parser)
class BaseLister(BaseParserMixIn, lister.Lister):
"""Common base for all sushycli listing commands"""
def get_parser(self, prog_name):
"""Common base for all listing command parsers.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(BaseLister, self).get_parser(prog_name)
return self.add_parser_options(parser)

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class BaseParserMixIn(base.BaseParserMixIn):
"""Common bits and pieces of all `sushycli chassis` commands.
Does not implement any CLI command by its own.
"""
CHASSIS_ID_HELP = (
'The canonical path to the Chassis resource to interact with. '
'It should include the root service, version and the unique resource '
'path to a Chassis. For example: /redfish/v1/Chassis/1U'
)
def add_parser_options(self, parser):
parser = super(BaseParserMixIn, self).add_parser_options(parser)
parser.add_argument(
'--chassis-id',
help=self.CHASSIS_ID_HELP)
return parser
class BaseCommand(BaseParserMixIn, base.BaseCommand):
"""Common base for all sushycli chassis status commands"""
class BaseLister(BaseParserMixIn, base.BaseLister):
"""Common base for all sushycli chassis listing commands"""

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class BaseParserMixIn(base.BaseParserMixIn):
"""Common bits and pieces of all `sushycli manager` commands.
Does not implement any CLI command by its own.
"""
MANAGER_ID_HELP = (
'The canonical path to the Manager resource to interact with. It '
'should include the root service, version and the unique resource '
'path to a Manager. For example: /redfish/v1/Managers/BMC'
)
def add_parser_options(self, parser):
parser = super(BaseParserMixIn, self).add_parser_options(parser)
parser.add_argument(
'--manager-id',
help=self.MANAGER_ID_HELP)
return parser
class BaseCommand(BaseParserMixIn, base.BaseCommand):
"""Common base for all sushycli manager status commands"""
class BaseLister(BaseParserMixIn, base.BaseLister):
"""Common base for all sushycli manager listing commands"""

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class BaseParserMixIn(base.BaseParserMixIn):
"""Common bits and pieces of all `sushycli system` commands.
Does not implement any CLI command by its own.
"""
SYSTEM_ID_HELP = (
'The canonical path to the ComputerSystem resource to interact with. '
'It should include the root service, version and the unique resource '
'path to a ComputerSystem. For example: /redfish/v1/Systems/1'
)
def add_parser_options(self, parser):
parser = super(BaseParserMixIn, self).add_parser_options(parser)
parser.add_argument(
'--system-id',
help=self.SYSTEM_ID_HELP)
return parser
class BaseCommand(BaseParserMixIn, base.BaseCommand):
"""Common base for all sushycli system status commands"""
class BaseLister(BaseParserMixIn, base.BaseLister):
"""Common base for all sushycli system listing commands"""

View File

@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
from sushycli import base_manager
class BaseParserMixIn(base.BaseParserMixIn):
"""Common bits and pieces of all `sushycli manager vmedia` commands.
Does not implement any CLI command by its own.
"""
DEVICE_ID_HELP = (
'The canonical path to the Virtual Media Device Manager resource to '
'interact with. It should include the root service, version, '
'the unique resource path to a Manager and the virtual media '
'device id. For example: /redfish/v1/Managers/BMC/VirtualMedia/Cd'
)
def add_parser_options(self, parser):
parser = super(BaseParserMixIn, self).add_parser_options(parser)
parser.add_argument(
'--device-id',
help=self.DEVICE_ID_HELP)
return parser
class BaseCommand(BaseParserMixIn, base_manager.BaseCommand):
"""Common base for all sushycli system status commands"""
class BaseLister(BaseParserMixIn, base_manager.BaseLister):
"""Common base for all sushycli system listing commands"""

View File

@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base_chassis
from sushycli import utils
class ChassisInventoryShow(base_chassis.BaseLister):
"""Show chassis inventory information"""
def take_action(self, args):
"""Show chassis inventory information command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(ChassisInventoryShow, self).take_action(args)
ch_inst = root.get_chassis(args.chassis_id)
columns = [
'Identity', 'Name', 'Description', 'Manufacturer', 'Part Number',
'Serial Number', 'SKU', 'Asset Tag', 'OEM Vendors'
]
return columns, [[utils.get_resource_column(ch_inst, column)
for column in columns]]

View File

@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class ChassisList(base.BaseLister):
"""List available chassis"""
def get_parser(self, prog_name):
"""List chassis command parser.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(ChassisList, self).get_parser(prog_name)
return parser
def take_action(self, args):
"""List chassis command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(ChassisList, self).take_action(args)
chassis = root.get_chassis_collection()
rows = []
for chassis in chassis.get_members():
chassis_inst = root.get_chassis(chassis.path)
rows.append(
[chassis_inst.name, chassis_inst.uuid, chassis.path]
)
columns = [
'Name', 'Identity', 'Chassis ID'
]
return columns, rows

View File

@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sys
from cliff.app import App
from cliff.commandmanager import CommandManager
class SushyCliApp(App):
"""Cliff application for the `sushycli` tool.
:param description: one-liner explaining the program purpose
:param version: application version number
:param command_manager: plugin loader
:param deferred_help: Allow subcommands to accept `help` with allowing
to defer help print after initialize_app
"""
def __init__(self):
super(SushyCliApp, self).__init__(
description='Redfish CLI based on sushy library',
version='0.1',
command_manager=CommandManager('sushycli'),
deferred_help=True)
def initialize_app(self, argv):
self.LOG.debug('initialize_app')
def prepare_to_run_command(self, cmd):
self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)
def clean_up(self, cmd, result, err):
self.LOG.debug('clean_up %s', cmd.__class__.__name__)
if err:
self.LOG.debug('got an error: %s', err)
def main(argv=sys.argv[1:]):
myapp = SushyCliApp()
return myapp.run(argv)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base_manager
from sushycli import utils
class ManagerInventoryShow(base_manager.BaseLister):
"""Show Manager inventory information"""
def take_action(self, args):
"""Show manager inventory information command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(ManagerInventoryShow, self).take_action(args)
mgr_inst = root.get_manager(args.manager_id)
columns = [
'Identity', 'Name', 'Description', 'OEM Vendors'
]
return columns, [[utils.get_resource_column(mgr_inst, column)
for column in columns]]

View File

@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class ManagerList(base.BaseLister):
"""List available managers"""
def get_parser(self, prog_name):
"""List managers command parser.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(ManagerList, self).get_parser(prog_name)
return parser
def take_action(self, args):
"""List managers command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(ManagerList, self).take_action(args)
managers = root.get_manager_collection()
rows = []
for manager in managers.get_members():
manager_inst = root.get_manager(manager.path)
rows.append(
[manager_inst.name, manager_inst.uuid, manager.path]
)
columns = [
'Name', 'Identity', 'Manager ID'
]
return columns, rows

View File

@ -1,114 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base_vmedia
class ManagerVmediaList(base_vmedia.BaseLister):
"""Display a list of available virtual media devices identities."""
def take_action(self, args):
"""List available virtual media devices command action"""
root = super(ManagerVmediaList, self).take_action(args)
mgr_inst = root.get_manager(args.manager_id)
return (['Virtual Media ID'], [[member]
for member in mgr_inst.virtual_media.members_identities])
class ManagerVmediaInsert(base_vmedia.BaseCommand):
"""Insert a remote media into the virtual media device.
This command allows to insert a HTTP-based image into the virtual device.
Note:
All systems being managed by this manager and booting from their
corresponding removable media device (e.g. cdrom or fd) will boot
the image inserted into managers virtual media device.
"""
def get_parser(self, prog_name):
"""Insert virtual media command parser"""
parser = super(ManagerVmediaInsert, self).get_parser(prog_name)
parser.add_argument(
'--image',
required=True,
help='The image path for virtual media access. '
'The value is a HTTP based URL.')
return parser
def take_action(self, args):
"""Insert virtual media command action"""
root = super(ManagerVmediaInsert, self).take_action(args)
mgr_inst = root.get_manager(args.manager_id)
vm_inst = mgr_inst.virtual_media.get_member(args.device_id)
vm_inst.insert_media(args.image)
return 0
class ManagerVmediaEject(base_vmedia.BaseCommand):
"""Detach one or all the virtual media device(s).
Note:
All removable media device of all systems being
managed by this manager will be ejected.
"""
MULTIPLE_SYSTEMS_WARNING = """"
WARNING! Multiple managed systems may be affected! If you are sure that
you want to continue this operation, please use --force argument!
"""
def get_parser(self, prog_name):
"""Eject virtual media command parser"""
parser = super(ManagerVmediaEject, self).get_parser(prog_name)
parser.add_argument(
'--force',
nargs='?',
const='Yes',
help='Virtual media force eject when multiple '
'systems has been managed by the manager')
return parser
def take_action(self, args):
"""Eject virtual media command action"""
root = super(ManagerVmediaEject, self).take_action(args)
mgr_inst = root.get_manager(args.manager_id)
virtmedia_col = mgr_inst.virtual_media
vm_inst = virtmedia_col.get_member(args.device_id)
if len(mgr_inst.systems) > 1 and not args.force:
print(self.MULTIPLE_SYSTEMS_WARNING)
return 0
vm_inst.eject_media()
return 0

View File

@ -1,121 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 argparse
from sushycli import base_system
class SystemBiosShow(base_system.BaseLister):
"""Show machine BIOS attributes"""
def take_action(self, args):
"""System BIOS show command action
:param args: a namespace of command-line attribute-value
pairs that come from the user
:returns: columns, values of data to be listed.
"""
root = super(SystemBiosShow, self).take_action(args)
sys_inst = root.get_system(args.system_id)
return (['BIOS attribute', 'BIOS value'],
[[attribute[0], attribute[1]]
for attribute in sys_inst.bios.attributes.items()])
class SystemBiosReset(base_system.BaseCommand):
"""Reset machine BIOS attributes to default"""
def take_action(self, args):
"""System BIOS reset command action
:param args: a namespace of command-line attribute-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(SystemBiosReset, self).take_action(args)
sys_inst = root.get_system(args.system_id)
sys_inst.bios.reset_bios()
return 0
class SystemBiosSet(base_system.BaseCommand):
"""Update the system BIOS attribute(s)
This command allows updating BIOS attributes, On the command line,
a declaration should typically be a number of key-value pairs.
:Example: foo=hello or foo="hello"
.. note:: Attributes update is not immediate but might require
system restart.
"""
def get_parser(self, prog_name):
"""Set system BIOS command parser
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(SystemBiosSet, self).get_parser(prog_name)
parser.add_argument(
'set',
nargs='+',
metavar="KEY=VALUE",
action=ParseDict,
help='Set BIOS attributes, Note that you should not '
'put spaces before or after the = sign.\n'
'If a value contains spaces, you should define '
'it with double quotes, for example:\n '
'foo="this is a sentence".')
return parser
def take_action(self, args):
"""Set system BIOS command action
:param args: a namespace of command-line attribute-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(SystemBiosSet, self).take_action(args)
sys_inst = root.get_system(args.system_id)
sys_inst.bios.set_attributes(args.set)
return 0
class ParseDict(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
d = {}
if values:
for item in values:
split_items = item.split("=", 1)
# we remove blanks around keys
key = split_items[0].strip()
value = split_items[1]
d[key] = value
setattr(namespace, self.dest, d)

View File

@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushy
from sushycli import base_system
class SystemBootShow(base_system.BaseLister):
"""Show system boot information"""
def take_action(self, args):
"""Show system boot information command action"""
root = super(SystemBootShow, self).take_action(args)
sys_inst = root.get_system(args.system_id)
colunms = [
'Boot mode',
'Available boot modes',
'Boot device',
'Available boot devices'
]
boot_modes = sorted(sys_inst.boot.allowed_values)
boot_devices = sorted(
sys_inst.get_allowed_system_boot_source_values())
values = [[sys_inst.boot.mode, ', '.join(boot_modes),
sys_inst.boot.target, ', '.join(boot_devices)]]
return colunms, values
class SystemBootSet(base_system.BaseCommand):
"""Set the system boot mode/device
Change system boot mode and device specifying the frequency;
either it is disabled, applied once or persistent for future reboots
"""
def get_parser(self, prog_name):
"""Set boot device command parser"""
parser = super(SystemBootSet, self).get_parser(prog_name)
parser.add_argument(
'--target',
choices=['none', 'pxe', 'floppy', 'cd', 'usb', 'hdd'],
type=lambda x: x.lower(),
required=True,
help='the target boot source')
parser.add_argument(
'--enabled',
type=lambda x: x.lower(),
choices=['disabled', 'once', 'continuous'],
help='Next reboot only or persistent to all future reboots, '
'default is set to boot once')
parser.add_argument(
'--mode',
choices=['bios', 'uefi'],
type=lambda x: x.lower(),
help='the machine boot mode')
return parser
def take_action(self, args):
"""Set boot device command action"""
root = super(SystemBootSet, self).take_action(args)
sys_inst = root.get_system(args.system_id)
sys_inst.set_system_boot_source(
args.target,
enabled=args.enabled,
mode=sushy.BOOT_SOURCE_MODE_UEFI
if args.mode == 'uefi' else sushy.BOOT_SOURCE_MODE_BIOS)
return 0

View File

@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base_system
from sushycli import utils
class SystemInventoryShow(base_system.BaseLister):
"""Show system inventory information"""
def take_action(self, args):
"""Show system inventory information command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(SystemInventoryShow, self).take_action(args)
sys_inst = root.get_system(args.system_id)
columns = [
'Identity', 'Name', 'Description', 'Manufacturer', 'Part Number',
'Serial Number', 'SKU', 'Asset Tag', 'OEM Vendors'
]
return columns, [[utils.get_resource_column(sys_inst, column)
for column in columns]]

View File

@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class SystemList(base.BaseLister):
"""List available systems"""
def get_parser(self, prog_name):
"""List systems command parser.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(SystemList, self).get_parser(prog_name)
return parser
def take_action(self, args):
"""List systems command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(SystemList, self).take_action(args)
systems = root.get_system_collection()
rows = []
for system in systems.get_members():
sys_inst = root.get_system(system.path)
rows.append(
[sys_inst.name, sys_inst.uuid, system.path]
)
columns = [
'Name', 'Identity', 'System ID'
]
return columns, rows

View File

@ -1,74 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushy
from sushycli import base_system
class SystemPowerShow(base_system.BaseLister):
"""Show machine power state"""
def take_action(self, args):
"""Power state management command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(SystemPowerShow, self).take_action(args)
sys_inst = root.get_system(args.system_id)
return ['Power state'], [[sys_inst.power_state]]
class SystemPowerSet(base_system.BaseCommand):
"""Change machine power state"""
def get_parser(self, prog_name):
"""Power state management command parser.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(SystemPowerSet, self).get_parser(prog_name)
parser.add_argument(
'state',
metavar='on|off',
type=lambda x: x.lower(),
choices=['on', 'off'],
help='Set machine power state')
return parser
def take_action(self, args):
"""Power state management command action.
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(SystemPowerSet, self).take_action(args)
sys_inst = root.get_system(args.system_id)
sys_inst.reset_system(
sushy.RESET_TYPE_ON
if args.state == 'on' else sushy.RESET_TYPE_FORCE_OFF)
return 0

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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 oslotest import base
class TestCase(base.BaseTestCase):
"""Test case base class for all unit tests"""

View File

@ -1,813 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2020 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.
from unittest import mock
import sushy
from sushycli.cmd.sushycli import main
from sushycli.tests.unit import base
@mock.patch.object(sushy, 'Sushy', autospec=True)
@mock.patch.object(sushy.connector, 'Connector', autospec=True)
class SuchyCliTestCase(base.TestCase):
@mock.patch('sys.stdout.write', autospec=True)
def test_show_traffic(self, mock_write, mock_connector, mock_sushy):
main(['version', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me', '--show-traffic'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=mock.ANY, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
@mock.patch('sys.stdout.write', autospec=True)
def test_insecure(self, mock_write, mock_connector, mock_sushy):
main(['version', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me', '--insecure'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=False)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly', verify=False)
@mock.patch('sys.stdout.write', autospec=True)
def test_tls_certificate(self, mock_write, mock_connector, mock_sushy):
main(['version', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--tls-certificate', '/dev/null'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify='/dev/null')
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly', verify='/dev/null')
@mock.patch('sys.stdout.write', autospec=True)
def test_service_endpoint_default(
self, mock_write, mock_connector, mock_sushy):
main(['version', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
@mock.patch('sys.stdout.write', autospec=True)
def test_service_endpoint_mounted(
self, mock_write, mock_connector, mock_sushy):
main(['version', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me:1234/out'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me:1234', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me:1234', connector=mock_connection,
password='fish', username='jelly',
root_prefix='/out')
@mock.patch('sys.stdout.write', autospec=True)
def test_version(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_root.redfish_version = '1.2.3'
main(['version', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+---------+\n'
'| Version |'
'\n+---------+\n'
'| 1.2.3 |\n'
'+---------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_manager_vmedia_list(
self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_manager = mock_root.get_manager.return_value
mock_manager.virtual_media.\
members_identities = (
'/redfish/v1/Managers/58893887-8974-2487-2389-841168418919/'
'VirtualMedia/Cd',
'/redfish/v1/Managers/58893887-8974-2487-2389-841168418919/'
'VirtualMedia/Floppy'
)
main(['manager', 'vmedia', 'list',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--manager-id', '/redfish/v1/Managers/BMC'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+-----------------------------------------'
'--------------------------------------+\n'
'| Virtual Media ID '
' |\n'
'+-----------------------------------------'
'--------------------------------------+\n'
'| /redfish/v1/Managers/58893887-8974-2487-'
'2389-841168418919/VirtualMedia/Cd |\n'
'| /redfish/v1/Managers/58893887-8974-2487-'
'2389-841168418919/VirtualMedia/Floppy |\n'
'+----------------------------------------'
'---------------------------------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
def test_manager_vmedia_insert(self, mock_connector, mock_sushy):
main(['manager', 'vmedia', 'insert',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--manager-id', '/redfish/v1/Managers/BMC',
'--device-id', '/redfish/v1/Managers/BMC/VirtualMedia/Cd',
'--image', 'http://fish.me/fishiso.iso'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root = mock_sushy.return_value
mock_root.get_manager.assert_called_once_with(
'/redfish/v1/Managers/BMC')
mock_manager = mock_root.get_manager.return_value
mock_vmedia = mock_manager.virtual_media.\
get_member('/redfish/v1/Managers/BMC/VirtualMedia/Cd')
mock_vmedia.insert_media.\
assert_called_once_with('http://fish.me/fishiso.iso')
def test_manager_vmedia_eject(self, mock_connector, mock_sushy):
main(['manager', 'vmedia', 'eject',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--manager-id', '/redfish/v1/Managers/BMC',
'--device-id', '/redfish/v1/Managers/BMC/VirtualMedia/Cd'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root = mock_sushy.return_value
mock_root.get_manager.assert_called_once_with(
'/redfish/v1/Managers/BMC')
mock_manager = mock_root.get_manager.return_value
mock_vmedia = mock_manager.virtual_media\
.get_member('/redfish/v1/Managers/BMC/VirtualMedia/Cd')
mock_vmedia.eject_media.assert_called_once()
@mock.patch('sys.stdout.write', autospec=True)
def test_system_boot_show(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock_root.get_system.return_value
mock_system.boot.mode = 'Disabled'
mock_system.boot.allowed_values = ['Pxe', 'Cd', 'Hdd']
mock_gasbsv = mock_system.get_allowed_system_boot_source_values
mock_gasbsv.return_value = {'hdd', 'pxe', 'cd'}
mock_system.boot.target = 'Hdd'
main(['system', 'boot', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+-----------+----------------------+-------------+----'
'--------------------+\n| Boot mode | Available boot mo'
'des | Boot device | Available boot devices |\n+-------'
'----+----------------------+-------------+------------'
'------------+\n| Disabled | Cd, Hdd, Pxe | Hd'
'd | cd, hdd, pxe |\n+-----------+---'
'-------------------+-------------+--------------------'
'----+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_system_bios_show(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock_root.get_system.return_value
mock_system.bios.description = None
mock_system.bios.attributes = {'BootMode': 'Uefi',
'EmbeddedSata': 'Raid',
'NicBoot1': 'NetworkBoot',
'ProcTurboMode': 'Enabled'}
main(['system', 'bios', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+----------------+-------------+\n'
'| BIOS attribute | BIOS value |\n'
'+----------------+-------------+\n'
'| BootMode | Uefi |\n'
'| EmbeddedSata | Raid |\n'
'| NicBoot1 | NetworkBoot |\n'
'| ProcTurboMode | Enabled |\n'
'+----------------+-------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
def test_system_bios_reset(self, mock_connector, mock_sushy):
main(['system', 'bios', 'reset',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root = mock_sushy.return_value
mock_root.get_system.assert_called_once_with(
'/redfish/v1/Systems/1')
mock_system = mock_root.get_system.return_value
mock_system.bios.reset_bios.assert_called_once()
def test_system_bios_set(self, mock_connector, mock_sushy):
main(['system', 'bios', 'set',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1',
'BootMode=Uefi', 'EmbeddedSata=Raid',
'NicBoot1=NetworkBoot', 'ProcTurboMode=Enabled'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root = mock_sushy.return_value
mock_root.get_system.assert_called_once_with(
'/redfish/v1/Systems/1')
mock_system = mock_root.get_system.return_value
mock_system.bios.set_attributes.assert_called_once_with(
{
"BootMode": "Uefi",
"EmbeddedSata": "Raid",
"NicBoot1": "NetworkBoot",
"ProcTurboMode": "Enabled"
}
)
def test_system_bios_set_with_keys_blanks(
self, mock_connector, mock_sushy):
main(['system', 'bios', 'set',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1',
'BootMode =Uefi', ' EmbeddedSata=Raid'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root = mock_sushy.return_value
mock_root.get_system.assert_called_once_with(
'/redfish/v1/Systems/1')
mock_system = mock_root.get_system.return_value
mock_system.bios.set_attributes.assert_called_once_with(
{
"BootMode": "Uefi",
"EmbeddedSata": "Raid",
}
)
def test_system_boot_set(self, mock_connector, mock_sushy):
main(['system', 'boot', 'set',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1',
'--target', 'cd',
'--enabled', 'once',
'--mode', 'uefi'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root = mock_sushy.return_value
mock_root.get_system.assert_called_once_with(
'/redfish/v1/Systems/1')
mock_system = mock_root.get_system.return_value
mock_system.set_system_boot_source.\
assert_called_once_with(sushy.BOOT_SOURCE_TARGET_CD,
enabled=sushy.BOOT_SOURCE_ENABLED_ONCE,
mode=sushy.BOOT_SOURCE_MODE_UEFI)
@mock.patch('sys.stdout.write', autospec=True)
def test_chassis_inventory_show(
self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_one_chassis = mock_root.get_chassis.return_value
mock_one_chassis.identity = 'A'
mock_one_chassis.name = 'B'
mock_one_chassis.description = 'C'
mock_one_chassis.manufacturer = 'D'
mock_one_chassis.part_number = 'E'
mock_one_chassis.serial_number = 'F'
mock_one_chassis.sku = 'G'
mock_one_chassis.asset_tag = 'H'
mock_one_chassis.oem_vendors = ['I', 'J']
main(['chassis', 'inventory', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--chassis-id', '/redfish/v1/Chassis/1U'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+----------+------+-------------+--------------+-----'
'--------+---------------+-----+-----------+----------'
'---+\n| Identity | Name | Description | Manufacturer '
'| Part Number | Serial Number | SKU | Asset Tag | OEM'
' Vendors |\n+----------+------+-------------+--------'
'------+-------------+---------------+-----+----------'
'-+-------------+\n| A | B | C | D'
' | E | F | G | H '
' | I, J |\n+----------+------+----------'
'---+--------------+-------------+---------------+----'
'-+-----------+-------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_manager_inventory_show(
self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_manager = mock_root.get_manager.return_value
mock_manager.identity = 'A'
mock_manager.name = 'B'
mock_manager.description = 'C'
mock_manager.manufacturer = 'D'
mock_manager.part_number = 'E'
mock_manager.serial_number = 'F'
mock_manager.sku = 'G'
mock_manager.asset_tag = 'H'
mock_manager.oem_vendors = ['I', 'J']
main(['manager', 'inventory', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--manager-id', '/redfish/v1/Mnagers/BMC'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+----------+------+-------------+-------------+\n| Id'
'entity | Name | Description | OEM Vendors |\n+-------'
'---+------+-------------+-------------+\n| A |'
' B | C | I, J |\n+----------+----'
'--+-------------+-------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_system_list(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock.MagicMock()
mock_system.identity = 'A'
mock_system.name = 'B'
mock_system.uuid = '38947555-7742-3448-3784-823347823834'
mock_system.path = '/redfish/v1/Systems/437XR1138R2'
mock_systems = mock_root.get_system_collection.return_value
mock_systems.get_members.return_value = [mock_system]
mock_root.get_system.return_value = mock_system
main(['system', 'list',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+------+--------------------------------------+------'
'---------------------------+\n| Name | Identity '
' | System ID '
' |\n+------+--------------------------------------'
'+---------------------------------+\n| B | 3894755'
'5-7742-3448-3784-823347823834 | /redfish/v1/Systems/4'
'37XR1138R2 |\n+------+-------------------------------'
'-------+---------------------------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_manager_list(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_manager = mock.MagicMock()
mock_manager.identity = 'A'
mock_manager.name = 'B'
mock_manager.uuid = '38947555-7742-3448-3784-823347823834'
mock_manager.path = '/redfish/v1/Managers/BMC'
mock_managers = mock_root.get_manager_collection.return_value
mock_managers.get_members.return_value = [mock_manager]
mock_root.get_manager.return_value = mock_manager
main(['manager', 'list',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+------+--------------------------------------+------'
'--------------------+\n| Name | Identity '
' | Manager ID |\n+------'
'+--------------------------------------+-------------'
'-------------+\n| B | 38947555-7742-3448-3784-8233'
'47823834 | /redfish/v1/Managers/BMC |\n+------+------'
'--------------------------------+--------------------'
'------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_chassis_list(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_one_chassis = mock.MagicMock()
mock_one_chassis.identity = 'A'
mock_one_chassis.name = 'B'
mock_one_chassis.uuid = '38947555-7742-3448-3784-823347823834'
mock_one_chassis.path = '/redfish/v1/Chassis/1U'
mock_chassis = mock_root.get_chassis_collection.return_value
mock_chassis.get_members.return_value = [mock_one_chassis]
mock_root.get_chassis.return_value = mock_one_chassis
main(['chassis', 'list',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+------+--------------------------------------+------'
'------------------+\n| Name | Identity '
' | Chassis ID |\n+------+---'
'-----------------------------------+-----------------'
'-------+\n| B | 38947555-7742-3448-3784-8233478238'
'34 | /redfish/v1/Chassis/1U |\n+------+--------------'
'------------------------+------------------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_system_inventory_show(
self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock_root.get_system.return_value
mock_system.identity = 'A'
mock_system.name = 'B'
mock_system.description = 'C'
mock_system.manufacturer = 'D'
mock_system.part_number = 'E'
mock_system.serial_number = 'F'
mock_system.sku = 'G'
mock_system.asset_tag = 'H'
mock_system.oem_vendors = ['I', 'J']
main(['system', 'inventory', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+----------+------+-------------+--------------+-----'
'--------+---------------+-----+-----------+----------'
'---+\n| Identity | Name | Description | Manufacturer '
'| Part Number | Serial Number | SKU | Asset Tag | OEM'
' Vendors |\n+----------+------+-------------+--------'
'------+-------------+---------------+-----+----------'
'-+-------------+\n| A | B | C | D'
' | E | F | G | H '
' | I, J |\n+----------+------+----------'
'---+--------------+-------------+---------------+----'
'-+-----------+-------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
@mock.patch('sys.stdout.write', autospec=True)
def test_system_power_show(self, mock_write, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock_root.get_system.return_value
mock_system.power_state = 'on'
main(['system', 'power', 'show',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
expected_calls = [
mock.call('+-------------+\n'
'| Power state |\n'
'+-------------+\n'
'| on |\n'
'+-------------+'),
mock.call('\n')
]
mock_write.assert_has_calls(expected_calls)
def test_system_power_on(self, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock_root.get_system.return_value
mock_system.power_state = 'off'
main(['system', 'power',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1',
'on'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root.get_system.assert_called_once_with('/redfish/v1/Systems/1')
mock_system = mock_root.get_system.return_value
mock_system.reset_system.assert_called_once_with('on')
def test_system_power_off(self, mock_connector, mock_sushy):
mock_root = mock_sushy.return_value
mock_system = mock_root.get_system.return_value
mock_system.power_state = 'on'
main(['system', 'power',
'--username', 'jelly', '--password', 'fish',
'--service-endpoint', 'http://fish.me',
'--system-id', '/redfish/v1/Systems/1',
'Off'])
mock_connection = mock_connector.return_value
mock_connector.assert_called_once_with(
'http://fish.me', response_callback=False, verify=True)
mock_sushy.assert_called_once_with(
'http://fish.me', connector=mock_connection,
password='fish', username='jelly')
mock_root.get_system.assert_called_once_with('/redfish/v1/Systems/1')
mock_system = mock_root.get_system.return_value
mock_system.reset_system.assert_called_once_with('force off')

View File

@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2020 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.
from unittest import mock
from sushycli.base import BaseParserMixIn
from sushycli.tests.unit import base
class BaseParserMixInTestCase(base.TestCase):
def setUp(self):
super(BaseParserMixInTestCase, self).setUp()
self.obj = BaseParserMixIn()
def test__to_json_from_none(self):
output = self.obj._to_json(None)
expected = '{}'
self.assertEqual(expected, output)
def test__to_json_from_dict(self):
output = self.obj._to_json({'test': 1})
expected = '{\n "test": 1\n}'
self.assertEqual(expected, output)
def test__to_json_from_json(self):
output = self.obj._to_json('{"test": 1}')
expected = '{\n "test": 1\n}'
self.assertEqual(expected, output)
def test__to_json_malformed(self):
self.obj.app = mock.MagicMock()
output = self.obj._to_json('not json')
expected = '{}'
self.assertEqual(expected, output)
self.assertEqual(1, self.obj.app.LOG.error.call_count)

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2020 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.
def get_resource_column(resource, attr):
"""Pull and format resource column"""
attr = attr.lower().replace(' ', '_')
value = getattr(resource, attr)
if isinstance(value, list):
value = ', '.join(str(x) for x in value)
return value

View File

@ -1,44 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2020 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 sushycli import base
class VersionShow(base.BaseLister):
"""Show Redfish protocol version implemented by the BMC
Implements `sushycli version show` command.
"""
def get_parser(self, prog_name):
"""Redfish version command parser.
:param prog_name: name of the cliff command being executed
:returns: an `argparse.ArgumentParser` instance
"""
parser = super(VersionShow, self).get_parser(prog_name)
return parser
def take_action(self, args):
"""Redfish version command action
:param args: a namespace of command-line option-value pairs that
come from the user
:returns: CLI process exit code
"""
root = super(VersionShow, self).take_action(args)
return ['Version'], [[root.redfish_version]]

View File

@ -1,9 +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.
coverage!=4.4,>=4.0 # Apache-2.0
oslotest>=3.2.0 # Apache-2.0
stestr>=2.0.0 # Apache-2.0
testtools>=2.2.0 # MIT

74
tox.ini
View File

@ -1,74 +0,0 @@
[tox]
minversion = 3.9.0
envlist = py3, pep8
skipsdist = True
ignore_basepython_conflict = true
[testenv]
usedevelop = True
basepython = python3
setenv =
VIRTUAL_ENV = {envdir}
PYTHONWARNINGS = default::DeprecationWarning
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt
commands = stestr run {posargs}
stestr slowest
[testenv:docs]
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -b html -W doc/source doc/build/html
[testenv:pdf-docs]
whitelist_externals = make
deps = {[testenv:docs]deps}
commands =
sphinx-build -W -b latex doc/source doc/build/pdf
make -C doc/build/pdf
[testenv:cover]
setenv =
{[testenv]setenv}
PYTHON=coverage run --parallel-mode
# After running this target, visit sushycli/cover/index.html
# in your browser, to see a nicer presentation report with annotated
# HTML listings detailing missed lines.
commands = coverage erase
stestr run {posargs}
coverage combine
coverage report
coverage html
coverage xml -o cover/coverage.xml
[testenv:releasenotes]
deps = {[testenv:docs]deps}
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:pep8]
deps=
hacking>=3.1.0,<4.0.0 # Apache-2.0
flake8-import-order>=0.17.1 # LGPLv3
pycodestyle>=2.0.0,<2.7.0 # MIT
commands = flake8 {posargs}
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
ignore = E123, E125
builtins = _
exclude = .venv, .git, .tox, dist, doc, *lib/python*, *egg,build
import-order-style = pep8
application-import-names = sushycli
filename = *.py
[testenv:lower-constraints]
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt

View File

@ -1,7 +0,0 @@
- project:
templates:
- check-requirements
- openstack-cover-jobs
- openstack-python3-xena-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3