diff options
author | Michal Minar <miminar@redhat.com> | 2014-04-22 15:19:56 +0200 |
---|---|---|
committer | Michal Minar <miminar@redhat.com> | 2014-04-24 09:58:44 +0200 |
commit | 2a78da4f98a583a382c553f2e1fc68299210e7d5 (patch) | |
tree | 53029ae0a8474a9ddc464419da7337d0d88dea13 | |
parent | 9138628a62461fd8914d5b82ec6a4ace964a4b2b (diff) | |
download | openlmi-scripts-2a78da4f98a583a382c553f2e1fc68299210e7d5.tar.gz openlmi-scripts-2a78da4f98a583a382c553f2e1fc68299210e7d5.tar.xz openlmi-scripts-2a78da4f98a583a382c553f2e1fc68299210e7d5.zip |
moved lmi meta-command to openlmi-tools repository
136 files changed, 3 insertions, 15147 deletions
diff --git a/Changelog b/Changelog deleted file mode 100644 index 25c39d6..0000000 --- a/Changelog +++ /dev/null @@ -1,67 +0,0 @@ -0.2.7 -* errors handled more gracefully, they are more helpful -* colorized error log messages -* interactive mode won't be ended by error so easily -* redone help command -* help command accepts command path -* added built-in commands - starting with ':' -* allowed for nesting into subcommands in interactive mode -* better tab completion -* added formatting properties to commands -* fixed --log-file option - -0.2.6 -* allow digits in command names and options - -0.2.5 -* terminate with exit code of the last executed command -* exit interactive mode only on user request - -0.2.4 -* add get_computer_system() utility function -* require at least lmi shell v0.8 -* correct support for lmi shell v0.8 and above - -0.2.3 -* made localhost a default host -* allow to have a fallback command for command multiplexers -* fixed rendering of instances when PROPERTIES are omitted - -0.2.2 -* double and single dash can now be used in usage strings -* fixed error handling -* improved logging of errors - handled the same way for all commands -* added [Log] LogToConsole configuration option -* added --notrace and --log-file options -* logging configuration fixes -* support for openlmi-python-base 0.2.1+ - -0.2.1 -* compability fix for newer openlmi-tools -* enhanced check_result() method invocation -* fixed handling of verbosity parameter -* fixed handling of ARG_ARRAY_SUFFIX - -0.2.0 -* allow embedded credentials in urls of hosts -* allow to override default namespace from config and command line -* added OPT_NO_UNDERSCORES property -* commands api change: replace any sequence of characters matching [^a-z_] -with a single underscore, when renaming options to function arguments -* added LmiInstanceLister base command class -* fixed exit command -* added ARG_ARRAY_SUFFIX -* added --same-credentials flag -* fixed parsing of hosts file - -0.1.1 -* support for (non)verification of server-side certificates -* allowed *connection* to be an argument in command usage strings -* fixed command declaration checking -* relicensed to BSD -* added namespace property to end-point commands -* associated functions invoked with namespace rather then just connection -object - -0.1.0 -* initial version with dependency only on docopt @@ -1,14 +1,12 @@ -include Makefile.inc - -COMMANDS ?= $(shell find commands -mindepth 1 -maxdepth 1 -type d) +COMMANDS ?= $(notdir $(shell find commands -mindepth 1 -maxdepth 1 -type d)) # all rules executable on meta-command and commands RULES := setup upload upload_docs clean develop MASSRULES := $(foreach rule,$(RULES),$(rule)-all) .PHONY: $(MASSRULES) -$(MASSRULES): %-all: % +$(MASSRULES): %-all: # executes rule for metacommand and for all commands found for cmd in $(COMMANDS); do \ - make -C $$cmd $*; \ + make -C commands/$$cmd $*; \ done diff --git a/completion/lmi-bash-completion/commands/_help b/completion/lmi-bash-completion/commands/_help deleted file mode 100755 index ae3ed8b..0000000 --- a/completion/lmi-bash-completion/commands/_help +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -if (( $# > 1 )); then - exit -fi -helpers_path=lmi-bash-completion -if ! [[ -e $helpers_path ]]; then - helpers_path="/usr/libexec/$helpers_path" -fi -compgen "-W $($helpers_path/print_possible_commands.sh)" -- "$1" diff --git a/completion/lmi-bash-completion/print_possible_commands.sh b/completion/lmi-bash-completion/print_possible_commands.sh deleted file mode 100755 index ba29a4a..0000000 --- a/completion/lmi-bash-completion/print_possible_commands.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# prints possible commands -# one per line -# parse lmi help -re_command="^[[:blank:]]*([[:alnum:]]+)[[:blank:]]*-" -while IFS= read line; do - if [[ $line =~ $re_command ]]; then - printf '%s\n' "${BASH_REMATCH[1]}" - fi -done < <(lmi help 2>/dev/null) diff --git a/completion/lmi.bash b/completion/lmi.bash deleted file mode 100644 index c60ab9b..0000000 --- a/completion/lmi.bash +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Authors: Roman Rakus <rrakus@redhat.com> -# -# Bash completion for LMI commands - -_lmi() { - local helpers_path="lmi-bash-completion" - if ! [[ -e "$helpers_path" ]]; then - helpers_path="/usr/libexec/$helpers_path" - fi - local options=(-c --config-file -h --host --hosts-file --user -v --trace -q --quiet -n --noverify --same-credentials --help --version) - local current="${COMP_WORDS[$COMP_CWORD]}" - local previous="${COMP_WORDS[COMP_CWORD-1]}" - local commands=( $($helpers_path/print_possible_commands.sh) ) - local used_command= - - for (( i=1; i < ${#COMP_WORDS[@]} - 1 && i < $COMP_CWORD; i++ )); do - for (( ci=0; ci < ${#commands[@]}; ci++ )); do - if [[ ${COMP_WORDS[$i]} == ${commands[$ci]} ]]; then - used_command=${commands[$ci]} - break 2; - fi - done - done - - if [[ $used_command ]]; then - # Check if we have completion executable for command - if [[ -x $helpers_path/commands/"_$used_command" ]] ; then - # pass the rest of words typed as parameters - COMPREPLY=( $($helpers_path/commands/_$used_command "${COMP_WORDS[@]:$((i+1))}") ) - else - # commands without completion - filename completion - COMPREPLY=( $(compgen -f -- "$current") ) - fi - else - case $current in - -*) COMPREPLY=( $(compgen "-W ${options[*]}" -- "$current" ) );; - *) case $previous in - -c|--config-file|--hosts-file) COMPREPLY=( $(compgen -f -- "$current" ) );; - -h|--host) COMPREPLY=( $(compgen -A hostname -- "$current" ) );; - --user) COMPREPLY=( $(compgen -u -- "$current" ) );; - --help|--version) ;; - *) COMPREPLY=( $(compgen "-W ${commands[*]}" -- "$current" ) );; - esac - esac - fi -} - -complete -F _lmi lmi diff --git a/config/lmi.conf b/config/lmi.conf deleted file mode 100644 index 6b35fc8..0000000 --- a/config/lmi.conf +++ /dev/null @@ -1,66 +0,0 @@ -# Sample configuration file for OpenLMI script meta-command. - -[Main] -# Python namespace, where command entry points will be searched for. -#CommandNamespace = lmi.scripts.cmd - -# Whether the exceptions should be logged with tracebacks. -#Trace = False - -# A number within 0-2 range saying, how verbose the output to console shall -# be. This differs from [Log] Level, which controls the logging messages being -# logged to file. If logging to console is enabled it sets the minimum -# severity level. -1 Suppresses all messages except for errors. 0 shows -# warnings, 1 info messages and 2 enables debug messages. This option also -# affects the verbosity of commands, making them print more information to -# stdout. -#Verbosity = 0 - -# Maximum number of lines stored in history file. -#HistoryMaxLength = 4000 - -[CIM] -# To override default CIM namespace, uncomment the line below. -#namespace = root/cimv2 - -[SSL] -# Whether to verify server-side certificate, when making secured connection -# over ssl. -#VerifyServerCertificate = True - -[Format] -# Whether to print values in human readable forms (e.g. with units). -#HumanFriendly = False - -# What format to use, when listing tabular data. Possible values are: -# {csv, table}. The former allows for easy machine parsing, the latter -# is more human friendly. -#ListerFormat = table - -# Whether to suppress headings (column names) when printing tables. -#NoHeadings = False - -[Log] -# Level can be set to following values: -# DEBUG, INFO, WARNING, ERROR, CRITICAL -# Affects only logging to a file. -#Level = ERROR - -# Format string used, when logging to a console. For details visit: -# http://docs.python.org/2/library/logging.html#logrecord-attributes -# This applies to levels more severe than INFO. -#ConsoleFormat = %(cseq)s%(levelname)-8s:%(creset)s %(message)s - -# Format string used, when logging to a console. This applies to all messages -# with severity level up to INFO. -#ConsoleInfoFormat = %(message)s - -# Whether the logging to console is enabled. -#LogToConsole = True - -# Format string used, when logging to a file. -#FileFormat = %(asctime)s:%(levelname)-8s:%(name)s:%(lineno)d - %(message)s - -# Allows to set a path to file, where messages will be logged. No log -# file is written at default. -#OutputFile = diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 0900330..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,236 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR ?= _build -PROJECTNAME = OpenLMIScripts -COMMANDS_PATH ?= ../commands -INCLUDE_COMMANDS ?= 0 -COMMANDS ?= logicalfile service software storage - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -n -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -TUTORIAL_SOURCES := mylf/*.md mylf/*.py mylf/lmi/*.py mylf/*/*.py mylf/*/*/*.py \ - mylf/doc/*.py mylf/doc/*.rst mylf/doc/Makefile mylf/doc/_static -TUTORIAL_TARBALL := _static/mylf.tar.gz - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp \ - devhelp epub latex latexpdf text man changes linkcheck doctest gettext \ - commands.rst - -export COMMANDS -export INCLUDE_COMMANDS - -all: html - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -api.rst: gendoc.sh $(wildcard ../lmi/scripts/*.py \ - ../lmi/scripts/common/*.py \ - ../lmi/scripts/_metacommand/*.py \ - ../lmi/scripts/formatter/*.py) - ./gendoc.sh - -commands.rst: - echo "Command libraries documentation" > $@ - echo "===============================" >> $@ -ifneq ($(INCLUDE_COMMANDS), 0) - -[ -e commands ] && rm -rf commands - mkdir commands - echo "Usage and developer documentations of provider libraries" >> $@ - echo "(also called *commands*.)" >> $@ - printf "\n.. toctree::\n" >> $@ - printf " :maxdepth: 2\n\n" >> $@ - for cmd in $(COMMANDS); do \ - printf " commands/$$cmd\n" >> $@; \ - ln -s ../$(COMMANDS_PATH)/$${cmd}/doc commands/$${cmd}_docdir; \ - title="$${cmd^} library"; \ - length=`echo "$$title" | wc -c`; \ - ( \ - printf "$${title}\n"; \ - printf -- "!%.0s" `seq 2 $$length`; \ - printf "\n\n"; \ - if [ -e commands/$${cmd}_docdir/cmdline.rst ]; then \ - sed "s,\(\.\.\s\+include::\s*\)\(.*\),\1$${cmd}_docdir/\2," \ - commands/$${cmd}_docdir/cmdline.rst \ - >commands/$${cmd}_cmdline.txt; \ - printf ".. include:: $${cmd}_cmdline.txt\n\n"; \ - fi; \ - printf "$${cmd^} library reference\n"; \ - printf -- "==========================\n"; \ - if [ -e commands/$${cmd}_docdir/python.rst ]; then \ - printf ".. toctree::\n :maxdepth: 2\n\n"; \ - printf " $${cmd}_docdir/python\n\n"; \ - else \ - printf ".. automodule:: lmi.scripts.$${cmd}\n"; \ - printf " :members:\n"; \ - fi; \ - ) > commands/$${cmd}.rst; \ - done -else - ( \ - printf "You may visit command usage and developer documentations\n"; \ - printf "on pythonhosted site:\n\n"; \ - for cmd in $(COMMANDS); do \ - printf " * \`openlmi-scripts-$${cmd}"; \ - printf " <http://pythonhosted.org/openlmi-scripts-$${cmd}>\`_\n"; \ - done \ - ) >> $@ -endif - - -clean: - -rm -rf $(BUILDDIR)/* modules/ api.rst commands.rst commands/ -ifneq ($(INCLUDE_COMMANDS), 0) - for cmd in $(COMMANDS); do \ - make -C $(COMMANDS_PATH)/$${cmd}/doc clean; \ - done -endif - -%.png: %.dia - dia -e $@ $< - -%.svg: %.uml plantuml.cfg - plantuml -config plantuml.cfg -Tsvg $< - -figures: - -$(TUTORIAL_TARBALL): - tar -czf $@ mylf - -html: figures api.rst commands.rst $(TUTORIAL_TARBALL) - ./build-with-sphinx.sh "$(COMMANDS)" -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/AnacondaStorageProvider.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/AnacondaStorageProvider.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/AnacondaStorageProvider" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/AnacondaStorageProvider" - @echo "# devhelp" - -epub: figures api.rst commands.rst - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: figures api.rst commands.rst - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - sed -i 's/^\\makeindex$$/\0[columns=1]/' $(BUILDDIR)/latex/$(PROJECTNAME).tex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: latex - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index 12b6e95..0000000 --- a/doc/README.md +++ /dev/null @@ -1,39 +0,0 @@ -Documentation -============= -This directory contains usage and developer documentation. -It's available online on [pythonhosted][]. - -How to build ------------- -Supported builds are *html*, *pdflatex* and *epub*. - -### Requirements - - * `bash` - * `GNU make` - * `setuptools` - provided by package `python-setuptools` - * `sphinx-build` - provided by package `python-sphinx` - -### Steps -There are two kinds of builds available. One containing documentation of `lmi` -command libraries and one with just references to them to external sources. -Following examples will generate only *html* documentation, to make any other -just replace the `html` argument with prefered one. - -#### Including commands documentation - - INCLUDE_COMMANDS=1 make -C doc html - -#### Without commands documentation -This build is the preferred one for Python Hosted site. - - make -C doc html - -How to upload -------------- -First build it (see the section above). -Then run: - - python setup.py upload_docs - -[pythonhosted]: http://pythonhosted.org/openlmi-scripts/index.html diff --git a/doc/_static/imode-tree.svg b/doc/_static/imode-tree.svg deleted file mode 100644 index 91c1614..0000000 --- a/doc/_static/imode-tree.svg +++ /dev/null @@ -1,605 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="678.85718" - height="399.14285" - id="svg2" - version="1.1" - inkscape:version="0.48.4 r9939" - sodipodi:docname="imode-tree.svg"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="0.98994949" - inkscape:cx="524.19771" - inkscape:cy="224.52211" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - inkscape:window-width="1920" - inkscape:window-height="1058" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="0" - fit-margin-top="0" - fit-margin-left="0" - fit-margin-right="0" - fit-margin-bottom="0" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-14.499999,-184.21933)"> - <path - style="fill:none;stroke:#b3b3b3;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 4;stroke-dashoffset:0" - d="m 162.85714,185.21933 0,397.14285" - id="path4052" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <path - sodipodi:type="arc" - style="fill:#ffffff;fill-opacity:1;stroke:#800000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0" - id="path4042" - sodipodi:cx="122.5" - sodipodi:cy="410.57648" - sodipodi:rx="69.64286" - sodipodi:ry="35.357143" - d="m 192.14286,410.57648 c 0,19.52721 -31.18017,35.35714 -69.64286,35.35714 -38.46269,0 -69.64286,-15.82993 -69.64286,-35.35714 0,-19.52721 31.18017,-35.35715 69.64286,-35.35715 38.46269,0 69.64286,15.82994 69.64286,35.35715 z" - transform="translate(35.714286,-14.285714)" /> - <g - id="g3003" - transform="translate(0,0.71427917)"> - <rect - ry="17.857143" - y="230.21933" - x="14.999999" - height="35.714287" - width="137.85715" - id="rect2985" - style="fill:#ffe680;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text2999" - y="253.59406" - x="41.628769" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - xml:space="preserve"><tspan - y="253.59406" - x="41.628769" - id="tspan3001" - sodipodi:role="line">TopLevel</tspan></text> - </g> - <g - id="g3033" - transform="translate(-41.20703,-4.2857208)"> - <rect - style="fill:#ffaaaa;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3024" - width="137.85715" - height="35.714287" - x="215" - y="235.21933" - ry="17.857143" /> - <text - xml:space="preserve" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - x="244.661" - y="258.41827" - id="text3026" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3028" - x="244.661" - y="258.41827">Storage</tspan></text> - </g> - <g - id="g4032" - transform="translate(0.22154353,20)"> - <rect - ry="17.857143" - y="463.07648" - x="173.57143" - height="35.714287" - width="137.85715" - id="rect3040" - style="fill:#ffaaaa;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text3042" - y="488.38968" - x="198.27148" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - xml:space="preserve"><tspan - y="488.38968" - x="198.27148" - id="tspan3044" - sodipodi:role="line">Software</tspan></text> - </g> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono" - x="46.047226" - y="290.93359" - id="text3049" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3051" - x="46.047226" - y="290.93359">lmi> :pwd</tspan><tspan - sodipodi:role="line" - x="46.047226" - y="308.43359" - id="tspan3053">/lmi</tspan></text> - <text - sodipodi:linespacing="125%" - id="text3055" - y="290.93359" - x="183.97691" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono" - xml:space="preserve"><tspan - y="290.93359" - x="183.97691" - id="tspan3057" - sodipodi:role="line" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono">>storage> :pwd</tspan><tspan - id="tspan3059" - y="308.43359" - x="183.97691" - sodipodi:role="line" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono">/lmi/storage</tspan></text> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono" - x="183.97691" - y="542.18982" - id="text3061" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3063" - x="183.97691" - y="542.18982">>sw> :pwd</tspan><tspan - sodipodi:role="line" - x="183.97691" - y="559.68982" - id="tspan3065">/lmi/sw</tspan></text> - <g - id="g3078" - transform="translate(-71.821487,-5)"> - <rect - ry="17.857143" - y="235.93361" - x="415.71426" - height="35.714287" - width="137.85715" - id="rect3069" - style="fill:#ffaaaa;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text3071" - y="261.2468" - x="450.17505" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - xml:space="preserve"><tspan - y="261.2468" - x="450.17505" - id="tspan3073" - sodipodi:role="line">LVCmd</tspan></text> - </g> - <g - id="g4037" - transform="translate(-8.9643402,2.1428265)"> - <rect - style="fill:#ffaaaa;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3085" - width="137.85715" - height="35.714287" - x="352.85712" - y="320.93362" - ry="17.857143" /> - <text - xml:space="preserve" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - x="374.60309" - y="346.24683" - id="text3087" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3089" - x="374.60309" - y="346.24683">RAIDCmd</tspan></text> - </g> - <g - id="g4027" - transform="translate(-7.5357819,0)"> - <rect - ry="17.857143" - y="422.36218" - x="351.42856" - height="35.714287" - width="137.85715" - id="rect3101" - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text3103" - y="447.67538" - x="377.3493" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - xml:space="preserve"><tspan - y="447.67538" - x="377.3493" - id="tspan3105" - sodipodi:role="line">TreeCmd</tspan></text> - </g> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - x="65.21373" - y="198.29857" - id="text3886" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3888" - x="65.21373" - y="198.29857">Root</tspan></text> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - x="198.54967" - y="198.79076" - id="text3890" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3892" - x="198.54967" - y="198.79076">Registered</tspan><tspan - sodipodi:role="line" - x="198.54967" - y="218.79076" - id="tspan3894">commands</tspan></text> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - x="350.43854" - y="198.79076" - id="text3896" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3898" - x="350.43854" - y="198.79076">Sub-commands</tspan></text> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono" - x="337.14752" - y="290.93359" - id="text3900" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3902" - x="337.14752" - y="290.93359">>>lv> :pwd</tspan><tspan - sodipodi:role="line" - x="337.14752" - y="308.43359" - id="tspan3904">/lmi/storage/lv</tspan></text> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono" - x="337.14752" - y="385.93359" - id="text3906" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3908" - x="337.14752" - y="385.93359">>>raid> :pwd</tspan><tspan - sodipodi:role="line" - x="337.14752" - y="403.43359" - id="tspan3910">/lmi/storage/raid</tspan></text> - <path - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" - d="M 245.57868,275.21932 194.8644,359.50503" - id="path3916" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <path - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" - d="M 362.85714,273.79075 C 360,275.93361 221.42858,372.36218 221.42858,372.36218" - id="path3918" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <path - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" - d="m 349.28571,350.21932 c -1.42857,2.85715 -117.85714,33.57144 -117.85714,33.57144" - id="path3920" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <path - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" - d="M 225.57868,478.79076 194.8644,432.36217" - id="path3924" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <g - id="g3992" - transform="translate(-41.428572,0)"> - <rect - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3878" - width="137.85715" - height="35.714287" - x="546.42859" - y="230.93361" - ry="17.857143" /> - <text - sodipodi:linespacing="125%" - id="text3929" - y="256.2468" - x="597.1394" - style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" - xml:space="preserve"><tspan - y="256.2468" - x="597.1394" - id="tspan3931" - sodipodi:role="line">List</tspan></text> - </g> - <g - id="g3997" - transform="translate(-40.000044,1.4285889)"> - <rect - ry="17.857143" - y="275.21933" - x="545.00006" - height="35.714287" - width="137.85715" - id="rect3880" - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text3933" - y="300.35675" - x="580.63763" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - xml:space="preserve"><tspan - style="font-size:20px" - y="300.35675" - x="580.63763" - id="tspan3935" - sodipodi:role="line">Create</tspan></text> - </g> - <g - id="g4002" - transform="translate(-38.571455,2.1428265)"> - <rect - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3882" - width="137.85715" - height="35.714287" - x="543.57147" - y="320.93362" - ry="17.857143" /> - <text - sodipodi:linespacing="125%" - id="text3937" - y="346.24683" - x="594.28229" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - xml:space="preserve"><tspan - style="font-size:20px" - y="346.24683" - x="594.28229" - id="tspan3939" - sodipodi:role="line">List</tspan></text> - </g> - <g - id="g4007" - transform="translate(-38.571455,0)"> - <rect - ry="17.857143" - y="368.07648" - x="543.57147" - height="35.714287" - width="137.85715" - id="rect3884" - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text3941" - y="393.38968" - x="579.19928" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - xml:space="preserve"><tspan - style="font-size:20px" - y="393.38968" - x="579.19928" - id="tspan3943" - sodipodi:role="line">Delete</tspan></text> - </g> - <path - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" - d="m 491.42857,441.64789 56.42857,0" - id="path3968" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <g - id="g4022" - transform="translate(-14.100082,20)"> - <rect - style="fill:#ffaaaa;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3972" - width="137.85715" - height="35.714287" - x="357.99286" - y="463.07648" - ry="17.857143" /> - <text - sodipodi:linespacing="125%" - id="text3974" - y="486.4512" - x="373.05426" - style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - xml:space="preserve"><tspan - y="486.4512" - x="373.05426" - id="tspan3976" - sodipodi:role="line">Repository</tspan></text> - </g> - <g - id="g4012" - transform="translate(-36.428572,1.4285579)"> - <rect - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3980" - width="137.85715" - height="35.714287" - x="541.42859" - y="481.64792" - ry="17.857143" /> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - x="575.82593" - y="506.96112" - id="text3982" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3984" - x="575.82593" - y="506.96112" - style="font-size:20px">Enable</tspan></text> - </g> - <g - id="g4017" - transform="translate(-36.428572,2.1428571)"> - <rect - ry="17.857143" - y="528.79077" - x="541.42859" - height="35.714287" - width="137.85715" - id="rect3986" - style="fill:#a8dbff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> - <text - sodipodi:linespacing="125%" - id="text3988" - y="554.10394" - x="572.81812" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - xml:space="preserve"><tspan - style="font-size:20px" - y="554.10394" - x="572.81812" - id="tspan3990" - sodipodi:role="line">Disable</tspan></text> - </g> - <text - xml:space="preserve" - style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - x="101.7055" - y="401.48608" - id="text3912" - sodipodi:linespacing="125%"><tspan - sodipodi:role="line" - id="tspan3914" - x="101.7055" - y="401.48608" - style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Droid Sans;-inkscape-font-specification:Droid Sans">multiplexers</tspan></text> - <path - inkscape:connector-curvature="0" - id="path4054" - d="m 327.85714,185.21933 0,397.14285" - style="fill:none;stroke:#b3b3b3;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 4;stroke-dashoffset:0" - sodipodi:nodetypes="cc" /> - <g - id="g4058"> - <path - sodipodi:type="arc" - style="fill:#ffffff;fill-opacity:1;stroke:#800000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0" - id="path4056" - sodipodi:cx="122.5" - sodipodi:cy="410.57648" - sodipodi:rx="69.64286" - sodipodi:ry="35.357143" - d="m 192.14286,410.57648 c 0,19.52721 -31.18017,35.35714 -69.64286,35.35714 -38.46269,0 -69.64286,-15.82993 -69.64286,-35.35714 0,-19.52721 31.18017,-35.35715 69.64286,-35.35715 38.46269,0 69.64286,15.82994 69.64286,35.35715 z" - transform="translate(500.71429,32.857143)" /> - <text - sodipodi:linespacing="125%" - id="text3912-0" - y="438.43362" - x="573.19476" - style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Terminus" - xml:space="preserve"><tspan - style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Droid Sans;-inkscape-font-specification:Droid Sans" - y="438.43362" - x="573.19476" - id="tspan3914-9" - sodipodi:role="line">end point</tspan><tspan - id="tspan3966" - style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Droid Sans;-inkscape-font-specification:Droid Sans" - y="463.43362" - x="573.19476" - sodipodi:role="line">commands</tspan></text> - </g> - <path - sodipodi:nodetypes="cc" - inkscape:connector-curvature="0" - id="path4064" - d="M 542.14286,407.36217 555,423.07646" - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" /> - <path - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" - d="m 545.00001,478.79074 13.57142,-13.57143" - id="path4066" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - <text - sodipodi:linespacing="125%" - id="text4068" - y="540.57654" - x="337.14752" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono" - xml:space="preserve"><tspan - y="540.57654" - x="337.14752" - id="tspan4070" - sodipodi:role="line">>>repo> :pwd</tspan><tspan - id="tspan4072" - y="558.07654" - x="337.14752" - sodipodi:role="line">/lmi/software/repo</tspan></text> - <path - sodipodi:nodetypes="cc" - inkscape:connector-curvature="0" - id="path4074" - d="M 337.00725,484.50505 228.43583,416.64789" - style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 1;stroke-dashoffset:0" /> - </g> -</svg> diff --git a/doc/_static/logicalfile.class.violet b/doc/_static/logicalfile.class.violet deleted file mode 100644 index 4df1546..0000000 --- a/doc/_static/logicalfile.class.violet +++ /dev/null @@ -1,556 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<java version="1.7.0_60" class="java.beans.XMLDecoder"> - <object class="com.horstmann.violet.ClassDiagramGraph"> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode0"> - <void property="name"> - <void property="text"> - <string>LMI_DataFile</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double0"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double0"/> - <double>1016.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double0"/> - <double>330.0</double> - </void> - </void> - <void method="setLocation"> - <double>1016.0</double> - <double>330.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode1"> - <void property="attributes"> - <void property="text"> - <string>CIM_Directory REF GroupComponent -CIM_LogicalFile REF PartComponent</string> - </void> - </void> - <void property="name"> - <void property="text"> - <string>LMI_DirectoryContainsFile</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double1"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double1"/> - <double>407.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double1"/> - <double>239.0</double> - </void> - </void> - <void method="setLocation"> - <double>407.0</double> - <double>239.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode2"> - <void property="name"> - <void property="text"> - <string>LMI_FIFOPipeFile</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double2"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double2"/> - <double>742.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double2"/> - <double>430.0</double> - </void> - </void> - <void method="setLocation"> - <double>742.0</double> - <double>430.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode3"> - <void property="attributes"> - <void property="text"> - <string>CIM_ManagedElement REF SameElement -CIM_ManagedElement REF SystemElement</string> - </void> - </void> - <void property="name"> - <void property="text"> - <string>LMI_FileIdentity</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double3"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double3"/> - <double>676.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double3"/> - <double>56.0</double> - </void> - </void> - <void method="setLocation"> - <double>676.0</double> - <double>56.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode4"> - <void property="attributes"> - <void property="text"> - <string>CIM_ManagedElement REF GroupComponent -LMI_UnixDirectory REF PartComponent</string> - </void> - </void> - <void property="name"> - <void property="text"> - <string>LMI_RootDirectory</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double4"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double4"/> - <double>128.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double4"/> - <double>418.0</double> - </void> - </void> - <void method="setLocation"> - <double>128.0</double> - <double>418.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode5"> - <void property="name"> - <void property="text"> - <string>LMI_SymbolicLink</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double5"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double5"/> - <double>923.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double5"/> - <double>431.0</double> - </void> - </void> - <void method="setLocation"> - <double>923.0</double> - <double>431.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode6"> - <void property="name"> - <void property="text"> - <string>LMI_UnixDeviceFile</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double6"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double6"/> - <double>1020.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double6"/> - <double>168.0</double> - </void> - </void> - <void method="setLocation"> - <double>1020.0</double> - <double>168.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode7"> - <void property="name"> - <void property="text"> - <string>LMI_UnixDirectory</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double7"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double7"/> - <double>462.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double7"/> - <double>425.0</double> - </void> - </void> - <void method="setLocation"> - <double>462.0</double> - <double>425.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode8"> - <void property="attributes"> - <void property="text"> - <string>string FSCreationClassName -string CSName -string CSCreationClassName -string CreationClassName -string FSName -string LFName</string> - </void> - </void> - <void property="name"> - <void property="text"> - <string>LMI_UnixFile</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double8"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double8"/> - <double>334.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double8"/> - <double>36.0</double> - </void> - </void> - <void method="setLocation"> - <double>334.0</double> - <double>36.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode9"> - <void property="name"> - <void property="text"> - <string>LMI_UnixSocket</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double9"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double9"/> - <double>1015.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double9"/> - <double>254.0</double> - </void> - </void> - <void method="setLocation"> - <double>1015.0</double> - <double>254.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode10"> - <void property="attributes"> - <void property="text"> - <string>string CreationClassName -string Name</string> - </void> - </void> - <void property="name"> - <void property="text"> - <string>CIM_ComputerSystem</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double10"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double10"/> - <double>180.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double10"/> - <double>245.0</double> - </void> - </void> - <void method="setLocation"> - <double>180.0</double> - <double>245.0</double> - </void> - </object> - </void> - <void method="addNode"> - <object class="com.horstmann.violet.ClassNode" id="ClassNode11"> - <void property="attributes"> - <void property="text"> - <string>string FSCreationClassName -string Name -string CSName -string CSCreationClassName -string CreationClassName -string FSName</string> - </void> - </void> - <void property="name"> - <void property="text"> - <string>CIM_LogicalFile</string> - </void> - </void> - </object> - <object class="java.awt.geom.Point2D$Double" id="Point2D$Double11"> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>x</string> - <void method="set"> - <object idref="Point2D$Double11"/> - <double>707.0</double> - </void> - </void> - <void class="java.awt.geom.Point2D$Double" method="getField"> - <string>y</string> - <void method="set"> - <object idref="Point2D$Double11"/> - <double>209.0</double> - </void> - </void> - <void method="setLocation"> - <double>707.0</double> - <double>209.0</double> - </void> - </object> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="HVH"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="V"/> - </void> - <void property="endLabel"> - <string>1</string> - </void> - </object> - <object idref="ClassNode4"/> - <object idref="ClassNode10"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="HVH"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="V"/> - </void> - <void property="endLabel"> - <string>1</string> - </void> - </object> - <object idref="ClassNode4"/> - <object idref="ClassNode7"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="VHV"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/> - </void> - </object> - <object idref="ClassNode5"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="VHV"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/> - </void> - </object> - <object idref="ClassNode6"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="VHV"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/> - </void> - </object> - <object idref="ClassNode2"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="VHV"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/> - </void> - </object> - <object idref="ClassNode9"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="VHV"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/> - </void> - </object> - <object idref="ClassNode0"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="HVH"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="V"/> - </void> - <void property="endLabel"> - <string>1</string> - </void> - </object> - <object idref="ClassNode3"/> - <object idref="ClassNode8"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="HVH"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="V"/> - </void> - <void property="endLabel"> - <string>1</string> - </void> - </object> - <object idref="ClassNode3"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/> - </void> - </object> - <object idref="ClassNode7"/> - <object idref="ClassNode11"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="HVH"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="V"/> - </void> - <void property="endLabel"> - <string>1</string> - </void> - </object> - <object idref="ClassNode1"/> - <object idref="ClassNode7"/> - </void> - <void method="connect"> - <object class="com.horstmann.violet.ClassRelationshipEdge"> - <void property="bentStyle"> - <object class="com.horstmann.violet.BentStyle" field="HVH"/> - </void> - <void property="endArrowHead"> - <object class="com.horstmann.violet.ArrowHead" field="V"/> - </void> - <void property="endLabel"> - <string>*</string> - </void> - </object> - <object idref="ClassNode1"/> - <object idref="ClassNode11"/> - </void> - </object> -</java> diff --git a/doc/_static/logicalfile.png b/doc/_static/logicalfile.png Binary files differdeleted file mode 100644 index 04633d1..0000000 --- a/doc/_static/logicalfile.png +++ /dev/null diff --git a/doc/build-with-sphinx.sh b/doc/build-with-sphinx.sh deleted file mode 100755 index e1a08d2..0000000 --- a/doc/build-with-sphinx.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# This script creates a temporary python workspace directory, where the -# openlmi-scripts and all specified commands are installed in order for -# sphinx-build to successfuly find them and generate devel documentation out of -# them. -# -# There are several environment variables affecting the execution: -# * SPHINXBUILD -# sphinx binary generating the documentation -# * INCLUDE_COMMANDS -# whether the command should be included in generated documentation -# * COMMANDS -# list of subcommands to include - -SPHINXBUILD=${SPHINXBUILD:-sphinx-build} -INCLUDE_COMMANDS=${INCLUDE_COMMANDS:-1} -tmp=`mktemp -d` -pushd .. -export PYTHONPATH=$tmp -python setup.py develop --install-dir=$tmp || exit 1 -if [ "$INCLUDE_COMMANDS" == 1 ]; then - pushd commands - COMMANDS="$1" - for cmd in ${COMMANDS}; do - pushd $cmd - python setup.py develop --install-dir=$tmp || exit 1 - popd - done - popd -fi -popd # .. -shift -${SPHINXBUILD} $@ || exit 1 -rm -rf $tmp diff --git a/doc/command-classes.rst b/doc/command-classes.rst deleted file mode 100644 index bdee7c7..0000000 --- a/doc/command-classes.rst +++ /dev/null @@ -1,163 +0,0 @@ -.. _command_classes: - -Command classes -=============== -Before reading this, please make sure you're familiar with -:ref:`command_wrappers_overview`. - -We focus here on commands intended for subclassing in command wrapper modules. -*OpenLMI Scripts* defines and uses other kinds of commands internally. But -the script developer does not need to know about them. - -.. seealso:: - General and class specific properties in :ref:`command_properties`. - -.. _end-point_commands: - -End-point commands ------------------- -Were already introduced before (see :ref:`end-point_commands_introduction`). -We'll dive into details here. - -Every end-point command allows to verify and transform options parsed by -docopt_ before they are passed to associated function. This can happen in -methods: - -``verify_options(self, options)`` - Taking pre-processed options dictionary as a first argument. - Properties affecting this pre-processing can be found in - :ref:`pre_processing_properties`. This method shall check option values or - their combination and raise ``lmi.scripts.common.errors.LmiInvalidOptions`` - if any inconsistency is discovered. - - Example usage: :: - - class FileLister(command.LmiInstanceLister): - DYNAMIC_PROPERTIES = True - - def verify_options(self, options): - file_types = { 'all', 'file', 'directory', 'symlink' - , 'fifo', 'device'} - if ( options['--type'] is not None - and options['--type'] not in file_types): - raise errors.LmiInvalidOptions( - 'Invalid file type given, must be one of %s' % - file_types) - - .. seealso:: - API doccumentation on - :py:meth:`~lmi.scripts.common.command.endpoint.LmiEndPointCommand.verify_options` - -``transform_options(self, options)`` - Takes verified options dictionary. It modifies this dictionary in - arbitrary way in place. Its return value is ignored. - - Example usage: :: - - class Lister(command.LmiLister): - COLUMNS = ('Device', 'Name', "ElementName", "Type") - - def transform_options(self, options): - """ - Rename 'device' option to 'devices' parameter name for better - readability. - """ - options['<devices>'] = options.pop('<device>') - - .. seealso:: - API documentation on - :py:meth:`~lmi.scripts.common.command.endpoint.LmiEndPointCommand.transform_options` - -Above methods can be used to process options in a way that any script library -function can be called. In case we need more control over what is called or -when we want to decide at runtime which function shall be called, we may override -:py:meth:`~lmi.scripts.common.command.endpoint.LmiEndPointCommand.execute` method -instead. Example of this may be found at :ref:`associating_a_function`. - -.. _lmi_check_result: - -``LmiCheckResult`` -~~~~~~~~~~~~~~~~~~ -This command invokes associated function on hosts in session, collects results -from them and compares them to an expected value. It does not produce any -output, when all returned values are expected. - -This command class is very useful when wrapping up some CIM class's method -such as ``LMI_Service::StartService()``. Example can be seen in -:ref:`property_descriptions`. - -Its specific properties are listed in :ref:`lmi_check_result_properties`. - -.. seealso:: - API documentation on - :py:class:`~lmi.scripts.common.command.checkresult.LmiCheckResult` - -.. _lmi_lister: - -``LmiLister`` -~~~~~~~~~~~~~ -Prints tablelike data. It expects associated function to return its result -in form: :: - - [row1, row2, ...] - -Where ``rowX`` is a tuple containing row values. Each such row is ``list`` or -``tuple`` of the same length. There is a property ``COLUMNS`` defining column -names [#]_ (see :ref:`lmi_lister_properties`). Generator is prefered over -a ``list`` of rows. If ``COLUMNS`` property is omitted, returned value shall -take the following form instead: :: - - (columns, data) - -Where ``columns`` has the same meaning as ``COLUMNS`` as a class property and -``data`` is the result of previous case [#]_. - -.. seealso:: - API documentation on - :py:class:`~lmi.scripts.common.command.lister.LmiLister` - -.. _lmi_instance_lister: - -``LmiInstanceLister`` -~~~~~~~~~~~~~~~~~~~~~ -Is a variant of ``LmiLister``. Instead of rows being tuples, here they are -instances of some CIM class. Instead of using ``COLUMNS`` property for -specifying columns labels, ``PROPERTIES`` is used for the same purpose here. -Its primary use is in specifying which properties of instances shall be -rendered in which column. This is described in detail in -:ref:`lmi_instance_lister_properties`. - -The expected output of associated function is therefore: :: - - [instance1, instance2, ...] - -Again, usage of generators is preferred. - -.. seealso:: - API documentation on - :py:class:`~lmi.scripts.common.command.lister.LmiInstanceLister` - -.. _lmi_show_instance: - -``LmiShowInstance`` -~~~~~~~~~~~~~~~~~~~ -Renders a single instance of some CIM class. It's rendered in a form of -two-column table, where the first column contains property names and -the second their corresponding values. Rendering is controlled in the same -way as for ``LmiInstanceLister`` (see :ref:`lmi_show_instance_properties`). - -.. seealso:: - API documentation on - :py:class:`~lmi.scripts.common.command.show.LmiShowInstance` - -.. **************************************************************************** - -.. _CIM: http://dmtf.org/standards/cim -.. _OpenLMI: http://fedorahosted.org/openlmi/ -.. _openlmi-tools: http://fedorahosted.org/openlmi/wiki/shell -.. _docopt: http://docopt.org/ - -------------------------------------------------------------------------------- - -.. [#] Having the same length as each row in returned data. -.. [#] Generator or a ``list`` of rows. diff --git a/doc/command-properties.rst b/doc/command-properties.rst deleted file mode 100644 index d45f8d9..0000000 --- a/doc/command-properties.rst +++ /dev/null @@ -1,574 +0,0 @@ -.. _command_properties: - -Command properties -================== - -As noted before in :ref:`end-point_commands`, command at first tries to -process input arguments, calls an associated function and then renders its -result. We'll now introduce properties affecting this process. - -Command class properties are written in their bodies and handled by their -metaclasses. After being processed, they are removed from class. So they are -not accessible as class attributes or from their instances. - -.. _pre_processing_properties: - -Options pre-processing ----------------------- -Influencing properties: - - * ``OPT_NO_UNDERSCORES`` (opt_no_underscores_) - * ``ARG_ARRAY_SUFFIX`` (arg_array_suffix_) - * ``OWN_USAGE`` (own_usage_) - -docopt_ will make a dictionary of options based on usage string such -as the one above (:ref:`usage_string`). Options dictionary matching this -example looks like this: :: - - { 'list' : bool - , '--all' : bool - , '--disabled' : bool - , 'start' : bool - , '<service>' : str - } - -Values of this dictionary are passed to an associated function as arguments -with names created out of matching keys. Since argument names can not contain -characters such as `'<'`, `'>'`, `'-'`, etc., these need to be replaced. -Process of renaming of these options can be described by the following pseudo -algorithm: - -.. _options_transform_algorithm: - - 1. arguments enclosed in brackets are un-surrounded -- brackets get - removed :: - - "<service>" -> "service" - - 2. arguments written in upper case are made lower cased :: - - "FILE" -> "file" - - 3. prefix of short and long options made of dashes shall be replaced with - single underscore :: - - "-a" -> "_a" - "--all" -> "_all" - - 4. any non-empty sequence of characters not allowed in python's identitier - shall be replaced with a single underscore :: - - "_long-option" -> "_long_option" - "special--cmd-#2" -> "special_cmd_2" - -Points 3 and 4 could be merged into one. But we separate them due to effects -of ``OPT_NO_UNDERSCORES`` property described below. - -.. seealso:: - Notes in :ref:`end-point_commands` for method - :py:meth`lmi.scripts.common.command.endpoint.LmiEndPointCommand.transform_options` - which is issued before the above algorithm is run. - -Treating dashes -~~~~~~~~~~~~~~~ -Single dash and double dash are special cases of commands. - -Double dash in usage string allows to pass option-like argument to a script -e.g.: :: - - lmi file show -- --file-prefix-with-double-dash - -Without the ``'--'`` argument prefixing the file, docopt_ would throw an error -beacause of ``--file-prefix-with-double-dash`` being treated as an unknown -option. This way it's correctly treated as an argument ``<file>`` given the -usage string: :: - - Usage: %(cmd)s file show [--] <file> - -Double dash isn't be passed to an associated function. - -Single dash on a command line is commonly used to specify stdout or stding. For -example in the following snippet: :: - - Usage: %(cmd)s file copy (- | <file>) <dest> - -``'-'`` stands for standard input which will be read instead of a file if the -user wishes to. - -Property descriptions -~~~~~~~~~~~~~~~~~~~~~ -.. _opt_no_underscores: - -``OPT_NO_UNDERSCORES`` : ``bool`` (defaults to ``False``) - Modifies point 3 of options pre-processing. It causes the prefix of dashes - to be completely removed with no replacement: :: - - "--long-options" -> "long-option" - - This may not be save if there is a command with the same name as the - option being removed. Setting this property to ``True`` will cause - overwriting the command with the value of option. A warning shall be - echoed if such a case occurs. - -.. _arg_array_suffix: - -``ARG_ARRAY_SUFFIX`` : ``str`` (defaults to ``""``) - Adds additional point (5) to `options_transform_algorithm`_. All - repeatable arguments, resulting in a ``list`` of items, are renamed to - ``<original_name><suffix>`` [#]_. Repeatable argument in usage string - looks like this: :: - - """ - Usage: %(cmd)s start <service> ... - """ - - Causing all of the ``<service>`` arguments being loaded into a ``list`` - object. - -.. _own_usage: - -``OWN_USAGE`` : ``bool`` (defaults to ``False``) - Says whether the documentation string of this class is a usage string. - Each command in hierarchy can have its own usage string. - - This can also be assigned a usage string directly: :: - - class MySubcommand(LmiCheckResult): - """ - Class doc string. - """ - OWN_USAGE = "Usage: %(cmd)s --opt1 --opt1 <file> <args> ..." - EXPECT = 0 - - But using a boolean value is more readable: :: - - class MySubcommand(LmiCheckResult): - """ - Usage: %(cmd)s --opt1 --opt1 <file> <args> ... - """ - OWN_USAGE = True - EXPECT = 0 - - .. note:: - - Using own usage strings in subcommands of top-level commands is not - recommended. It brings a lot of redundancy and may prove problematic - to modify while keeping consistency among hierarchically nested - usages. - - Therefore try to have just one usage string in a top-level command. - And one top-level command in a single module. Resulting in one usage - string per one command wrappers module. This makes it easier to read - and modify. - -.. _associating_a_function: - -Associating a function ----------------------- -Influencing properties: - - * ``CALLABLE`` (callable_) - -When command is invoked, its method -:py:meth:`~lmi.scripts.common.command.endpoint.LmiEndPointCommand.execute` will -get verified and transformed options as positional and keyword arguments. -This method shall pass them to an associated function residing in script -library and return its result on completition. - -One way to associate a function is to use ``CALLABLE`` property. The other -is to define very own ``execute()`` method like this: :: - - class Lister(command.LmiInstanceLister): - PROPERTIES = ('Name', "Started", 'Status') - - def execute(self, ns, _all, _disabled, _oneshot): - kind = 'enabled' - if _all: - kind = 'all' - elif _disabled: - kind = 'disabled' - elif _oneshot: - kind = 'oneshot' - for service_inst in service.list_services(ns, kind): - yield service_inst - -This may come handy if the application object [#]_ needs to be accessed or -if we need to decide which function to call based on command line options. - -.. _property_descriptions: - -Property descriptions -~~~~~~~~~~~~~~~~~~~~~ -.. _callable: - -``CALLABLE`` : ``str`` (defaults to ``None``) - This is a mandatory option if - :py:meth:`~lmi.scripts.common.command.endpoint.LmiEndPointCommand.execute` - method is not overriden. It may be a string composed of a full path of - module and its callable delimited with ``':'``: :: - - CALLABLE = 'lmi.scripts.service:start' - - Causes function ``start()`` of ``'lmi.scripts.service'`` module to be - associated with command. - - Callable may also be assigned directly like this: :: - - from lmi.scripts import service - class Start(command.LmiCheckResult): - CALLABLE = service.start - EXPECT = 0 - - The first variant (by assigning string) comes handy if the particular - module of associated function is not yet imported. Thus delaying the - import until the point of function's invocation - if the execution comes - to this point at all. In short it speeds up execution of ``lmi`` - meta-command by reducing number of module imports that are not needed. - -.. _function_invocation: - -Function invocation -------------------- -Influencing properties: - - * ``NAMESPACE`` (namespace_) - -Property descriptions -~~~~~~~~~~~~~~~~~~~~~ - -.. _namespace: - -``NAMESPACE`` : ``str`` (defaults to ``None``) - This property affects the first argument passed to an associated function. - Various values have different impact: - - +-----------+---------------------------------------+-------------------------------------+ - | Value | Value of first argument. | Its type | - +===========+=======================================+=====================================+ - | ``None`` | Same impact as value ``"root/cimv2"`` | :py:class:`lmi.shell.LMINamespace` | - +-----------+---------------------------------------+-------------------------------------+ - | ``False`` | Raw connection object | :py:class:`lmi.shell.LMIConnection` | - +-----------+---------------------------------------+-------------------------------------+ - | any path | Namespace object with given path | :py:class:`lmi.shell.LMINamespace` | - +-----------+---------------------------------------+-------------------------------------+ - - This usually won't need any modification. Sometimes perhaps associated - function might want to access more than one namespace, in that case an - instance of :py:class:`lmi.shell.LMIConnection` might provide more useful. - - Namespace can also be overriden globally in a configuration file or with - an option on command line. - - -Output rendering ----------------- -All these options begin with ``FMT_`` which is a shortcut for *formatter* as -they become options to formatter objects. These can be defined not only in -end-point commands but also in multiplexers. In the latter case they set the -defaults for all their direct and indirect child commands. - -.. note:: - These options override configuration settings and command line options. - Therefor use them with care. - -They are: - -.. _fmt_no_headings: - -``FMT_NO_HEADINGS`` : ``bool`` (defaults to ``False``) - Allows to suppress headings (column or row names) in the output. - - .. note:: - With :ref:`lmi_lister` command it's preferable to set the *COLUMNS* - property to empty list instead. Otherwise associated function is - expected to return column headers as a first row in its result. - -.. _fmt_human_friendly: - -``FMT_HUMAN_FRIENDLY`` : ``bool`` (defaults to ``False``) - Forces the output to be more pleasant to read by human beings. - -.. _specifying_requirements: - -Command specific properties ---------------------------- -Each command class can have its own specific properties. Let's take a look on -them. - -``LmiCommandMultiplexer`` -~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _commands: - -``COMMANDS`` : ``dict`` (mandatory) - Dictionary assigning subcommands to their names listed in usage string. - Example follows: :: - - class MyCommand(LmiCommandMultiplexer): - ''' - My command description. - - Usage: %(cmd)s mycommand (subcmd1 | subcmd2) - ''' - COMMANDS = {'subcmd1' : Subcmd1, 'subcmd2' : Subcmd2} - OWN_USAGE = True - - Where ``Subcmd1`` and ``Subcmd2`` are some other ``LmiBaseCommand`` - subclasses. Documentation string must be parseable with docopt_. - - ``COMMANDS`` property will be translated to - :py:meth:`~lmi.scripts.common.command.multiplexer.LmiCommandMultiplexer.child_commands` - class method by - :py:class:`~lmi.scripts.common.command.meta.MultiplexerMetaClass`. - -``FALLBACK_COMMAND`` : :py:class:`lmi.scripts.common.command.endpoint.LmiEndPointCommand` - Command class used when no command defined in ``COMMANDS`` dictionary is - passed on command line. - - Take for example this usage string: :: - - """ - Display hardware information. - - Usage: - %(cmd)s [all] - %(cmd)s system - %(cmd)s chassis - """ - - This suggests there are tree commands defined taking care of listing - hardware informations. Entry point definition could look like this: :: - - class Hardware(command.LmiCommandMultiplexer): - OWN_USAGE = __doc__ # usage string from above - COMMANDS = { 'all' : All - , 'system' : System - , 'chassis' : Chassis - } - FALLBACK_COMMAND = All - - Without the ``FALLBACK_COMMAND`` property, the multiplexer would not - handle the case when ``'all'`` argument is omitted as is suggested in - the usage string. Adding it to command properties causes this multiplexer - to behave exactly as ``All`` subcommand in case that no command - is given on command line. - -.. _lmi_select_command_properties: - -``LmiSelectCommand`` properties -------------------------------- -Following properties allow to define profile and class requirements for -commands. - -.. _select: - -``SELECT`` : ``list`` (mandatory) - Is a list of pairs ``(condition, command)`` where ``condition`` is an - expression in *LMIReSpL* language. And ``command`` is either a string with - absolute path to command that shall be loaded or the command class itself. - - Small example: :: - - SELECT = [ - ( 'OpenLMI-Hardware < 0.4.2' - , 'lmi.scripts.hardware.pre042.PreCmd' - ) - , ('OpenLMI-Hardware >= 0.4.2 & class LMI_Chassis == 0.3.0' - , HwCmd - ) - ] - - It says: Let the ``PreHwCmd`` command do the job on brokers having - ``openlmi-hardware`` package older than ``0.4.2``. Use the ``HwCmd`` - anywhere else where also the ``LMI_Chassis`` CIM class in version ``0.3.0`` - is available. - - First matching condition wins and assigned command will be passed all the - arguments. If no condition can be satisfied and no default command is set, - an exception will be raised. - - .. seealso:: - Definition of *LMIReSpL* mini-language: - :py:mod:`~lmi.scripts.common.versioncheck.parser` - -.. _default: - -``DEFAULT`` : ``string`` or reference to command class - Defines fallback command used in case no condition in ``SELECT`` can be - satisfied. - -.. _lmi_lister_properties: - -``LmiLister`` properties -~~~~~~~~~~~~~~~~~~~~~~~~ -.. _columns: - -``COLUMNS`` : ``tuple`` - Column names. It's a tuple with name for each column. Each row of data - shall then contain the same number of items as this tuple. If omitted, - associated function is expected to provide them in the first row of - returned list. It's translated to - :py:meth:`~lmi.scripts.common.command.lister.LmiBaseListerCommand.get_columns` - class method. - - If set to empty list, no column headers will be printed. Every item of - returned list of associated function will be treated as data. Note that - setting this to empty list makes the *FMT_NO_HEADINGS* property - redundant. - -.. _lmi_instance_commands_properties: -.. _lmi_show_instance_properties: -.. _lmi_instance_lister_properties: - -``LmiShowInstance`` and ``LmiInstanceLister`` properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These two classes expect, as a result of their associated function, an instance -or a list of instances of some CIM class. They take care of rendering them to -standard output. Thus their properties affect the way how their properties -are rendered. - -.. _properties: - -``PROPERTIES`` : ``tuple`` - Property names in the same order as the properties shall be listed. Items - of this tuple can take multiple forms: - - Property Name : ``str`` - Will be used for the name of column/property in output table and the - same name will be used when obtaining the value from instance. Thus - this form may be used only if the property name of instance can appear - as a name of column. - - (Column Name, Property Name) : ``(str, str)`` - This pair allows to render value of property under different name - (*Column Name*). - - (Column Name, getter) : ``(str, callable)`` - This way allows the value to be arbitrarily computed. The second - item is a callable taking one and only argument -- the instance of - class to be rendered. - - Example below shows different ways of rendering attributes for instances - of ``LMI_Service`` CIM class: :: - - class Show(command.LmiShowInstance): - CALLABLE = 'lmi.scripts.service:get_instance' - PROPERTIES = ( - 'Name', - ('Enabled', lambda i: i.EnabledDefault == 2), - ('Active', 'Started')) - - First property will be shown with the same label as the name of property. - Second one modifies the value of ``EnabledDefault`` from ``int`` to - ``bool`` representing enabled state. The last one uses different label for - property name ``Started``. - -.. _dynamic_properties: - -``DYNAMIC_PROPERTIES`` : ``bool`` (defaults to ``False``) - Whether the associated function is expected to return the properties tuple - itself. If ``True``, the result of associated function must be in form: :: - - (properties, data) - - Where ``properties`` have the same inscription and meaning as a - ``PROPERTIES`` property of class. - - Otherwise, only the ``data`` is expected. - - .. note:: - Both :py:class:`~lmi.scripts.common.command.show.LmiShowInstance` - and :py:class:`~lmi.scripts.common.command.lister.LmiInstanceLister` - expect different ``data`` to be returned. See :ref:`lmi_show_instance` - and :ref:`lmi_instance_lister` for more information. - -.. note:: - - Omitting both ``PROPERTIES`` and ``DYNAMIC_PROPERTIES`` makes the - ``LmiShowInstance`` render all attributes of instance. For - ``LmiInstanceLister`` this is not allowed, either ``DYNAMIC_PROPERTIES`` - must be ``True`` or ``PROPERTIES`` must be filled. - - -.. _lmi_check_result_properties: - -``LmiCheckResult`` properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This command typically does not produce any output if operation succeeds. The -operation succeeds if the result of associated function is expected. There are -more ways how to say what is an expected result. One way is to use ``EXPECT`` -property. The other is to provide very own implementation of -:py:class:`~lmi.scripts.common.command.checkresult.LmiCheckResult.check_result` -method. - -.. _expect: - -``EXPECT``: (mandatory) - Any value can be assigned to this property. This value is then expected - to be returned by associated function. Unexpected result is treated - as an error. - - A callable object assigned here has special meaning. This object must - accept exactly two parameters: - - 1. options - Dictionary with parsed command line options returned by - docopt_ after being processed by - :py:meth:`~lmi.scripts.common.command.endpoint.LmiEndPointCommand.transform_options`. - 2. result - Return value of associated function. - - If the associated function does not return an expected result, an error - such as: :: - - There was 1 error: - host kvm-fedora-20 - 0 != 1 - - will be presented to user which is not much helpful. To improve user - experience, the - :py:class:`~lmi.scripts.common.command.checkresult.LmiCheckResult.check_result` - method could be implemented instead. Note the example: :: - - class Update(command.LmiCheckResult): - ARG_ARRAY_SUFFIX = '_array' - - def check_result(self, options, result): - """ - :param list result: List of packages successfuly installed - that were passed as an ``<package_array>`` arguments. - """ - if options['<package_array>'] != result: - return (False, ('failed to update packages: %s' % - ", ".join( set(options['<package_array>']) - - set(result)))) - return True - - The ``execute()`` method is not listed to make the listing shorter. This - command could be used with usage string such as: :: - - %(cmd)s update [--force] [--repoid <repository>] <package> ... - - In case of a failure, this would produce output like this one: :: - - $ lmi sw update wt wt-doc unknownpackage - There was 1 error: - host kvm-fedora-20 - failed to update packages: unknownpackage - -.. seealso:: - - Docopt_ home page and its git: http://github.org/docopt/docopt. - -------------------------------------------------------------------------------- - -.. [#] Angle brackets here just mark the boundaries of name components. They - have nothing to do with arguments. -.. [#] Application object is accessible through ``app`` property of each - command instance. - -.. **************************************************************************** - -.. _CIM: http://dmtf.org/standards/cim -.. _OpenLMI: http://fedorahosted.org/openlmi/ -.. _openlmi-tools: http://fedorahosted.org/openlmi/wiki/shell -.. _docopt: http://docopt.org/ -.. _docopt-git: http://github.org/docopt - diff --git a/doc/conf.py.skel b/doc/conf.py.skel deleted file mode 100644 index af4caa9..0000000 --- a/doc/conf.py.skel +++ /dev/null @@ -1,290 +0,0 @@ -# -*- coding: utf-8 -*- -# -# OpenLMI Scripts documentation build configuration file, created by -# sphinx-quickstart on Thu Sep 5 12:50:18 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# 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('..')) - -if os.environ.get('INCLUDE_COMMANDS', '0').lower() in {'1', 'true', 'yes'}: - commands_path = os.environ.get('COMMANDS_PATH', '../commands') - commands = os.environ.get('COMMANDS', '') - if commands: - for i, cmd in enumerate(commands.split(',')): - sys.path.insert(1 + i, os.path.join(os.path.abspath(commands_path), cmd)) - - include_commands_docs = True - include_commands = commands.split(',') - -# -- 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 = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'OpenLMI Scripts' -copyright = u'2013-2014, Red Hat, Inc.' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '@@VERSION@@' -# The full version, including alpha/beta/rc tags. -release = '@@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 = ['mylf/*'] - -# 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 = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- 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 = 'default' - -# 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'] - -# 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 = 'OpenLMIScriptsdoc' - - -# -- 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': u""" -\\usepackage{imakeidx} -\\usepackage{tikz} - -\\makeatletter -\\newcount\\dirtree@lvl -\\newcount\\dirtree@plvl -\\newcount\\dirtree@clvl -\\def\\dirtree@growth{% - \\ifnum\\tikznumberofcurrentchild=1\\relax - \\global\\advance\\dirtree@plvl by 1 - \\expandafter\\xdef\\csname dirtree@p@\\the\\dirtree@plvl\\endcsname{\\the\\dirtree@lvl} - \\fi - \\global\\advance\\dirtree@lvl by 1\\relax - \\dirtree@clvl=\\dirtree@lvl - \\advance\\dirtree@clvl by -\\csname dirtree@p@\\the\\dirtree@plvl\\endcsname - \\pgf@xa=0.5cm\\relax - \\pgf@ya=-0.5cm\\relax - \\pgf@ya=\\dirtree@clvl\\pgf@ya - \\pgftransformshift{\\pgfqpoint{\\the\\pgf@xa}{\\the\\pgf@ya}}% - \\ifnum\\tikznumberofcurrentchild=\\tikznumberofchildren - \\global\\advance\\dirtree@plvl by -1 - \\fi -} - -\\tikzset{ - dirtree/.style={ - growth function=\\dirtree@growth, - every node/.style={anchor=north}, - every child node/.style={anchor=west}, - edge from parent path={(\\tikzparentnode\\tikzparentanchor) |- (\\tikzchildnode\\tikzchildanchor)} - } -} -\\makeatother -""", -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'OpenLMIScripts.tex', u'OpenLMI Scripts Documentation', - u'Michal Minář', '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', 'openlmiscripts', u'OpenLMI Scripts Documentation', - [u'Michal Minář'], 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', 'OpenLMIScripts', u'OpenLMI Scripts Documentation', - u'Michal Minář', 'OpenLMIScripts', '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' - -def setup(app): - app.add_config_value('include_commands', False, 'env') - app.add_config_value('commands', '', 'env') diff --git a/doc/configuration.rst b/doc/configuration.rst deleted file mode 100644 index 3c56ee9..0000000 --- a/doc/configuration.rst +++ /dev/null @@ -1,175 +0,0 @@ -.. _configuration: - -Configuration -============= -``lmi`` meta-command has the main configuration file located in: :: - - /etc/openlmi/scripts/lmi.conf - -User can have his own configuration file taking precedence over anything in -global one above: :: - - $HOME/.lmirc - -Configuration is written in MS Windows INI files fashion. Please refer to -corresponding `RFC 822`_ and to `ConfigParser`_ for python specific -interpretation language. - -Follows a list of sections with their list of options. Most of the options -listed here can be overridden with command line parameters. - -.. seealso:: - :py:mod:`~lmi.scripts.common.configuration` - -.. _sect_main: - -Section [Main] --------------- -.. _main_command_namespace: - -CommandNamespace : ``string`` - Python namespace, where command entry points will be searched for. - - Defaults to ``lmi.scripts.cmd``. - -.. _main_trace: - -Trace : ``boolean`` - Whether the exceptions should be logged with tracebacks. - - Defaults to ``False``. - - Can be overridden with ``--trace`` and ``--notrace`` options on - command-line. - - .. note:: - For most exceptions generated by scripts a *Verbosity* option - needs to be highest as well for tracebacks to be printed. - -.. _main_verbosity: - -Verbosity: ``integer`` - A number within 0-2 range saying, how verbose the output shall be. This - differs from `log_level`_, which controls the logging messages written to - file. If logging to console is enabled it sets the minimum severity level. - -1 Suppresses all messages except for errors. 0 shows warnings, 1 info - messages and 2 enables debug messages. This option also affects the - verbosity of commands, making them print more information to stdout. - - Defaults to 0. - - Can be overridden with ``-v`` and ``-q`` flags on command-line. - -Section [CIM] -------------- -.. _cim_namespace: - -Namespace : ``string`` - Allows to override default CIM namespace, which will be passed to - script library functions. - - Defaults to ``root/cimv2``. - -Section [SSL] -------------- -.. _ssl_verify_server_certificate: - -VerifyServerCertificate : ``boolean`` - Whether to verify server-side certificate, when making secured - connection over https. - - Defaults to ``True``. - - Can be overridden with ``-n | --noverify`` flag on command-line. - -Section [Format] ----------------- -.. _format_human_friendly: - -HumanFriendly : ``boolean`` - Whether to print values in human readable forms (e.g. with units). - - Defaults to ``False``. - - Can be overridden with ``-H | --human-frienly`` flag on command-line. - -.. _format_lister_format: - -ListerFormat : one of {``csv``, ``table``} - What format to use, when listing tabular data. ``csv`` format allows for - easy machine parsing, the second one is more human friendly. - - Defaults to ``table``. - - Can be overridden with ``-L | --lister-format`` option on command line. - -.. _format_no_headings: - -NoHeadings : ``boolean`` - Whether to suppress headings (column names) when printing tables. - - Defaults to ``False``. - - Can be overridden with ``-N | --no-headings`` option on command line. - -Section [Log] -------------- -.. _log_level: - -Level : one of {``DEBUG``, ``INFO``, ``WARNING``, ``ERROR``, ``CRITICAL``} - Minimal severity level of messages to log. Affects only logging to a - file. See the `main_verbosity`_ option controlling console logging level. - - Defaults to ``ERROR``. - -LogToConsole : ``boolean`` - Whether the logging to console is enabled. - - Defaults to ``True`` - - On command-line the same could be achieved by redirecting *stderr* to - ``/dev/null``. - -.. _log_console_format: - -ConsoleFormat : ``string`` - Format string used when logging to a console. This applies to warnings and - more severe messages. Refer to `Format String` in python's documentation for - details. - - Defaults to ``%(levelname)s: %(message)s``. - -.. _log_console_info_format: - -ConsoleInfoFormat : ``string`` - Format string used when logging to a console. Applies to info and debug - messages. Refer to `Format String` in python's documentation for details. - - - Defaults to ``%(message)s``. - -.. _log_file_format: - -FileFormat : ``string`` - Format string used, when logging to a console. This applies only when - *OutputFile* is set (see below). Refer to `Format String` in python's - documentation for details. - - Defaults to - ``%(asctime)s:%(levelname)-8s:%(name)s:%(lineno)d - %(message)s`` - -.. _log_output_file: - -OutputFile : ``string`` - Allows to set a path to file, where messages will be logged. No log - file is written at default. - - Defaults to empty string. - - Can be overridden on command line with ``--log-file`` option. - -.. **************************************************************************** - -.. _`RFC 822`: http://tools.ietf.org/html/rfc822.html -.. _`ConfigParser`: http://docs.python.org/2/library/configparser.html -.. _`Format String`: http://docs.python.org/2/library/logging.html#logrecord-attributes diff --git a/doc/gendoc.sh b/doc/gendoc.sh deleted file mode 100755 index 4ce3244..0000000 --- a/doc/gendoc.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -# -# Helper script to generate developer documentation in -# doc/modules and api.rst -# - -TOPDIR=.. - -[ -e $TOPDIR/doc/modules ] || mkdir $TOPDIR/doc/modules - -function get_modules() { - find $TOPDIR/lmi/scripts/ -type f -a -name '*.py' | \ - sed 's:/__init__\.py$::' | \ - grep -v "^${TOPDIR}/lmi/scripts/\?$" | \ - grep -v '/_' | \ - sort -u -} - -function get_module_name() { - path=`sed -e "s:^.*${TOPDIR}/lmi/scripts/\(.*\)$:\1:" -e "s:/:.:g"` - basename -s .py "$path" -} - -function get_version_string() { - out=`python $TOPDIR/setup.py --version` - git_version=`git describe HEAD 2>/dev/null` - echo -n "\`\`$out\`\`" - if [ -n "$git_version" ]; then - echo ", git: \`\`$git_version\`\`" - fi -} - -# api.rst header -cat >$TOPDIR/doc/api.rst <<_EOF_ -OpenLMI Scripts API -=================== - -This is a generated documentation form *OpenLMI Scripts* sources. - -Developer of script library will be interested in -:py:mod:\`lmi.scripts.common\` package providing useful functionality to -script development. - -Generated from version: $(get_version_string) - -Contents: - -.. toctree:: - :maxdepth: 2 - -_EOF_ - -# Generate modules/*.rst -for module_path in `get_modules`; do - module=`echo "$module_path" | get_module_name` - out=$module.rst - len=`echo $module| wc -c` - len=$(($len-1)) - underline="" - for i in `seq $len`; do underline="=$underline"; done - - cat >$TOPDIR/doc/modules/$out <<_EOF_ -$module -$underline -.. automodule:: lmi.scripts.$module - :members: -_EOF_ - echo >>$TOPDIR/doc/api.rst " modules/$module" -done diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index c95879e..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -OpenLMI Scripts documentation -============================= - -Client-side python modules and command line utilities. Contains python -libraries for interfacing with OpenLMI providers through local or remote CIMOM -with WBEM as a protocol in between. It also contains `lmi` meta-command -allowing to instrument these libraries from command line. - -Content: - -.. toctree:: - :maxdepth: 2 - - usage - configuration - script-development - api - commands - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index c9db9bc..0000000 --- a/doc/make.bat +++ /dev/null @@ -1,190 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source -set I18NSPHINXOPTS=%SPHINXOPTS% source -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^<target^>` where ^<target^> is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\OpenLMIScripts.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\OpenLMIScripts.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/doc/mylf/README.md b/doc/mylf/README.md deleted file mode 100644 index b3e7107..0000000 --- a/doc/mylf/README.md +++ /dev/null @@ -1,7 +0,0 @@ -MyLF -==== -A complete source for step-by-step tutorial hosted on -https://fedorahosted.org/openlmi/wiki/scripts/tutorial. - -It provides similar functionality as `logicalfile` script and operates upon the -same set of providers. diff --git a/doc/mylf/doc/Makefile b/doc/mylf/doc/Makefile deleted file mode 100644 index 5efe803..0000000 --- a/doc/mylf/doc/Makefile +++ /dev/null @@ -1,177 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build -COMMAND_NAME := mylf - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext cmdregen cmdline.generated - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " " - @echo " cmdregen to regenerate cmdline.generated with actual content from installed lmi help" - -cmdregen: cmdline.generated - -cmdline.generated: - if ! python -c 'import lmi.scripts.$(COMMAND_NAME)' >/dev/null; then \ - echo "Please install install the command library first." >&2; \ - exit 1; \ - fi - ( \ - echo ".."; \ - echo " !!!!!!!!!"; \ - echo -n " This is generated file. Use 'make cmdregen' to regenerate it"; \ - echo " from installed 'lmi help <CMD_NAME>'"; \ - echo " !!!!!!!!!"; \ - echo ; \ - for i in `sed -n '/entry_points/,/)/p' ../setup.py | \ - sed -n "s/\s*,\?['\"]\s*\([a-z-]\+\)\s*=.*/\1/p"`; do \ - lmi help $$i | python ../../../tools/help2rst $$i | \ - tr -d '\033' |sed 's/..1034h//'; \ - done \ - ) > $@ - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenLMIMyLFscripts.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenLMIMyLFscripts.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/OpenLMIMyLFscripts" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenLMIMyLFscripts" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/mylf/doc/cmdline.rst b/doc/mylf/doc/cmdline.rst deleted file mode 100644 index ae96494..0000000 --- a/doc/mylf/doc/cmdline.rst +++ /dev/null @@ -1,6 +0,0 @@ -LMI command line reference -========================== -.. - Write some description here. - -.. include:: cmdline.generated diff --git a/doc/mylf/doc/conf.py b/doc/mylf/doc/conf.py deleted file mode 100644 index 91e943c..0000000 --- a/doc/mylf/doc/conf.py +++ /dev/null @@ -1,285 +0,0 @@ -# -*- coding: utf-8 -*- -# -# OpenLMI MyLF scripts documentation build configuration file, created by -# sphinx-quickstart on Wed Oct 2 08:58:25 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# 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 = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'OpenLMI MyLF scripts' -copyright = u'2013-2014, Red Hat, Inc.' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.0.1' -# The full version, including alpha/beta/rc tags. -release = '0.0.1' - -# 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 = ['_build'] - -# 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 = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- 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 = 'default' - -# 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'] - -# 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 = 'OpenLMIMyLFscriptsdoc' - - -# -- 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]). -latex_documents = [ - ('index', 'OpenLMIMyLFscripts.tex', u'OpenLMI MyLF scripts Documentation', - u'Michal', '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', 'openlmimylfscripts', u'OpenLMI MyLF scripts Documentation', - [u'Michal'], 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', 'OpenLMIMyLFscripts', u'OpenLMI MyLF scripts Documentation', - u'Michal', 'OpenLMIMyLFscripts', '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' - - -# -- Options for Epub output --------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = u'OpenLMI MyLF scripts' -epub_author = u'Michal' -epub_publisher = u'Michal' -epub_copyright = u'2013-2014, Red Hat, Inc.' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -#epub_cover = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -# Allow duplicate toc entries. -#epub_tocdup = True diff --git a/doc/mylf/doc/index.rst b/doc/mylf/doc/index.rst deleted file mode 100644 index dc7a55f..0000000 --- a/doc/mylf/doc/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. OpenLMI MyLF scripts documentation master file, created by - sphinx-quickstart on Wed Oct 2 08:58:25 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to OpenLMI MyLF scripts's documentation! -================================================ - -Contents: - -.. toctree:: - :maxdepth: 2 - - cmdline - python - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/mylf/doc/make.bat b/doc/mylf/doc/make.bat deleted file mode 100644 index 4fc6844..0000000 --- a/doc/mylf/doc/make.bat +++ /dev/null @@ -1,190 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^<target^>` where ^<target^> is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\OpenLMIMyLFscripts.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\OpenLMIMyLFscripts.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/doc/mylf/lmi/__init__.py b/doc/mylf/lmi/__init__.py deleted file mode 100644 index b1a2ff0..0000000 --- a/doc/mylf/lmi/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -__import__('pkg_resources').declare_namespace(__name__) diff --git a/doc/mylf/lmi/scripts/__init__.py b/doc/mylf/lmi/scripts/__init__.py deleted file mode 100644 index b1a2ff0..0000000 --- a/doc/mylf/lmi/scripts/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -__import__('pkg_resources').declare_namespace(__name__) diff --git a/doc/mylf/lmi/scripts/mylf/__init__.py b/doc/mylf/lmi/scripts/mylf/__init__.py deleted file mode 100644 index 18bebbb..0000000 --- a/doc/mylf/lmi/scripts/mylf/__init__.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -import os - -from lmi.shell import LMIInstance, LMIInstanceName -from lmi.scripts.common import errors -from lmi.scripts.common import get_logger - -LOG = get_logger(__name__) - -def logical_file_type_name(file_identity): - """ - Get a name of file type for supplied instance of ``CIM_LogicalFile``. - """ - namemap = { - 'lmi_datafile' : 'file', - 'lmi_unixdevicefile' : 'device', - 'lmi_unixdirectory' : 'directory', - 'lmi_fifopipefile' : 'fifo', - 'lmi_symboliclink' : 'symlink', - 'lmi_unixsocket' : 'socket' - } - try: - return namemap[file_identity.classname.lower()] - except KeyError: - LOG().warn('Unhandled logical file class "%s".', - file_identity.classname) - return 'unknown' - -def permission_string(file_identity): - """ - Make a ls-like permission string for supplied instance of - ``CIM_LogicalFile``. - """ - return ''.join(l if getattr(file_identity, a) else '-' - for l, a in zip('rwx', ('Readable', 'Writeable', 'Executable'))) - -def get_computer_system(ns): - """ - :returns: Instance of ``Linux_ComputerSystem``. - """ - if not hasattr(get_computer_system, 'instance'): - get_computer_system.instance = ns.Linux_ComputerSystem.first_instance() - return get_computer_system.instance - -def get_unix_file_instance(ns, path, dereference=False): - """ - :param boolean dereference: Whether to follow symbolic links - :returns: Instance of ``LMI_UnixFile`` corresponding to given *path*. - """ - cs = get_computer_system(ns) - uf_name = ns.LMI_UnixFile.new_instance_name({ - 'CSCreationClassName' : cs.classname, - 'CSName' : cs.name, - 'LFName' : path, - 'LFCreationClassName' : 'ignored', - 'FSCreationClassName' : 'ignored', - 'FSName' : 'ignored', - }) - try: - uf = uf_name.to_instance() - if dereference: - lf = get_logical_file_instance(ns, uf, False) - if logical_file_type_name(lf) == 'symlink': - try: - target = lf.TargetFile - if not os.path.isabs(target): - target = os.path.abspath( - os.path.join(os.path.dirname(lf.Name), target)) - # recursively try to dereference - uf = get_unix_file_instance(ns, target, dereference) - except Exception as err: - LOG.warn('failed to get link target "%s": %s', - lf.TargetLink, err) - return uf - except: - raise errors.LmiFailed('No such file or directory: "%s".' % path) - -def get_logical_file_instance(ns, file_ident, dereference=False): - """ - Get an instance of ``CIM_LogicalFile`` corresponding to given file - identity. - - :param file_ident: Either a file path or an instance of ``LMI_UnixFile``. - :param boolean dereference: Whether to follow symbolic links - """ - if isinstance(file_ident, basestring): - uf = get_unix_file_instance(ns, file_ident, dereference) - elif isinstance(file_ident, LMIInstanceName): - uf = file_ident.to_instance() - else: - uf = file_ident - return uf.first_associator(AssocClass='LMI_FileIdentity') - -def make_directory_instance_name(ns, directory): - """ - Retrieve object path of a directory. - - :type directory: string - :param directory: Full path to the directory. - :rtype: :py:class:`lmi.shell.LMIInstanceName` - """ - if directory != '/': - directory = directory.rstrip('/') - cs = get_computer_system(ns) - return ns.LMI_UnixDirectory.new_instance_name( - { 'CSCreationClassName' : cs.classname - , 'CSName' : cs.name - , 'CreationClassName' : 'LMI_UnixDirectory' - , 'FSCreationClassName' : 'LMI_LocalFileSystem' - , 'FSName' : '' - , 'Name' : directory}) - -def get_directory_instance(ns, directory): - """ - Retrieve instance of `LMI_UnixDirectory`. - - :type directory: string of :py:class:`lmi.shell.LMIInstanceName` - :param directory: Full path to the directory or its instance name. - :rtype: :py:class:`lmi.shell.LMIInstance` - """ - if isinstance(directory, basestring): - directory = make_directory_instance_name(ns, directory) - if isinstance(directory, LMIInstanceName): - directory = directory.to_instance() - return directory - -def list_directory(ns, directory, file_type='any'): - """ - Yields instances of ``CIM_LogicalFile`` representing direct children of the - given directory. - - :param directory: Either a file path or an instance of - ``LMI_UnixDirectory``. - :param file_type: Filter of files made by checking their type. One of: :: - - {'any', 'file', 'device', 'directory', 'fifo', 'symlink', 'socket'} - """ - def _generate_children(): - for child in get_directory_instance(ns, directory).associators( - AssocClass='LMI_DirectoryContainsFile', - Role='GroupComponent', - ResultRole='PartComponent'): - if ( file_type and file_type != 'any' - and logical_file_type_name(child) != file_type): - continue - yield child - return sorted(_generate_children(), key=lambda i: i.Name) - -def create_directory(ns, directory): - """ - Create a directory. - - :type directory: string - :param directory: Full path to the directory. - """ - ns.LMI_UnixDirectory.create_instance( - make_directory_instance_name(ns, directory).path.keybindings) - -def delete_directory(ns, directory): - """ - Delete an empty directory. - - :param directory: Either a file path or an instance of - ``LMI_UnixDirectory``. - """ - get_directory_instance(ns, directory).delete() diff --git a/doc/mylf/lmi/scripts/mylf/cmd.py b/doc/mylf/lmi/scripts/mylf/cmd.py deleted file mode 100644 index 59b0f32..0000000 --- a/doc/mylf/lmi/scripts/mylf/cmd.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. - -""" -Read informations about file system structure. - -Usage: - %(cmd)s list [options] <directory> - %(cmd)s show [-L] <file> - %(cmd)s create <directory> - %(cmd)s delete <directory> - -Options: - -t --type <type> Filter listed files by their type. One of: - any, file, device, directory, fifo, symlink, socket. - Defaults to any. - -L --dereference Causes symlink to be followed. -""" - -from lmi.scripts import mylf -from lmi.scripts.common import command -from lmi.scripts.common import errors - -class Show(command.LmiLister): - COLUMNS = ('Attribute', 'Value') - - def transform_options(self, options): - options['<path>'] = options.pop('<file>') - - def execute(self, ns, path, _dereference): - uf = mylf.get_unix_file_instance(ns, path, _dereference) - lf = mylf.get_logical_file_instance(ns, uf, _dereference) - return [ - ('Path' , lf.Name), - ('Type' , mylf.logical_file_type_name(lf)), - ('User ID' , uf.UserID), - ('Group ID' , uf.GroupID), - ('Size' , lf.FileSize), - ('Permissions' , mylf.permission_string(lf)) - ] - -class List(command.LmiInstanceLister): - CALLABLE = mylf.list_directory - PROPERTIES = ( - 'Name', - ('Type', mylf.logical_file_type_name), - ('Permissions', mylf.permission_string), - ('Size', 'FileSize')) - - def verify_options(self, options): - if ( options['--type'] is not None - and not options['--type'].lower() in { - 'any', 'file', 'directory', 'symlink', 'dev', 'socket', 'fifo'}): - raise errors.LmiInvalidOptions( - 'Unsupported type: %s' % options['--type']) - - def transform_options(self, options): - file_type = options.pop('--type') - if file_type is None: - file_type = 'any' - options['file-type'] = file_type - -class Create(command.LmiCheckResult): - EXPECT = None - CALLABLE = mylf.create_directory - -class Delete(command.LmiCheckResult): - EXPECT = None - CALLABLE = mylf.delete_directory - -MyLF = command.register_subcommands('MyLF', __doc__, - { 'show' : Show - , 'list' : List - , 'create' : Create - , 'delete' : Delete - }) diff --git a/doc/mylf/setup.py b/doc/mylf/setup.py deleted file mode 100644 index 9281962..0000000 --- a/doc/mylf/setup.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -from setuptools import setup - -try: - long_description = open('README.md', 'rt').read() -except IOError: - long_description = '' - -setup( - name='openlmi-scripts-mylf', - version='0.0.1', - description='OpenLMI scripts for LogicalFile profile', - long_description=long_description, - author=u'Michal', - author_email='Minar', - url='https://github.com/openlmi/openlmi-mylf', - download_url='https://github.com/openlmi/openlmi-mylf/tarball/master', - platforms=['Any'], - license="BSD", - classifiers=[ - 'License :: OSI Approved :: BSD License', - 'Operating System :: POSIX :: Linux', - 'Topic :: System :: Systems Administration', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - install_requires=['openlmi-scripts'], - - namespace_packages=['lmi', 'lmi.scripts'], - packages=['lmi', 'lmi.scripts', 'lmi.scripts.mylf'], - include_package_data=True, - - entry_points={ - 'lmi.scripts.cmd': [ - 'mylf = lmi.scripts.mylf.cmd:MyLF', - ], - }, - ) diff --git a/doc/script-development.rst b/doc/script-development.rst deleted file mode 100644 index a4ec801..0000000 --- a/doc/script-development.rst +++ /dev/null @@ -1,668 +0,0 @@ -Script Development -================== -This provides a general overview on what script is, how is it written -and is interfaced with. - -Prerequisities --------------- -Reader should be familiar about a CIM_ (Common Information Model). He should -have a general idea about, what OpenLMI_ is and what it does. He should get -familiar with ``lmishell``, which is a python binary shipped with -openlmi-tools_. - -Also user should be acquinted with writing help strings for unix command line -utilities [#]_. - -Introduction ------------- -By a *script* in this document we mean: - - * Python library utilizing ``lmishell`` for instrumenting CIM providers - through a CIMOM broker. It resides in ``lmi.scripts.<profile_name>`` - package, where ``<profile_name>`` corresponds to a DMTF profile or an - arbitrary set of ``OpenLMI`` providers. - * Command wrappers for this library as a set of classes inheriting from - :py:class:`~lmi.scripts.common.command.base.LmiBaseCommand`. These may - create a hierarchy of nested subcommands. They are the entry points of - ``lmi`` meta-command to the wrapped functionality of library. - -Command wrappers are part of the library usually grouped in a single -module named after the ``lmi`` subcommand or ``cmd``: :: - - lmi.scripts.<profile_name>.cmd - -Writing a library ------------------ -Library shall consist of a set of functions taking a namespace or -connection object as a first argument. There are no special requirements -on how to divide these functions into submodules. Use common sense. Smaller -scripts can have all functionality in -``lmi/scripts/<profile_name>/__init__.py`` module. With wrappers usually -contained in ``lmi/scripts/<profile_name>/cmd.py``. - -Library should be written with an ease of use in mind. Functions should -represent possible use cases of what can be done with particular -providers instead of wrapping 1-to-1 a CIM class's methods in python -functions. - -Any function that shall be called by a command wrapper must accect a -``namespace`` argument, which by a convention is called ``ns``. It's an -instance of :py:class:`lmi.shell.LMINamespace` providing quick access to -represented CIM namespace [#]_ and its classes. It's also possible to specify -that function shall be passed a raw :py:class:`lmi.shell.LMIConnection` object. -For details see :ref:`function_invocation`. - -Service example -~~~~~~~~~~~~~~~ -Suppose we have a service profile and we want to write a python interface -for. Real provider implementation can be found at ``src/service`` directory -in upstream git [#]_. For more information please refer to `service description`_. - -As you may see, this implements single CIM class ``LMI_Service`` with a few -useful methods such as: - - * ``StartService()`` - * ``StopService()`` - -We'd like our users to provide a way how to list system services, get a -details for one of them and allow to start, stop and restart them. - -Simplified [#]_ version of some of these functions may look like this: :: - - def list_services(ns, kind='enabled'): - for service in sorted(ns.LMI_Service.instances(), - key=lambda i: i.Name): - if kind == 'disabled' and service.EnabledDefault != \ - ns.LMI_Service.EnabledDefaultValues.Disabled: - continue - if kind == 'oneshot' and service.EnabledDefault != \ - ns.LMI_Service.EnabledDefaultValues.NotApplicable: - continue - if kind == 'enabled' and service.EnabledDefault != \ - ns.LMI_Service.EnabledDefaultValues.Enabled: - # list only enabled - continue - yield service - -It yields instances of ``LMI_Service`` class. There is no need to use -exclusively ``yield`` statement instead of ``return``. We prefer to use it in -enumerating functions because of memory usage reduction (which is possible to -occur in the future, when underlying components will also allow for lazy -evaluation). Moreover user may limit the number of instances listed, reducing -the number of instances evaluated. - -:: - - from lmi.shell import LMIInstanceName - from lmi.scripts.common import get_logger - from lmi.scripts.common.errors import LmiFailed - - LOG = get_logger(__name__) - - def start_service(ns, service): - if isinstance(service, basestring): - # let's accept service as a string - inst = ns.LMI_Service.first_instance(key="Name", value=service) - name = service - else: # or as LMIInstance or LMIInstanceName - inst = service - name = inst.path['Name'] - if inst is None: - raise LmiFailed('No such service "%s".' % name) - if isinstance(inst, LMIInstanceName): - # we need LMIInstance - inst = inst.to_instance() - res = inst.StartService() - if res == 0: - LOG().debug('Started service "%s" on hostname "%s".', - name, ns.hostname) - return res - -In similar fashion, ``stop_service``, ``restart_service`` and others could be -written. - -``ns`` argument typically points to ``root/cimv2`` namespace, which is the -main implementation namespace for ``OpenLMI`` providers. One could also write -these functions operating upon a connection object like this: :: - - def get_instance(c, service): - inst = c.root.cimv2.LMI_Service.first_instance( - key="Name", value=service) - if inst is None: - raise LmiFailed('No such service "%s".' % service) - return inst - -User can then easily access any other namespace he may need. Command classes -need to be informed about what wrapped function expects. This will be -explained later in more detail (see :ref:`function_invocation`). - -The ``LOG`` variable provides acces to the logger of this module. Messages -logged in this way end up in a log file [#]_ and console. Implicitly only -warnings and higher priority messages are logged into a console. This is -controllable with ``lmi`` parameters. - -Useful information should not be rendered or printed by these functions -directly. Wrapper commands shall post-process instances or data returned, -render useful information and print it on standard output stream. - -If operation fails due to some not-so-unexpected error, please use -:py:class:`~lmi.scripts.common.errors.LmiFailed` exception with human readable -description. - -For more *real world* examples, take a look on scripts already present in our -`upstream git`_. - -.. _command_wrappers_overview: - -Command wrappers overview -------------------------- -They are a set of commands wrapping up library's functionality in a set of -commands creating a tree invocable by ``lmi`` meta-command. All commands are -subclasses of :py:class:`~lmi.scripts.common.command.base.LmiBaseCommand`. - -Behaviour of commands is controlled by class properties such as these: :: - - class Show(command.LmiShowInstance): - CALLABLE = 'lmi.scripts.service:get_instance' - PROPERTIES = ( - 'Name', - 'Caption', - ('Enabled', lambda i: i.EnabledDefault == 2), - ('Active', 'Started'), - 'Status') - -Example above contains definition of **show** command for instances of -``LMI_Service``. Its associated function is ``get_instance()`` located in -``lmi.scripts.service`` module [#]_. Properties used will be described -in detail later (see :ref:`lmi_instance_commands_properties`). Let's just say, -that ``PROPERTIES`` specify a way how the instance is rendered. - -Top-level commands -~~~~~~~~~~~~~~~~~~ -Are entry points of a script library. They are direct subcommands of ``lmi``. -For example: :: - - $ lmi help - $ lmi service list - $ lmi sw show openlmi-providers - -``help``, ``service`` and ``sw`` are top-level commands. One script library -(such as ``service`` above) can provide one or more of them. They need to be -listed in a ``setup.py`` script in ``entry_points`` argument of ``setup()`` -function. More details will be noted later in `Writing setup.py`_. - -They contain usage string which is a documentation and prescription of -command-line arguments in one string. This string is printed when user -requests command's help: :: - - $ lmi help - -.. _usage_string: - -Usage string -^^^^^^^^^^^^ -looks like this: :: - - """ - System service management. - - Usage: - %(cmd)s list [--all | --disabled] - %(cmd)s start <service> - - Options: - --all List all services available. - --disabled List only disabled services. - """ - -Format of this string is very important, it's parsed by a docopt_ command line -parser, generating options dictionary for commands. Please refer to its -documentation for details. - -.. note:: - - There is one deviation to *classical* usage string. It's the use of - ``%(cmd)s`` formatting mark. This is replaced with full command's name. - Full name means that all subcommands and binary name prefixing current - command on command line are part of it. So for example full name of - command **list** in a following string passed to command line: :: - - lmi sw list pkgs - - is ``lmi sw list``. - - If parsing **sw** usage, it is just ``lmi sw``. - - Please use this notation instead of writing your own usages completely. - Although it may work from command line, it won't work in interactive - mode without ``%(cmd)s`` being used. - -.. _end-point_commands_introduction: - -End-point commands -~~~~~~~~~~~~~~~~~~ -Are associated with one or more function of script library. They handle the -following: - - 1. call docopt_ parser on command line arguments - 2. make some name pre-processing on them (see - :ref:`pre_processing_properties`) - 3. verify them (see :ref:`end-point_commands`) - 4. transform them (see :ref:`end-point_commands`) - 5. pass them to associated function - 6. collect results - 7. render them and print them - -Developper of command wrappers needs to be familiar about each step. We will -describe them later in details. - -There are following end-point commands available for subclassing: - - * ``LmiCheckResult`` (see :ref:`lmi_check_result`) - * ``LmiLister`` (see :ref:`lmi_lister`) - * ``LmiInstanceLister`` (see :ref:`lmi_instance_lister`) - * ``LmiShowInstance`` (see :ref:`lmi_show_instance`) - -They differ in how they render the result obtained from associated function. - -These are listed in depth in :ref:`end-point_commands`. - -.. _command_multiplexers_introduction: - -Command multiplexers -~~~~~~~~~~~~~~~~~~~~ -Provide a way how to group multiple commands under one. Suppose you want to -list packages, repositories and files. All of these use cases need different -arguments, and render different information so logically they should be -represented by independent end-point commands. What binds them together is the -user's wish to *list* something. He may wish for other things like *show*, -*add*, *remove* etc. Having all combination of these wishes and things would -generate a lot of commands under the top-level one. Let's instead group them -under particular *wish* like this: - - * ``sw list packages`` - * ``sw list repositories`` - * ``sw list files`` - * ``sw show package`` - -To reflect it in our commands definition hierarchy, we need to use -:py:class:`~lmi.scripts.common.command.multiplexer.LmiCommandMultiplexer` -command. - -:: - - class Lister(command.LmiCommandMultiplexer): - """ List information about packages, repositories or files. """ - COMMANDS = { - 'packages' : PkgLister, - 'repositories' : RepoLister, - 'files' : FileLister - } - -Where ``COMMANDS`` property maps subcommand classes to their names as will -be passed on command line. Each command multiplexer consumes one command -argument from command line, representing the subcommand and passes the rest -of options to it. In this way we can create arbitrarily tall command trees. - -Top-level command is nothing else than a subclass of ``LmiCommandMultiplexer``. - -Specifying profile and class requirements -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Most commands require some provider installed on managed machine to work -properly. Each such provider should be represented by an instance of -``CIM_RegisteredProfile`` on remote broker. This instance looks like -this (in MOF syntax): :: - - instance of CIM_RegisteredProfile { - InstanceID = "OpenLMI+OpenLMI-Software+0.4.2"; - RegisteredOrganization = 1; - OtherRegisteredOrganization = "OpenLMI"; - RegisteredVersion = "0.4.2"; - AdvertiseTypes = [2]; - RegisteredName = "OpenLMI-Software"; - }; - -We are interested just in ``RegisteredName`` and ``RegisteredVersion`` -properties that we'll use for requirement specification. - -Requirement is written in *LMIReSpL* language. For its formal definition -refer to documentation of :py:mod:`~lmi.scripts.common.versioncheck.parser`. -Since the language is quite simple, few examples should suffice: - - ``'OpenLMI-Software < 0.4.2'`` - Requires OpenLMI Software provider to be installed in version lower - than ``0.4.2``. - ``'OpenLMI-Hardware == 0.4.2 & Openlmi-Software >= 0.4.2'`` - Requires both hardware and software providers to be installed in - particular version. Short-circuit evaluation is utilized here. It - means that in this example OpenLMI Software won't be queried unless - OpenLMI Hardware is installed and having desired version. - ``'profile "OpenLMI-Logical File" > 0.4.2'`` - If you have spaces in the name of profile, surround it in double - quotes. ``profile`` keyword is optional. It could be also present in - previous examples. - -Version requirements are not limited to profiles only. CIM classes may be -specified as well: - - ``'class LMI_SoftwareIdentity >= 0.3.0 & OpenLMI-LogicalFile'`` - In case of class requirements the ``class`` keyword is mandatory. As - you can see, version requirement is optional. - ``'! (class LMI_SoftwareIdentity | class LMI_UnixFile)'`` - Complex expressions can be created with the use of brackets and other - operators. - -One requirement is evaluated in these steps: - - Profile requirement - 1. Query ``CIM_RegisteredProfile`` for instances with - ``RegisteredName`` matching given name. If found, go to 2. Otherwise - query ``CIM_RegisteredSubProfile`` [#subprof]_ for instances with - ``RegisteredName`` matching given name. If not found return - ``False``. - 2. Select the (sub)profile with highest version and go to 3. - 3. If the requirement has version specification then compare it to the - value of ``RegisteredVersion`` using given operator. If the relation - does not apply, return ``False``. - 4. Return ``True``. - - Class requirement - 1. Get specified class. If not found, return ``False``. - 2. If the requirement has version specification then compare it to the - value of ``Version`` [#missing_version]_ qualifier of - obtained class using given operator. And if the relation - does not apply, return ``False``. - 3. Return ``True``. - -Now let's take a look, where these requirements can be specified. -There is a special select command used to specify which command to load -for particular version on remote broker. It can be written like this: :: - - from lmi.scripts.common.command import LmiSelectCommand - - class SoftwareCMD(LmiSelectCommand): - - SELECT = [ - ( 'OpenLMI-Software >= 0.4.2 & OpenLMI-LogicalFile' - , 'lmi.scripts.software.current.SwLFCmd') - , ( 'OpenLMI-Software >= 0.4.2' - , 'lmi.scripts.software.current.SwCmd') - , ('OpenLMI-Software', 'lmi.scripts.software.pre042.SwCmd') - ] - -It says to load ``SwLFCmd`` command in case both OpenLMI Software and -OpenLMI LogicalFile providers are installed. If not, load the ``SwCMD`` from -``current`` module for OpenLMI Software with recent version and fallback to -``SwCmd`` for anything else. If the OpenLMI Software provider is not available -at all, no command will be loaded and exception will be raised. - -Previous command could be used as an entry point in your ``setup.py`` script -(see the :ref:`entry_points`). There is also a utility that makes it look -better: :: - - from lmi.scripts.common.command import select_command - - SoftwareCMD = select_command('SoftwareCMD', - ( 'OpenLMI-Software >= 0.4.2 & OpenLMI-LogicalFile' - , 'lmi.scripts.software.current.SwLFCmd'), - ( 'OpenLMI-Software >= 0.4.2', 'lmi.scripts.software.current.SwCmd'), - ('OpenLMI-Software', 'lmi.scripts.software.pre042.SwCmd') - ) - -.. seealso:: - Documentation of - :py:class:`~lmi.scripts.common.command.select.LmiSelectCommand` and - :py:class:`~lmi.scripts.common.command.helper.select_command`. - - And also notes on related :ref:`lmi_select_command_properties`. - -Command wrappers module -~~~~~~~~~~~~~~~~~~~~~~~ -Usually consists of: - - 1. license header - 2. usage dostring - parseable by docopt_ - 3. end-point command wrappers - 4. single top-level command - -The top-level command is usally defined like this: :: - - Service = command.register_subcommands( - 'Service', __doc__, - { 'list' : Lister - , 'show' : Show - , 'start' : Start - , 'stop' : Stop - , 'restart' : Restart - }, - ) - -Where the ``__doc__`` is a usage string (see usage_string_) and module's doc -string at the same time. It's mentioned in point 2. ``Service`` is a name, -which will be listed in ``entry_points`` dictionary described in section below -(entry_points_). The global variable's name we assign to should be the same as -the value of the first argument to -:py:func:`~lmi.scripts.common.command.helper.register_subcommands`. The last -argument here is the dictionary mapping all subcommands of **service** to their -names [#]_. - -Egg structure -~~~~~~~~~~~~~ -Script library is distributed as an python egg, making it easy to distribute -and install either to system or user directory. - -Following tree shows directory structure of *service* egg residing in -`upstream git`_: - -.. only:: not latex - - :: - - commands/service - ├── lmi - │ ├── __init__.py - │ └── scripts - │ ├── __init__.py - │ └── service - │ ├── cmd.py - │ └── __init__.py - ├── README.md - └── setup.py - -.. raw:: latex - - \begin{center} - \begin{tikzpicture}[dirtree] - \node {commands/service} - child { node {lmi} - child { node {\_\_init\_\_.py} } - child { node {scripts} - child { node {\_\_init\_\_.py} } - child { node {service} - child { node {cmd.py} } - child { node {\_\_init\_\_.py} } - } - } - } - child { node {README.md} } - child { node {setup.py} }; - \end{tikzpicture} \\ - \end{center} - -This library then can be imported with: :: - - from lmi.scripts import service - -``commands/service/scripts/service`` must be a package (directory with -``__init__.py``) because ``lmi.scripts`` is a namespace package. It -can have arbitrary number of modules and subpackages. The care should be taken -to make the API easy to use and learn though. - -Writing ``setup.py`` --------------------- -Follows a minimal example of ``setup.py`` script for service library. :: - - from setuptools import setup, find_packages - setup( - name="openlmi-scripts-service", - version="0.1.0", - description='LMI command for system service administration.', - url='https://github.com/openlmi/openlmi-scripts', - platforms=['Any'], - license="BSD", - install_requires=['openlmi-scripts'], - namespace_packages=['lmi', 'lmi.scripts'], - packages=['lmi', 'lmi.scripts', 'lmi.scripts.service'], - - entry_points={ - 'lmi.scripts.cmd': [ - 'service = lmi.scripts.service.cmd:Service', - ], - }, - ) - -.. _entry_points: - -Entry points -~~~~~~~~~~~~ -The most notable argument here is ``entry_points`` which is a dictionary -containing python namespaces where plugins are registered. In this case, we -register single top-level command (see `Top-level commands`_) called -``service`` in ``lmi.scripts.cmd`` namespace. This particular namespace is used -by ``lmi`` meta-command when searching of registered user commands. ``Service`` -is a command multiplexer, created with a call to -:py:func:`~lmi.scripts.common.command.helper.register_subcommands` grouping -end-point commands together. - -Next example shows setup with more top-level commands -(of storage scripts library): :: - - entry_points={ - 'lmi.scripts.cmd': [ - 'fs = lmi.scripts.storage.fs_cmd:Fs', - 'partition = lmi.scripts.storage.partition_cmd:Partition', - 'raid = lmi.scripts.storage.raid_cmd:Raid', - 'lv = lmi.scripts.storage.lv_cmd:Lv', - 'vg = lmi.scripts.storage.vg_cmd:Vg', - 'storage = lmi.scripts.storage.storage_cmd:Storage', - 'mount = lmi.scripts.storage.mount_cmd:Mount', - ], - }, - -Conventions ------------ -There are several conventions you should try to follow in your shiny scripts. - -Logging messages -~~~~~~~~~~~~~~~~ -In each module where logging facilities are going to be used, define global -varibale ``LOG`` like this: :: - - from lmi.scripts.common import get_logger - - LOG = get_logger(__name__) - -It's a callable used throughout particular module in this way: :: - - LOG().warn('All the data of "%s" will be lost!', partition) - -Each message should be a whole sentence. It shall begin with an upper case -letter and end with a dot or other sentence terminator. - -Bad example: :: - - LOG().info('processing %s', card) - -Exceptions -~~~~~~~~~~ -Again all the exceptions should be initialized with messages forming -a whole sentence. - -They will be catched and printed on *stderr* by lmi meta-command. If the -*Trace* option in :ref:`sect_main` is on, traceback will be printed. There is -just one exception. If the exception inherits from -:py:class:`~lmi.scripts.common.errors.LmiError`, traceback won't be printed -unless verbosity level is highest: :: - - # self refers to some command - self.app.config.verbosity == self.app.config.OUTPUT_DEBUG - -This is a feature allowing for common error use-cases to be gracefully -handled. In your scripts you should stick to using -:py:class:`~lmi.scripts.common.errors.LmiFailed` for such exceptions. - -Following is an example of such a common error-case, where printing traceback -does not add any interesting information: :: - - iname = ns.LMI_Service.new_instance_name({ - "Name": service, - "CreationClassName" : "LMI_Service", - "SystemName" : cs.Name, - "SystemCreationClassName" : cs.CreationClassName - }) - inst = iname.to_instance() - if inst is None: - raise errors.LmiFailed('No such service "%s".' % service) - # process the service instance - -``service`` is a name provided by user. If such a service is not found, -``inst`` will be assigned ``None``. In this case we don't want to continue in -script's execution thus we raise an exception. We provide very clear message -that needs no other comment. We don't want any traceback to be printed, thus -the use of :py:class:`~lmi.scripts.common.errors.LmiFailed`. - -Detailed description --------------------- -These pages provide more details of some aspects: - -.. toctree:: - :maxdepth: 2 - - command-classes - command-properties - -Tutorial --------- - -.. toctree:: - :maxdepth: 2 - - script-tutorial - -------------------------------------------------------------------------------- - -.. seealso:: - - Docopt_ documentation. - - :ref:`command_classes` - - :ref:`command_properties` - - :ref:`script_tutorial` - -------------------------------------------------------------------------------- - -.. [#] Described by a POSIX. -.. [#] Default namespace is ``"root/cimv2"``. -.. [#] view: https://fedorahosted.org/openlmi/browser/openlmi-providers - git: ``ssh://git.fedorahosted.org/git/openlmi-providers.git/`` -.. [#] Simplified here means that there are no documentation strings - and no type checking. -.. [#] If logging to a file is enabled in configuration. -.. [#] Precisely in an ``__init__.py`` module of this package. -.. [#] These names must exactly match the names in usage strings. - -.. [#subprof] This is a subclass of ``CIM_RegisteredProfile`` thus it has the - same properties. -.. [#missing_version] If the Version qualifier is missing, -1 will be used - for comparison instead of empty string. - -.. **************************************************************************** - -.. _CIM: http://dmtf.org/standards/cim -.. _OpenLMI: http://fedorahosted.org/openlmi/ -.. _openlmi-tools: http://fedorahosted.org/openlmi/wiki/shell -.. _docopt: http://docopt.org/ -.. _`service description`: https://fedorahosted.org/openlmi/wiki/service -.. _`upstream git`: https://github.com/openlmi/openlmi-scripts diff --git a/doc/script-tutorial.rst b/doc/script-tutorial.rst deleted file mode 100644 index 517a9dd..0000000 --- a/doc/script-tutorial.rst +++ /dev/null @@ -1,447 +0,0 @@ -.. _script_tutorial: - -Script Tutorial -=============== - -This is a step-by-step tutorial on developing script library for -*OpenLMI* providers. - -Required knowledge ------------------- - -You should be familiar with terms like *CIM*, *cimom*, *schema*, -*provider*, *DMTF* profile. -`This <http://fedorahosted.org/openlmi/wiki/ProviderTutorialIntroduction>`_ -short tutorial should be enough to get you started. - -You should also be familiar with scripting in *python* and -`lmishell <http://www.openlmi.org/using_lmishell>`_ which we use heavily -in snippets below. - -Preparation ------------ - -This tutorial assumes you have ``tog-pegasus`` *cimom* up and running -with ``openlmi-logicalfile`` providers installed and registered on -remote host. We will connect to it from client machine which needs the -following installed: - -* ``openlmi-python-base`` -* ``openlmi-tools`` -* ``openlmi-scripts`` - -Installing python dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For the first two items you may use standard rpms build for Fedora. The -first one is available from ''OpenLMI Nightly'' repository having the -newest builds: - -.. code-block:: sh - - cat > /etc/yum.repos.d/openlmi-nightly.repo - [openlmi-nightly] - name=openlmi-nightly - baseurl=http://openlmi-rnovacek.rhcloud.com/rpm/rawhide/ - gpgcheck=0 - enabled = 1 - ^D - -And then run: - -.. code-block:: sh - - yum install openlmi-python-base openlmi-tools - -Or you may install them to your user directory as python eggs with -``pip``: - -.. code-block:: sh - - pip install openlmi-python openlmi-tools - -The last one can only be installed with ``pip``: - -.. code-block:: sh - - pip install openlmi-scripts - -Or directly from git repository (viz `below <#install_from_git>`_). - -Setting up environment -~~~~~~~~~~~~~~~~~~~~~~ - -We'll stick to the process described -`here <https://github.com/openlmi/openlmi-scripts#developing-lmi-scripts>`_ -that let us develop quickly without the need to reinstall anything while -making changes. - -First let's check out our ``openlmi-scripts`` repository: - -:: - - git clone https://github.com/openlmi/openlmi-scripts.git - cd openlmi-scripts - -Optionally we may install ``openlmi-scripts`` to user directory if not -having done yet: - -.. code-block:: sh - - python setup.py install --user - -Then let's set up the workspace directory: - -.. code-block:: sh - - WSP=~/.python_workspace - mkdir $WSP - # may be added to `$HOME/.profile` or `$HOME/.bashrc` - export PYTHONPATH=$WSP:$PYTHONPATH - export PATH="$PATH:$WSP" - -Making our command structure -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We'll use provided ``commands/make_new.py`` script to create the basic -structure and ``setup.py`` file: - -.. code-block:: sh - - cd commands - # this will ask us additional questions used to create setup.py file - ./make_new.py mylf - -Because a script implementation for ``OpenLMI LogicalFile`` profile is -already present in upstream repository (in ``commands/logicalfile``), we -need to name our library distinctly (e.g. ``mylf``). - -Following structure should be created: - -:: - - mylf - ├── doc - │  ├── _build - │  ├── cmdline.rst - │  ├── conf.py - │  ├── err.log - │  ├── index.rst - │  ├── make.bat - │  ├── _static - │  └── _templates - ├── lmi - │  ├── __init__.py - │  └── scripts - │  ├── __init__.py - │  └── mylf - │  ├── cmd.py - │  └── __init__.py - ├── README.md - └── setup.py - -We should check that everything matches in ``mylf/setup.py`` and correct -any shortcomings. - -OpenLMI LogicalFile introduction --------------------------------- - -*OpenLMI LogicalFile* is a CIM provider which provides a way to read -information about files and directories. The provider also allows to -traverse the file hierarchy, create and remove empty directories. - -.. figure:: _static/logicalfile.png - :align: center - :alt: LogicalFile model - - LogicalFile model - -It consists mainly of few specializations of ``CIM_LogicalFile`` -representing any type of file on filesystem, ``LMI_UnixFile`` holding -unix specific information for each such file and association classes -between them. ``CIM_LogicalFile`` has following key properties inherited -by ``LMI_*`` subclasses above: - -* **Name** -* **CSName** -* **CSCreationClassName** -* ``FSCreationClassName`` -* ``CreationClassName`` -* ``FSName`` - -Only those shown in **bold** are mandatory. Others are ignored when -requesting an instance of ``CIM_LogicalFile``. This applies also to -``LMI_UnixFile`` with **Name** being replaced with **LFName**. None of -the presented classes supports enumeration of instances. Only references -can be obtained. - -With ``CreateInstance()`` and ``DeleteInstance()`` calls issued on -class/object of ``LMI_UnixDirectory`` we are able to create and delete -directories. - -Writing usage string -~~~~~~~~~~~~~~~~~~~~ - -Usage string is a module's documentation, help message and a -prescription for command line parser all-in-one. Writing it is pretty -straightforward. Let's put it to ``mylf/lmi/scripts/mylf/cmd.py``: - -:: - - """ - Read informations about file system structure. - - Usage: - %(cmd)s list [options] <directory> - %(cmd)s show [-L] <file> - %(cmd)s create <directory> - %(cmd)s delete <directory> - - Options: - -t --type <type> Filter listed files by their type. One of: - any, file, device, directory, fifo, symlink, socket. - Defaults to any. - -L --dereference Causes symlink to be followed. - """ - -The first line provides a short description shown in help of ``lmi`` -meta-command for its registered subcommand. Text under ``Usage:`` and -``Options:`` are parsed by ``doctopt``. Please refer to its -documentation at http://docopt.org for more information. - -**Note** the ``%(cmd)s`` string which needs to be present instead of -``lmi mylf`` or similar command names. - -Let's add one more snippet so we can test it: - -:: - - from lmi.scripts.common import command - - MyLF = command.register_subcommands('MyLF', __doc__, {}) - -This creates a command multiplexer without any children (we'll add them -later). - -And finally let's modify our ``mylf/setup.py`` by adding entry point: - -:: - - entry_points={ - 'lmi.scripts.cmd': [ - 'mylf = lmi.scripts.mylf.cmd:MyLF', - ], - } - -Now we can install it and test it: - -.. code-block:: sh - - # make sure the $WSP is in $PYTHONPATH - python mylf/stup.py develop --install-dir=$WSP - lmi help - lmi help mylf - -We should be able to see the usage string we've written. - -Implementing ``list`` -~~~~~~~~~~~~~~~~~~~~~ - -Most of neccessary functionality has been implemented in previous -snippet for the ``show`` command. Following snippet is enough to -generate all the files in directory. Put it again to -``mylf/lmi/scripts/mylf/__init__.py``. - -:: - - def make_directory_instance_name(ns, directory): - """ - Retrieve object path of a directory. - - :type directory: string - :param directory: Full path to the directory. - :rtype: :py:class:`lmi.shell.LMIInstanceName` - """ - if directory != '/': - directory = directory.rstrip('/') - cs = get_computer_system(ns) - return ns.LMI_UnixDirectory.new_instance_name( - { 'CSCreationClassName' : cs.classname - , 'CSName' : cs.name - , 'CreationClassName' : 'LMI_UnixDirectory' - , 'FSCreationClassName' : 'LMI_LocalFileSystem' - , 'FSName' : '' - , 'Name' : directory}) - - def get_directory_instance(ns, directory): - """ - Retrieve instance of `LMI_UnixDirectory`. - - :type directory: string of :py:class:`lmi.shell.LMIInstanceName` - :param directory: Full path to the directory or its instance name. - :rtype: :py:class:`lmi.shell.LMIInstance` - """ - if isinstance(directory, basestring): - directory = make_directory_instance_name(ns, directory) - if isinstance(directory, LMIInstanceName): - directory = directory.to_instance() - return directory - - def list_directory(ns, directory, file_type='any'): - """ - Yields instances of ``CIM_LogicalFile`` representing direct children of the - given directory. - - :param directory: Either a file path or an instance of - ``LMI_UnixDirectory``. - :param file_type: Filter of files made by checking their type. One of: :: - - {'any', 'file', 'device', 'directory', 'fifo', 'symlink', 'socket'} - """ - def _generate_children(): - for child in get_directory_instance(ns, directory).associators( - AssocClass='LMI_DirectoryContainsFile', - Role='GroupComponent', - ResultRole='PartComponent'): - if ( file_type and file_type != 'any' - and logical_file_type_name(child) != file_type): - continue - yield child - return sorted(_generate_children(), key=lambda i: i.Name) - -Note the ``associators()`` call on ``LMI_UnixDirectory`` instance. It -enumerates all ``CIM_LogicalFile`` instances that are referenced by -``LMI_DirectoryContainsFile`` associations. These represent a relation -of parent directory and its direct children. Parent directory is -referenced with ``GroupComponent`` role while the children with -``PartComponent``. It's advisable to always provide as much information -to calls like: - -- ``associators()`` -- ``associator_names()`` -- ``references()`` -- ``reference_names()`` - -as possible. Without the ``AssocClass`` parameter given, broker would -try to enumerate all instrumented association classes possible, -resulting in very poor performance. Both ``Role`` and ``ResultRole`` -parameters need to be given here, otherwise a parent directory of the -one being enumerated would also appear in output. - -Following subclass of ``LmiInstanceLister`` needs to be added to -``mylf/lmi/scripts/mylf/cmd.py`` and added to ``MyLF`` subcommands -dictionary (omitted for now). - -:: - - class List(command.LmiInstanceLister): - CALLABLE = mylf.list_directory - PROPERTIES = ( - 'Name', - ('Type', mylf.logical_file_type_name), - ('Permissions', mylf.permission_string), - ('Size', 'FileSize')) - - def verify_options(self, options): - if ( options['--type'] is not None - and not options['--type'].lower() in { - 'any', 'file', 'directory', 'symlink', 'dev', 'socket', 'fifo'}): - raise errors.LmiInvalidOptions( - 'Unsupported type: %s' % options['--type']) - - def transform_options(self, options): - file_type = options.pop('--type') - if file_type is None: - file_type = 'any' - options['file-type'] = file_type - -Instead of defining our own ``execute()`` method, we just associate -wrapped function defined previously using ``CALLABLE`` property. Thanks -to the ability to transform option names in any way, we are not limited -to the use of arguments as listed in usage string. Apart from renaming -options, we also check for the value of ``--type``. Overriding -``verify_options()`` to check for validity of options is the more -preferred approach compared to delayed checking in associated function. - -Implementing ``create`` and ``delete`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Let's again start with content of ``mylf/lmi/scripts/mylf/__init__.py`` -module. - -:: - - def create_directory(ns, directory): - """ - Create a directory. - - :type directory: string - :param directory: Full path to the directory. - """ - ns.LMI_UnixDirectory.create_instance( - make_directory_instance_name(ns, directory).path.keybindings) - - def delete_directory(ns, directory): - """ - Delete an empty directory. - - :param directory: Either a file path or an instance of - ``LMI_UnixDirectory``. - """ - get_directory_instance(ns, directory).delete() - -``create_instance()`` call of any ``LMIClass`` creates a new instance, -in this case we create an instance of ``LMI_UnixDirectory``. If it -exists already, an exception will be raised. On the other hand, -``delete_directory()`` operates on an ``LMIInstance`` which must exists. -If directory does not exist or it's not empty, an exception will be -raised. - -Now let's move on to ``mylf/lmi/scripts/mylf/cmd.py``: - -:: - - class Create(command.LmiCheckResult): - EXPECT = None - CALLABLE = mylf.create_directory - - class Delete(command.LmiCheckResult): - EXPECT = None - CALLABLE = mylf.delete_directory - -``LmiCheckResult`` is a special command that prints no useful -information. It allows us to check, whether the associated function -returns expected result and prints an error if not. Here we expect -``None``. Associated functions in this case throw an exception upon any -error which have the same effect. For more information please refer to -its -`documentation <http://pythonhosted.org/openlmi-scripts/command-classes.html#lmicheckresult>`_. - -Test it -~~~~~~~ - -:: - - lmi -h $HOST mylf create /root/some_directory - # try it for the second time (it will fail) - lmi -h $HOST mylf create /root/some_directory - # now let's delete it - lmi -h $HOST mylf delete /root/some_directory - # try it for the second time (it will fail) - lmi -h $HOST mylf delete /root/some_directory - -Summary -------- - -Now that the script is ready and tested, we may commit it, push it, do a -pull request (on -`github <https://help.github.com/articles/using-pull-requests>`_) and -host it on `PyPI <https://pypi.python.org/pypi>`_: - -:: - - python setup.py register - python setup.py sdist upload - -You can also download source tarball :download:`tarball <_static/mylf.tar.gz>`. - diff --git a/doc/usage.rst b/doc/usage.rst deleted file mode 100644 index 70e19ca..0000000 --- a/doc/usage.rst +++ /dev/null @@ -1,133 +0,0 @@ -`lmi` meta-command usage -======================== -``lmi`` meta-command is a command line utility build on top of client-side -libraries. Each library for particular set of providers can declare one or -more commands that will be registered with ``lmi`` meta-command and will be -available to user at command line. - -Running from command line -------------------------- -It can run single command given on command line like this: :: - - lmi -h ${hostname} service list --all - -Getting help ------------- -For detailed help run: :: - - lmi --help - -To get a list of available commands with short descriptions: :: - - lmi help - -For help on a particular registered command: :: - - lmi help service - -Running in interactive mode ---------------------------- -Or it can be run in interactive mode when command is omitted: :: - - lmi -h ${hostname} - lmi> help - ... - lmi> sw search django - ... - lmi> sw install python-django - ... - lmi> exit - -The most important command is ``help`` which shows you possible commands. -It also accepts arguments: :: - - lmi> help storage raid - -prints help for raid subcommand of storage command. The same behaviour is achieved -with: :: - - lmi> storage raid --help - -Built-in commands -~~~~~~~~~~~~~~~~~ -Interactive mode comes with few special commands. To get their list together -with help, type: :: - - lmi> : help - -They are prefixed with ``:`` and optional space. Currently only namespace nesting -commands are supported. Those are ``:cd``, ``:..`` and ``:pwd``. - -Command namespaces -~~~~~~~~~~~~~~~~~~ -Most end-point commands (also known as multiplexers) have one or more subcommands -assigned. Namespace of such multiplexer is comprised of these subcommand's names. -The root multiplexer (also known as top-level command) has namespace comprised of -registered commands. - -.. figure:: _static/imode-tree.svg - :align: center - :alt: Command namespaces. - :width: 600px - -Above figure depicts command tree. On the left side is a top-level command. It -has two registered commands (``Storage`` and ``Software``) that creates its -namespace. Each of them has their own sub-commands -- either another -multiplexer (``LVCmd``, ``Repository``, etc.) or an end-point command. - -Under each multiplexer or top-level command is corresponding prompt with path. -This prompt becomes active when ``:cd`` command is used to nest into particular -command's namespace. Such a namespace begins an active one. Just one namespace -can be active at a moment. Commands from non-active namespaces are not -available. For example if ``RAIDCmd`` is currently active -- current command -path is ``/lmi/storage/raid``, just ``list`` and ``delete`` commands are -available ( note that in reality ``RAIDCMD`` has a lot more subcommands). Thus -one can not write ``enable`` or ``disable`` which belongs to ``Repository`` -command's namespace. - -There is also something like global namespace containing static commands and -built-ins. Commands from global namespace are accessible from any active -namespace. - -Static commands -+++++++++++++++ - - +------+------------------------------------------------------------------+ - | EOF | Same as hitting ``^D``. If some nested into some subcommand's | - | | namespace, it will map to ``:cd ..`` and parent namespace will | - | | become active. If the top-level namespace is active, program | - | | will exit. | - +------+------------------------------------------------------------------+ - | exit | Exits immediately. It accepts optional exit code as an argument. | - +------+------------------------------------------------------------------+ - | help | Lists available commands. Accepts command path as an optional | - | | argument. | - +------+------------------------------------------------------------------+ - -Changing command namespaces ---------------------------- -Changing or nesting to some command namespace can be achieved with built-in -commands ``:cd`` and ``:..``. The latter is just a shortcut for ``:cd ..`` that -changes to parent namespace. ``:cd`` accepts a path to command. Whole command -path begins with ``/lmi`` prefix which denotes top-level command. Path -beginning with a ``'/'`` is an absolute path, which means it contains all -command names on a path from top-level command to target one. Other paths are -relative to current command. - -Here is a an example of changing command namespaces: :: - - lmi> :pwd - /lmi - lmi> :cd storage - >storage> :pwd - /lmi/storage - >storage> :cd lv - >>lv> :pwd - /lmi/storage/lv - >>lv> :cd ../raid - >>raid> :cd ../../sw/repo - >>repo> :.. - >sw> :cd /storage # /lmi prefix is optional for absolute paths - >storage :cd / - lmi> - diff --git a/lmi/__init__.py b/lmi/__init__.py deleted file mode 100644 index ca65877..0000000 --- a/lmi/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -__import__('pkg_resources').declare_namespace(__name__) diff --git a/lmi/scripts/__init__.py b/lmi/scripts/__init__.py deleted file mode 100644 index ca65877..0000000 --- a/lmi/scripts/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -__import__('pkg_resources').declare_namespace(__name__) diff --git a/lmi/scripts/_metacommand/__init__.py b/lmi/scripts/_metacommand/__init__.py deleted file mode 100644 index affea7b..0000000 --- a/lmi/scripts/_metacommand/__init__.py +++ /dev/null @@ -1,239 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Subpackage containing functionality of lmi meta-command. -""" - -import argparse -import logging -import sys - -from lmi.scripts import common -from lmi.scripts.common import errors -from lmi.scripts._metacommand import util -from lmi.scripts._metacommand import exit -from lmi.scripts._metacommand.help import Help -from lmi.scripts._metacommand.manager import CommandManager -from lmi.scripts._metacommand.interactive import Interactive -from lmi.scripts._metacommand.toplevel import TopLevelCommand -from lmi.scripts.common.command import LmiCommandMultiplexer, LmiBaseCommand -from lmi.scripts.common.configuration import Configuration -from lmi.scripts.common.session import Session -from lmi.shell import LMIUtil - -LOG = common.get_logger(__name__) - -# write errors to stderr until logging is configured -logging.getLogger('').addHandler(logging.StreamHandler()) - -class MetaCommand(object): - """ - Main application class. It instantiates configuration object, logging and - then it passes control to commands. - - Example usage: - - MetaCommand().run() - """ - - def __init__(self): - # allow exceptions in lmi shell - LMIUtil.lmi_set_use_exceptions(True) - # instance of CommandManager, created when first needed - self._command_manager = None - self.stdout = sys.stdout - self.stderr = sys.stderr - self.stdin = sys.stdin - # instance of Session, created when needed - self._session = None - # instance of Configuration, created in setup() - self.config = None - # dictionary of not yet processed options, it's created in setup() - self._options = None - self._active_command = None - - def _configure_logging(self): - """ - Setup logging. It expects Configuration object to be already - initialized. - - Logging can be tuned in various ways: - - * In configuration file with options: - * [Main] Verbosity - * [Log] OutputFile - * [Log] FileFormat - * [Log] ConsoleFormat - * [Log] ConsoleInfoFormat - * [Log] LogToConsole - * With command line options: - ``-v`` flags : - Each such flag increases logging level of what is logged - into console. This overrides `[Main] Verbosity` option. - ``-q`` : - Causes supression of any output made to stdout except for - error messages. This overrides ``[Main] Verbosity``. - option and ``-v`` flags. - ``--log-file`` : - Output file for logging messages. This overrides ``[Log] - OutputFile`` option. - - Implicitly only warnings and errors are logged to the standard error - stream without any tracebacks. - """ - util.setup_logging(self.config, self.stderr) - - @property - def command_manager(self): - """ - Return instance of ``CommandManager``. It's initialized when first - needed. - - :rtype: (``CommandManager``) - """ - if self._command_manager is None: - self._command_manager = CommandManager() - self._command_manager.add_command('help', Help) - return self._command_manager - - @property - def session(self): - """ - Return instance of Session. Instantiated when first needed. - - :rtype: (``Session``) - """ - if self._session is None: - if ( not self._options['--host'] - and not self._options['--hosts-file']): - self._options['--host'] = [util.get_default_hostname()] - LOG().info('No hosts given, using "%s".', - self._options['--host'][0]) - hostnames = [] - # credentials loaded from file - credentials = {} - def add_hosts(hosts, creds): - """ Update hostnames and credentials for new data. """ - hostnames.extend(hosts) - credentials.update(creds) - if self._options['--hosts-file']: - hosts_path = self._options['--hosts-file'] - try: - with open(hosts_path, 'r') as hosts_file: - add_hosts(*util.parse_hosts_file(hosts_file)) - except (OSError, IOError) as err: - LOG().critical('Could not read hosts file "%s": %s', - hosts_path, err) - sys.exit(1) - add_hosts(*util.get_hosts_credentials(self._options['--host'])) - if self._options['--user']: - credentials.update({ - # credentials in file has precedence over --user option - h : credentials.get(h, (self._options['--user'], '')) - for h in hostnames if h not in credentials - }) - self._session = Session(self, hostnames, credentials, - same_credentials=self._options['--same-credentials']) - return self._session - - @property - def active_command(self): - return self._active_command - @active_command.setter - def active_command(self, cmd): - if not isinstance(cmd, LmiBaseCommand): - raise TypeError("cmd must be an instance of LmiBaseCommand") - self._active_command = cmd - - def print_version(self): - """ Print version of this egg to stdout. """ - self.stdout.write("%s\n" % util.get_version()) - - def setup(self, options): - """ - Initialise global Configuration object and set up logging. - - :param options: (``dict``) Dictionary of options parsed from command - line by docopt. - """ - conf_kwargs = {} - if options['--config-file']: - conf_kwargs['user_config_file_path'] = options.pop('--config-file') - self.config = Configuration.get_instance(**conf_kwargs) - # two mutually exclusive options - if options['--trace'] or options['--notrace']: - self.config.trace = bool(options['--trace']) - if options.pop('--quiet', False): - self.config.verbosity = Configuration.OUTPUT_SILENT - elif options['-v'] and options['-v'] > 0: - self.config.verbosity = options['-v'] - if options.pop('--noverify', False): - self.config.verify_server_cert = False - self.config.log_file = options.pop('--log-file', None) - self._configure_logging() - del options['--trace'] - del options['--notrace'] - del options['-v'] - self.config.namespace = options.pop('--namespace', None) - self.config.human_friendly = options.pop('--human-friendly', None) - self.config.no_headings = options.pop('--no-headings', None) - self.config.lister_format = options.pop('--lister-format', None) - # unhandled options may be used later (for session creation), - # so let's save them - self._options = options - - def run(self, argv): - """ - Equivalent to the main program for the application. - - :param argv: (``list``) Input arguments and options. - Contains all arguments but the application name. - """ - retval = exit.EXIT_CODE_FAILURE - cmd = TopLevelCommand(self) - try: - retval = cmd.run(argv) - except Exception as exc: - if isinstance(exc, errors.LmiUnsatisfiedDependencies): - retval = exit.EXIT_CODE_UNSATISFIED_DEPENDENCIES - LOG().exception(str(exc)) - if isinstance(retval, bool) or not isinstance(retval, (int, long)): - return ( exit.EXIT_CODE_SUCCESS if bool(retval) or retval is None - else exit.EXIT_CODE_FAILURE) - - return retval - -def main(argv=sys.argv[1:]): - """ - Main entry point function. It just passes arguments to instantiated - ``MetaCommand``. - """ - return MetaCommand().run(argv) - diff --git a/lmi/scripts/_metacommand/cmdutil.py b/lmi/scripts/_metacommand/cmdutil.py deleted file mode 100644 index 986e835..0000000 --- a/lmi/scripts/_metacommand/cmdutil.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2013, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Utility functions for command inspection. -""" -from lmi.scripts.common import errors -from lmi.scripts.common.command import LmiBaseCommand -from lmi.scripts.common.command import LmiCommandMultiplexer - -def get_subcommand_names(command): - """ - :param command: Either a multiplexer command or top-level one. - :returns: Names of children commands. - :rtype: list - """ - if not isinstance(command, LmiBaseCommand): - raise TypeError("command must be an instance of LmiBaseCommand") - if isinstance(command, LmiCommandMultiplexer): - return command.child_commands().keys() - if command.parent is None: # top level command - return command.app.command_manager.command_names - raise ValueError("command must be either multiplexer or top-level command") - -def get_subcommand_factory(command, name): - """ - :param command: Either a multiplexer command or top-level one. - :returns: Callable returning an instance of - :py:class:`~lmi.scripts.common.command.multiplexer.LmiCommandMultiplexer` - :rtype: callable - """ - if isinstance(command, LmiCommandMultiplexer): - try: - return command.child_commands()[name] - except KeyError: - cmd_path = command.cmd_name_parts - cmd_path.append(name) - raise errors.LmiCommandNotFound(" ".join(cmd_path)) - if command.parent is None: # top level command - return command.app.command_manager.find_command(name) - raise ValueError("command must be either multiplexer or top-level command") - diff --git a/lmi/scripts/_metacommand/exit.py b/lmi/scripts/_metacommand/exit.py deleted file mode 100644 index e876073..0000000 --- a/lmi/scripts/_metacommand/exit.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module containing help command. -""" - -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import LmiEndPointCommand -from lmi.scripts.common import errors - -LOG = get_logger(__name__) - -EXIT_CODE_SUCCESS = 0 -EXIT_CODE_FAILURE = 1 -EXIT_CODE_KEYBOARD_INTERRUPT = 2 -EXIT_CODE_COMMAND_NOT_FOUND = 3 -EXIT_CODE_INVALID_SYNTAX = 4 -EXIT_CODE_UNSATISFIED_DEPENDENCIES = 5 - -def _execute_exit(exit_code): - """ Associated function with ``Exit`` command. """ - raise errors.LmiTerminate(exit_code) - -class Exit(LmiEndPointCommand): - """ - Terminate the shell. - - Usage: %(cmd)s [<exit_code>] - """ - CALLABLE = _execute_exit - OWN_USAGE = True - - def verify_options(self, options): - code = options['<exit_code>'] - if code is not None: - try: - int(code) - except ValueError: - raise errors.LmiInvalidOptions( - "<exit_code> must be an integer not \"%s\"" % code) - - def transform_options(self, options): - code = options.get('<exit_code>', None) - if code is None: - code = EXIT_CODE_SUCCESS - options['<exit_code>'] = int(code) - diff --git a/lmi/scripts/_metacommand/help.py b/lmi/scripts/_metacommand/help.py deleted file mode 100644 index b88f6b5..0000000 --- a/lmi/scripts/_metacommand/help.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# - -""" -Module containing help command. -""" - -from lmi.scripts.common import errors -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import LmiEndPointCommand -from lmi.scripts._metacommand import cmdutil -from lmi.scripts._metacommand import exit - -LOG = get_logger(__name__) - -class Help(LmiEndPointCommand): - """ - Print the list of supported commands with short description. - If a subcommand is given, its detailed help will be printed. - - Usage: %(cmd)s [<subcommand>...] - """ - OWN_USAGE = True - - def execute(self, subcommand): - mgr = self.app.command_manager - node = self.app.active_command - toplevel = self - while toplevel.parent is not None: - toplevel = toplevel.parent - - if node or subcommand: - # Help for some subcommand will be printed. - if node is None: - node = toplevel - if subcommand: - index = 0 - try: - while index < len(subcommand) and not node.is_end_point(): - while node.is_selector(): - cmd_factory, _ = node.select_cmds().next() - node = cmd_factory(self.app, node.cmd_name, - node.parent) - # selector may return either multiplexer or end-point - if node.is_end_point(): - break - cmd_factory = cmdutil.get_subcommand_factory(node, - subcommand[index]) - node = cmd_factory(self.app, subcommand[index], node) - index += 1 - except errors.LmiCommandNotFound as err: - LOG().error(str(err)) - if node is not toplevel: - self.app.stdout.write(node.get_usage(True)) - if node is self.app.active_command: - # show additional information only when no command given - self.app.stdout.write('\nTo get help for built-in commands,' - ' type:\n :help\n') - return exit.EXIT_CODE_SUCCESS - - # let's print the summary of available commands - self.app.stdout.write("Commands:\n") - max_cmd_len = max(len(n) for n in mgr) - cmd_line = " %%-%ds - %%s\n" % max_cmd_len - for cmd in sorted(mgr): - self.app.stdout.write(cmd_line - % (cmd, mgr[cmd].get_description() - .strip().split("\n", 1)[0])) - self.app.stdout.write( - "\nFor more informations about particular command type:\n" - " help <command>\n") - - return exit.EXIT_CODE_SUCCESS - diff --git a/lmi/scripts/_metacommand/interactive.py b/lmi/scripts/_metacommand/interactive.py deleted file mode 100644 index 349d622..0000000 --- a/lmi/scripts/_metacommand/interactive.py +++ /dev/null @@ -1,475 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module with interactive application. -""" - -import cmd -import docopt -import itertools -import os -import readline -import shlex - -from lmi.scripts.common import errors -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import LmiBaseCommand, LmiCommandMultiplexer -from lmi.scripts._metacommand import cmdutil -from lmi.scripts._metacommand import exit - -LOG = get_logger(__name__) - -BUILT_INS_USAGE = """ -Built-ins. - -Usage: - : cd [<path>] - : .. - : pwd - : (help | -h | --help) - -Description: - cd Nest to a subcommand. Accepts a sequence of subcommands separated - with "/". If "/" is given, top-level command becomes the active one. - Other supported special symbols are ".." and ".". - .. Make parent command the active one. Same as issuing ":cd ..". - pwd Print current command path. - help Show this help. -""" - -BUILT_INS = ('..', 'cd', 'pwd', 'help') - -class LmiBuiltInError(errors.LmiError): - """ General exception concerning the use of built-in commands. """ - pass - -class LmiCanNotNest(LmiBuiltInError): - """ Raised upon invalid use of *cd* built-in command. """ - pass - -class Interactive(cmd.Cmd): - """ - Launched by the main application. It enters *shell* mode. Allows to launch - set of commands interactively on all given hosts. Session object stays the - same for the whole life of interactive mode. - - :param top_level_cmd: Top-level command. - :type top_level_cmd: :py:class:`.toplevel.TopLevelCommand` - """ - - def __init__(self, top_level_cmd): - self._top_level_cmd = top_level_cmd - cmd.Cmd.__init__(self, - stdin=top_level_cmd.app.stdin, stdout=top_level_cmd.app.stdout) - self._last_exit_code = exit.EXIT_CODE_SUCCESS - self.doc_header = 'Static commands' - self.app.active_command = top_level_cmd - self.load_history() - - # ************************************************************************* - # Properties - # ************************************************************************* - @property - def app(self): - """ :returns: Application object. """ - return self._top_level_cmd.app - - @property - def command_manager(self): - """ :returns: An instance of :py:class:`~.manager.CommandManager`. """ - return self.app.command_manager - - @property - def on_top_level_node(self): - """ - :returns: Whether the current node is a top-level one. In other words - we're not nested in any subcommand namespace. - :rtype: boolean - """ - return self._top_level_cmd is self.app.active_command - - @property - def prompt(self): - """ - :returns: Dynamically computed shell prompt. - :rtype: string - """ - if self.app.stdin.isatty(): - parents_num = 0 - node = self.app.active_command - while node.parent is not None: - parents_num += 1 - node = node.parent - return '>'*parents_num + self.app.active_command.cmd_name + '> ' - return '' - - # ************************************************************************* - # Private methods - # ************************************************************************* - def _change_to_node(self, path): - """ - Handles any command path. It constructs an object of command - corresponding to given path. Path can be absolute or relative. - - :param str path: Path to command. Looks similar to unix file path. - Command names are separated with ``'/'``. - :returns: Multiplexer command corresponding to path. - :rtype: :py:class:`~lmi.scripts.common.command.multiplexer.LmiCommandMultiplexer` - """ - cur_node = self.app.active_command - if path.startswith('/'): - if path.startswith('/lmi'): - path = path[4:] - else: - path = path[1:] - cur_node = self._top_level_cmd - - cmd_chain = os.path.normpath(path).split('/') - for subcmd in cmd_chain: - if subcmd == '..': - cur_node = ( cur_node.parent - if cur_node.parent is not None else cur_node) - elif subcmd and subcmd != '.': - if not subcmd in cmdutil.get_subcommand_names(cur_node): - raise LmiCanNotNest('No such subcommand "%s".' % - "/".join(cmd_chain)) - cmd_cls = cmdutil.get_subcommand_factory(cur_node, subcmd) - if not issubclass(cmd_cls, LmiCommandMultiplexer): - raise LmiCanNotNest('Can not nest to subcommand "%s" which' - " is not a multiplexer." % "/".join(cmd_chain)) - if not cmd_cls.has_own_usage(): - raise LmiCanNotNest('Can not nest to subcommand "%s" which' - ' lacks any help message.' % "/".join(cmd_chain)) - cur_node = cmd_cls(self.app, subcmd, cur_node) - self.app.active_command = cur_node - return exit.EXIT_CODE_SUCCESS - - def _do_built_in_cmd(self, args): - """ - Execute built-in command. - - :param list args: Command arguments including command name. - """ - options = docopt.docopt(BUILT_INS_USAGE, args, help=False) - if options['cd']: - path = '.' if not options.get('<path>', None) else options['<path>'] - return self._change_to_node(path) - elif options['..']: - return self._change_to_node('..') - elif options['pwd']: - self.app.stdout.write("/" - + "/".join(self.app.active_command.get_cmd_name_parts( - all_parts=True)) + "\n") - elif options['help'] or options['-h'] or options['--help']: - self.app.stdout.write(BUILT_INS_USAGE[1:]) - return exit.EXIT_CODE_SUCCESS - - def _execute_line_parts(self, line_parts): - """ - Try to execute given line. This method can throw various exceptions - that needs to be handled by a caller, otherwise interactive mode will - be terminated. - - :param list line_parts: Parsed command line arguments. - :returns: Command's exit code. - """ - if line_parts[0][0] == ':': - if len(line_parts[0]) > 1: - line_parts[0] = line_parts[0][1:] - else: - line_parts = line_parts[1:] - return self._do_built_in_cmd(line_parts) - - else: - # let's try to run registered subcommand - retval = self.run_subcommand(line_parts) - if isinstance(retval, bool) or not isinstance(retval, (int, long)): - retval = ( exit.EXIT_CODE_SUCCESS - if bool(retval) or retval is None - else exit.EXIT_CODE_FAILURE) - return retval - - # ************************************************************************* - # Public methods - # ************************************************************************* - def complete(self, text, state): - """ - Overrides parent's method so that registered commands can be also - completed. - """ - if state == 0: - import readline - origline = readline.get_line_buffer() - line = origline.lstrip() - stripped = len(origline) - len(line) - begidx = readline.get_begidx() - stripped - endidx = readline.get_endidx() - stripped - command, _, _ = self.parseline(line) - compfunc = self.completedefault - if command and hasattr(self, 'complete_' + command): - compfunc = getattr(self, 'complete_' + command) - self.completion_matches = compfunc(text, line, begidx, endidx) - try: - return self.completion_matches[state] - except IndexError: - return None - - def completedefault(self, text, line, *_args, **_kwargs): - """ - Tab-completion for commands known to the command manager and subcommands - in current command namespace. Does not handle command options. - """ - if line.startswith(':'): - commands = BUILT_INS - else: - commands = set(self.completenames(text)) - commands.update(set(cmdutil.get_subcommand_names(self.app.active_command))) - completions = sorted(n for n in commands - if not text or n.startswith(text)) - return completions - - def clear_history(self): - """ Clear readline history. """ - readline.clear_history() - - def default(self, line): - """ - This is run, when line contains unknown command to ``cmd.Cmd``. It - expects us to handle it if we know it, or print an error. - - :param str line: Line given to our shell. - """ - try: - line_parts = shlex.split(line) - except ValueError as err: - LOG().error(str(err)) - return exit.EXIT_CODE_INVALID_SYNTAX - - try: - self._execute_line_parts(line_parts) - - except docopt.DocoptExit as err: - # command found, but options given to it do not comply with its - # usage string - if '--help' in line_parts: - return self.do_help(" ".join( - line_parts[:line_parts.index('--help')])) - LOG().warn("Wrong options given: %s", line.strip()) - self.stdout.write(str(err)) - if ( line_parts[0] in cmdutil.get_subcommand_names( - self.app.active_command) - and cmdutil.get_subcommand_factory(self.app.active_command, - line_parts[0]).is_end_point()): - self.stdout.write("\n\nTo see a full usage string, type:\n" - " help %s\n" % line_parts[0]) - else: - self.stdout.write("\n\nTo see a list of available commands," - " type:\n help\n") - self._last_exit_code = exit.EXIT_CODE_FAILURE - - except errors.LmiCommandNotFound as err: - LOG().error(str(err)) - self._last_exit_code = exit.EXIT_CODE_COMMAND_NOT_FOUND - - except errors.LmiUnsatisfiedDependencies as err: - LOG().error(str(err)) - self._last_exit_code = exit.EXIT_CODE_UNSATISFIED_DEPENDENCIES - - except errors.LmiError as err: - LOG().error(str(err)) - self._last_exit_code = exit.EXIT_CODE_FAILURE - - except KeyboardInterrupt as err: - LOG().debug('%s: %s', err.__class__.__name__, str(err)) - self._last_exit_code = exit.EXIT_CODE_KEYBOARD_INTERRUPT - - return self._last_exit_code - - def do_EOF(self, _arg): #pylint: disable=C0103,R0201 - """ - Exit on End-Of-File if we are on top-level command. Otherwise change - to parent command. - """ - if self.app.stdin.isatty(): - self.app.stdout.write('\n') - if self.app.stdin.isatty() and not self.on_top_level_node: - self._change_to_node('..') - else: - raise errors.LmiTerminate(self._last_exit_code) - - def do_exit(self, arg): - """ - This makes the exit command work in both (non-)interactive modes. - """ - command = ["exit"] - if arg: - command.append(arg) - return self.run_subcommand(command) - - def do_help(self, arg): - """ Handle help subcommand. """ - if arg: - try: - arg_parts = shlex.split(arg) - except ValueError as err: - LOG().error(str(err)) - return exit.EXIT_CODE_INVALID_SYNTAX - - method_name = '_'.join( - itertools.chain(['do'], - itertools.takewhile(lambda x: not x.startswith('-'), - arg_parts))) - if hasattr(self, method_name): - return cmd.Cmd.do_help(self, arg) - else: - arg_parts = [] - - if ( self.on_top_level_node - and ( not arg - or ( len(arg_parts) == 1 - and arg not in self.app.command_manager.command_names))): - if not self.completenames(arg): - LOG().error(str(errors.LmiCommandNotFound(arg))) - cmd.Cmd.do_help(self, '') - else: - cmd.Cmd.do_help(self, arg) - cmd_names = set(self.command_manager) - cmd_names.difference_update(set(self.completenames(''))) - self.print_topics( - "Application commands (type help <topic>):", - sorted(cmd_names), 15, 80) - self.print_topics( - "Built-in commands (type :help):", - [':'+bi for bi in BUILT_INS], 15, 80) - return exit.EXIT_CODE_SUCCESS - - return self.run_subcommand(['help'] + arg_parts) - - def emptyline(self): #pylint: disable=R0201 - """ Do nothing for empty line. """ - pass - - def help_exit(self): - """ Provide help for exit command. """ - cur_node = self.app.active_command - # temporarily change to top level command where help cmd is registered - self.app.active_command = self._top_level_cmd - try: - return self.run_subcommand(["help", "exit"]) - finally: - self.app.active_command = cur_node - - def help_help(self): - """ - Use the command manager to get instructions for "help". - """ - cur_node = self.app.active_command - # temporarily change to top level command where help cmd is registered - self.app.active_command = self._top_level_cmd - try: - return self.run_subcommand(["help", "help"]) - finally: - self.app.active_command = cur_node - - def load_history(self): - """ - Load a readline history file. - """ - if ( self.app.config.history_max_length == 0 - or not os.path.exists(self.app.config.history_file)): - return - LOG().debug('Reading history file "%s"', self.app.config.history_file) - try: - readline.read_history_file(self.app.config.history_file) - if ( self.app.config.history_max_length > 0 - and readline.get_current_history_length() - > self.app.config.history_max_length): - readline.set_history_length(self.app.config.history_max_length) - readline.write_history_file(self.app.config.history_file) - readline.read_history_file(self.app.config.history_file) - except (IOError, OSError) as err: - LOG().warn('Failed to read history file "%s".', - self.app.config.history_file, exc_info=err) - - def postcmd(self, stop, _line): - """ - This is called after the ``do_*`` command to postprocess its result and - decide whether to stop the shell. We want to stop only when - :py:class:`lmi.scripts.common.errors.LmiError` is raised. This - exception is catched upwards in call chain. - - :returns: Whether to stop the shell. - :rtype: bool - """ - return False - - def run_subcommand(self, args): - """ - Run a subcommand given as a first item of ``args``. It must be one of - commands registered in manager. Returns the return value of invoked - command. - - :param list args: List of commands. - """ - if not isinstance(args, (list, tuple)): - raise TypeError("args must be a list") - if len(args) < 1: - raise ValueError("args must not be empty") - try: - cmd_factory = cmdutil.get_subcommand_factory( - self.app.active_command, args[0]) - parent = self.app.active_command - except errors.LmiCommandNotFound: - # When nested into a subcommand, let it handle the args if known. - # If not known, try one of static commands. - if not self.on_top_level_node and hasattr(self, 'do_' + args[0]): - cmd_factory = self.app.command_manager.find_command(args[0]) - parent = self._top_level_cmd - else: - raise - cmd_inst = cmd_factory(self.app, args[0], parent) - return cmd_inst.run(args[1:]) - - def save_history(self): - """ - Saves current history of commands into the history file. If the length - of history exceeds a maximum history file length, the history will be - truncated. - """ - if self.app.config.history_max_length != 0: - LOG().debug('Writing history file "%s"', - self.app.config.history_file) - if self.app.config.history_max_length > 0: - readline.set_history_length(self.app.config.history_max_length) - try: - readline.write_history_file(self.app.config.history_file) - except (IOError, OSError), err: - LOG().warn('Failed to write history file "%s".', - self.app.config.history_file, exc_info=err) diff --git a/lmi/scripts/_metacommand/manager.py b/lmi/scripts/_metacommand/manager.py deleted file mode 100644 index 773979b..0000000 --- a/lmi/scripts/_metacommand/manager.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Manager module for direct subcommands of lmi metacommand. Most of them are -loaded from entry_points of installed python eggs. -""" - -import pkg_resources - -from lmi.scripts.common import Configuration -from lmi.scripts.common import errors -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import base -from lmi.scripts.common.command import util - -LOG = get_logger(__name__) - -class _CustomCommandWrapper(object): - """ - Provide an interface mocking an entry_point object for custom commands - added by lmi metacommand application. - - :param name: (``str``) Name of command. - :param cmd_class: (``LmiBaseCommand``) Factory for custom commands. - """ - - def __init__(self, name, cmd_class): - if not isinstance(name, basestring): - raise TypeError("name must be a string") - if not issubclass(cmd_class, base.LmiBaseCommand): - raise TypeError("cmd_class must be a LmiBaseCommand") - self._name = name - self._cmd_class = cmd_class - - @property - def name(self): - """ Return command name. """ - return self._name - - def load(self): - """ Return command class. """ - return self._cmd_class - -class CommandManager(object): - """ - Manager of direct subcommands of lmi metacommand. It manages commands - registered with entry_points under particular namespace installed by - python eggs. Custom commands may also be added. - - :param namespace: (``str``) Namespace, where commands are registered. - For example ``lmi.scripts.cmd``. - """ - - def __init__(self, namespace=None): - if namespace is not None and not isinstance(namespace, basestring): - raise TypeError("namespace must be a string") - if namespace is None: - namespace = Configuration.get_instance().get_safe( - "Main", "CommandNamespace") - self._namespace = namespace - self._commands = {} - self._load_commands() - - @property - def command_names(self): - """ Returns list of command names. """ - return self._commands.keys() - - def __len__(self): - return len(self._commands) - - def __iter__(self): - """ Yields command names. """ - return iter(self._commands) - - def __getitem__(self, cmd_name): - """ Gets command factory for name. """ - return self.find_command(cmd_name) - - def _load_commands(self): - """ Loads commands from entry points under provided namespace. """ - def _add_entry_point(epoint): - """ - Convenience function taking an entry point, making some name - checks and adding it to registered commands. - """ - if not util.RE_COMMAND_NAME.match(epoint.name): - LOG().error('Invalid command name: %s, ignoring.', epoint.name) - return - if epoint.name in self._commands: - LOG().warn('Command "%s" already registered, ignoring.', - epoint.name) - else: - LOG().debug('Found registered command "%s".', epoint.name) - self._commands[epoint.name] = epoint - - for entry_point in pkg_resources.iter_entry_points(self._namespace): - if isinstance(entry_point, dict): - for epoint in entry_point.values(): - _add_entry_point(epoint) - else: - _add_entry_point(entry_point) - - - def add_command(self, name, cmd_class): - """ - Registers custom command. May be used for example for *help* command. - - :param name: (``str``) Name of command. - :param cmd_class: (``LmiBaseCommand``) Factory for commands. - """ - if not isinstance(name, basestring): - raise TypeError("name must be a string") - if not issubclass(cmd_class, base.LmiBaseCommand): - raise TypeError("cmd_class must be a LmiBaseCommand") - if not util.RE_COMMAND_NAME.match(name): - raise errors.LmiCommandInvalidName( - cmd_class.__module__, cmd_class.__class__.__name__, name) - if name in self._commands: - LOG().warn('Command "%s" already managed, overwriting with "%s:%s".', - name, cmd_class.__module__, cmd_class.__name__) - self._commands[name] = _CustomCommandWrapper(name, cmd_class) - - def find_command(self, cmd_name): - """ - Loads a command associated with given name and returns it. - - :param cmd_name: (``str``) Name of command to load. - :rtype: (``LmiBaseCommand``) Factory for commands. - """ - try: - return self._commands[cmd_name].load() - except KeyError: - raise errors.LmiCommandNotFound(cmd_name) - except ImportError as err: - LOG().debug('Failed to import command "%s".', cmd_name, exc_info=err) - raise errors.LmiCommandImportError( - cmd_name, self._commands[cmd_name].module_name, err) - - def reload_commands(self, keep_custom=True): - """ - Flushes all commands and reloads entry points. - - :param keep_custom: (``bool``) Custom commands -- not loaded from - entry points -- are preserved. - """ - if keep_custom: - keep = { n: c for n, c in self._commands.items() - if isinstance(c, _CustomCommandWrapper)} - else: - keep = {} - self._commands = {} - self._load_commands() - self._commands.update(keep) - diff --git a/lmi/scripts/_metacommand/toplevel.py b/lmi/scripts/_metacommand/toplevel.py deleted file mode 100644 index 1e654a3..0000000 --- a/lmi/scripts/_metacommand/toplevel.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module defining the root command (``lmi`` binary). -""" - -USAGE_STRING = \ -""" -OpenLMI command line interface for CIM providers. It's functionality is -composed of registered subcommands, operating on top of simple libraries, -interfacing with particular OpenLMI profile providers. -Works also in interactive mode which is entered, when <command> argument is -omitted. - -Usage: - %(cmd)s [options] [(--trace | --notrace)] [-v]... [-h <host>]... - <command> [<args> ...] - %(cmd)s [options] [(--trace | --notrace)] [-v]... [-h <host>]... - %(cmd)s (--help | --version) - -Options: - -c --config-file <config> Path to a user configuration file. Options - specified here override any settings of global - configuration file. - -h --host <host> Hostname of target system. - --hosts-file <hosts> Path to a file containing target hostnames. - Each hostname must be listed on a single line. - --user <user> Username used in connection to any target host. - --same-credentials Use the first credentials given for all hosts. - -n --noverify Do not verify cimom's ssl certificate. - -v Increase verbosity of output. - --trace Show tracebacks on errors. - --notrace Suppress tracebacks for exceptions. - -q --quiet Supress output except for errors. - --log-file <log_file> Output file for logging messages. - --namespace <namespace> Default CIM namespace to use. - -N --no-headings Don't print table headings. - -H --human-friendly Print large values in human friendly units (i.e. - MB, GB, TB etc.) - -L --lister-format (table | csv) - Print output of lister commands in CSV or table - format. CSV format is more suitable for machine - processing. Defaults to table. - --help Show this text and quit. - --version Print version of '%(cmd)s' in use and quit. - -Handling hosts: - If no --host or --hosts-file given the "localhost" is tried. When running - under root with Pegasus CIMOM, this results in a connection over unix - socket (without the need for credentials). - - Hosts may contain embedded credentials e.g.: - http://user:passwd@hostname:5988 - Avoid supplying them on command line though since arguments are visible in - process table. Use --hosts-file option instead. -""" - -import docopt - -from lmi.scripts._metacommand import exit -from lmi.scripts._metacommand import util -from lmi.scripts._metacommand import Interactive -from lmi.scripts.common import get_logger -from lmi.scripts.common import errors -from lmi.scripts.common.command import base - -LOG = get_logger(__name__) - -class TopLevelCommand(base.LmiBaseCommand): - """ - Top level (instance, without any parent) command handling application - parameters and passing work to registered subcommands. - """ - - @classmethod - def has_own_usage(cls): - return True - - @classmethod - def child_commands(cls): - return [] - - @classmethod - def is_end_point(cls): - return False - - def __init__(self, app, cmd_name='lmi'): - base.LmiBaseCommand.__init__(self, app, cmd_name) - - def get_usage(self, proper=False): - return USAGE_STRING[1:] % { 'cmd' : " ".join(self.cmd_name_parts) } - - def run_subcommand(self, cmd_name, args): - """ - Finds a command factory, instantiates it and passes the control. - """ - cmd_factory = self.app.command_manager[cmd_name] - cmd = cmd_factory(self.app, cmd_name, parent=self) - return cmd.run(args) - - def start_interactive_mode(self): - """ Run the command line loop of interactive application. """ - self.app.command_manager.add_command("exit", exit.Exit) - iapp = Interactive(self) - while True: - try: - ret = iapp.cmdloop() - break - except errors.LmiTerminate as err: - ret = err.args[0] - break - except KeyboardInterrupt as err: - LOG().debug('%s: %s', err.__class__.__name__, str(err)) - self.app.stdout.write('\n') - iapp.save_history() - return ret - - def run(self, args): - """ - Handle program arguments, set up the application and call - a subcommand or enter interactive mode. Return exit code. - - :param args: (``list``) Arguments without the binary name. - """ - if not isinstance(args, (tuple, list)): - raise TypeError("args must be a list") - try: - options = docopt.docopt(self.get_usage(), args, - version=util.get_version(), help=False, options_first=True) - except docopt.DocoptLanguageError as exc: - self.app.stderr.write("%s\n" % str(exc)) - return exit.EXIT_CODE_FAILURE - if options.pop('--help', False): - self.app.stdout.write(self.get_usage()) - self.app.stdout.write("\nCommands:\n") - self.app.stdout.write(" %s\n" % " ".join( - n for n in sorted(self.app.command_manager))) - return exit.EXIT_CODE_SUCCESS - if options.pop('--version', False): - self.app.print_version() - return exit.EXIT_CODE_SUCCESS - self.app.setup(options) - if options['<command>'] is None: - return self.start_interactive_mode() - - try: - LOG().debug('Running command "%s".', options['<command>']) - return self.run_subcommand(options['<command>'], options['<args>']) - except docopt.DocoptExit as err: - if '--help' in args: - cmd_args = options['<args>'] - cmd_args = cmd_args[:cmd_args.index('--help')] - return self.run_subcommand( - 'help', [options['<command>']] + cmd_args) - raise - - diff --git a/lmi/scripts/_metacommand/util.py b/lmi/scripts/_metacommand/util.py deleted file mode 100644 index 15686a3..0000000 --- a/lmi/scripts/_metacommand/util.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Meta-command utility module. -""" - -import logging -import logging.config -import os -import pkg_resources -import platform -import re -import socket -import sys -import urlparse - -from lmi.scripts.common import Configuration -from lmi.scripts.common import get_logger -from lmi.scripts.common import lmi_logging - -PYTHON_EGG_NAME = "openlmi-scripts" -#: Service name identifying tcp port used to connect to CIMOM. -DEFAULT_BROKER_SERVICE_NAME = 'wbem-https' - -RE_NETLOC = re.compile(r'^((?P<username>[^:@]+)(:(?P<password>[^@]+))?@)?' - r'(?P<hostname>[^:]+)(:(?P<port>\d+))?$') - -VERBOSITY_2_LOG_LEVEL = { - Configuration.OUTPUT_SILENT : logging.ERROR, - Configuration.OUTPUT_WARNING : logging.WARNING, - Configuration.OUTPUT_INFO : logging.INFO, - Configuration.OUTPUT_DEBUG : logging.DEBUG, -} - -DEFAULT_LOGGING_CONFIG = { - 'version' : 1, - 'disable_existing_loggers': True, - 'formatters' : { - 'console' : { - "()" : "lmi.scripts.common.lmi_logging.LevelDispatchingFormatter", - "formatters" : { - logging.INFO : - Configuration.default_options()['ConsoleInfoFormat'] - }, - 'default': Configuration.default_options()['ConsoleFormat'], - 'datefmt' : '%Y-%m-%d %H:%M:%S' - }, - 'file' : { - 'format' : Configuration.default_options()['FileFormat'], - 'datefmt' : '%Y-%m-%d %H:%M:%S' - } - }, - 'handlers' : { - 'console': { - 'class' : "logging.StreamHandler", - 'level' : logging.ERROR, - 'formatter': 'console', - }, - 'console_shell': { - 'class' : "logging.StreamHandler", - 'level' : logging.CRITICAL, - 'formatter': 'console', - }, - 'file' : { - 'class' : "logging.FileHandler", - 'level' : Configuration.default_options()['Level'].upper(), - 'formatter': 'file', - } - }, - 'loggers' : { - 'lmi.shell' : { - 'handlers' : ['console_shell'], - 'level' : logging.DEBUG, - 'propagate' : False - } - }, - 'root' : { - 'level' : logging.DEBUG, - 'handlers' : ['console'] - } -} - -LOG = get_logger(__name__) - -def setup_logging(app_config, stderr=sys.stderr): - """ - Setup logging to console and optionally to the file. - - :param app_config: (``lmi.scripts.common.Configuration``) - Configuration object. - :param stderr: (``file``) Output stream, where console handler should - dispatch logging messages. - """ - cfg = DEFAULT_LOGGING_CONFIG.copy() - - # Set up logging to a file - if app_config.log_file: - cfg['handlers']['file']['filename'] = app_config.log_file - cfg['formatters']['file']['format'] = app_config.get_safe( - 'Log', 'FileFormat', raw=True) - try: - cfg['handlers']['file']['level'] = \ - getattr(logging, app_config.logging_level.upper()) - except KeyError: - LOG().error('Unsupported logging level: "%s".', - app_config.logging_level) - cfg['root']['handlers'].append('file') - cfg['loggers']['lmi.shell']['handlers'].append('file') - else: - del cfg['formatters']['file'] - del cfg['handlers']['file'] - - if app_config.get_safe('Log', "LogToConsole", bool): - # Set up logging to console - if stderr is not sys.stderr: - cfg['handlers']['console']['stream'] = stderr - cfg['handlers']['console']['level'] = \ - VERBOSITY_2_LOG_LEVEL[app_config.verbosity] - - # make the verbosity of lmi shell one level less - lmi_shell_verbosity = app_config.verbosity - if ( app_config.verbosity < app_config.OUTPUT_DEBUG - and app_config.verbosity > app_config.OUTPUT_SILENT): - lmi_shell_verbosity -= 1 - cfg['handlers']['console_shell']['level'] = \ - VERBOSITY_2_LOG_LEVEL[lmi_shell_verbosity] - - # use ConsoleInfoFormat for INFO and less severe levels - cfg['formatters']['console']['formatters'] = { - logging.INFO : - app_config.get_safe('Log', 'ConsoleInfoFormat', raw=True) - } - # use ConsoleFormat for any other level - cfg['formatters']['console']['default'] = app_config.get_safe( - 'Log', 'ConsoleFormat', raw=True) - else: - cfg['root']['handlers'].remove('console') - cfg['loggers']['lmi.shell']['handlers'].remove('console_shell') - del cfg['handlers']['console'] - del cfg['handlers']['console_shell'] - - use_colors = platform.system() != 'Windows' and stderr.isatty() - lmi_logging.setup_logger(use_colors = use_colors) - logging.config.dictConfig(cfg) - -def get_version(egg_name=PYTHON_EGG_NAME): - """ - Gets version string of any python egg. Defaults to the egg of current - application. - """ - return pkg_resources.get_distribution(egg_name).version - -def get_hosts_credentials(hostnames): - """ - Parse list of hostnames, get credentials out of them and return - ``(hostnames, creds)``, where ``hostnames`` is a list of ``hostnames`` - with credentials removed and ``creds`` is a dictionary with a pair - ``(username, password)`` for every hostname, that supplied it. - - :param hostnames: (``list``) List of hostnames with optional credentials. - For example: ``http://root:password@hostname:5988``. - """ - if not hasattr(hostnames, '__iter__'): - raise TypeError("hostnames must be a list of hosts") - new_hostnames = [] - credentials = {} - for hostname in hostnames: - parsed = urlparse.urlparse(hostname) - if not parsed.netloc and parsed.path: - # got something like [user[:pass]@]hostname[:port] (no scheme) - match = RE_NETLOC.match(hostname) - if match: - hostname = match.group('hostname') - if match.group('port'): - hostname += ':' + match.group('port') - if match.group('username') or match.group('password'): - credentials[hostname] = ( - match.group('username'), match.group('password')) - elif parsed.username or parsed.password: - hostname = parsed.scheme - if parsed.scheme: - hostname += "://" - hostname += parsed.hostname - if parsed.port: - hostname += ":" + str(parsed.port) - hostname += parsed.path - credentials[hostname] = (parsed.username, parsed.password) - new_hostnames.append(hostname) - return (new_hostnames, credentials) - -def parse_hosts_file(hosts_file): - """ - Parse file with hostnames to connect to. Return list of parsed hostnames. - - :param hosts_file: (``file``) File object openned for read. - It containes hostnames. Each hostname occupies single line. - :rtype: (``tuple``) A pair of ``(hosts, creds)``, where ``hosts`` is a list - of string with hostnames and ``creds`` is a dictionary mapping - ``(username, password)`` to each hostname if supplied. - """ - hostnames = [] - for line in hosts_file.readlines(): - hostnames.append(line.strip()) - return get_hosts_credentials(hostnames) - -def get_default_hostname(port=None): - """ - Choose default hostname to connect to. If logged as root, this will default - to localhost, which results in unix socket being used for connection. - Otherwise use full qualified domain name and hostname will be tried in this - order. If they are not address-resolvable, '127.0.0.1' is returned. - - This function shall be used only if no uri is specified on command line. - - :param port: Port of desired service running on host (CIMOM broker). - This defaults to :py:attr:`DEFAULT_BROKER_SERVICE_NAME` - :type port: string or int - """ - if port is None: - port = DEFAULT_BROKER_SERVICE_NAME - # Functions used to get hostname. first resolvable result will be used. - name_getters = [] - if os.getuid() != 0: - # Use non-'localhost' name only if we'are not logged in as root - # for it prevents the use of unix socket. - name_getters.extend([socket.getfqdn, socket.gethostname]) - name_getters.append(lambda: 'localhost') - for name_func in name_getters: - try: - hostname = name_func() - socket.getaddrinfo(hostname, port) - break - except socket.gaierror: - pass - else: - hostname = '127.0.0.1' - return hostname diff --git a/lmi/scripts/common/__init__.py b/lmi/scripts/common/__init__.py deleted file mode 100644 index cda4266..0000000 --- a/lmi/scripts/common/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Package with client-side python modules and command line utilities. -""" - -import logging - -from lmi.shell import LMINamespace -from lmi.shell import LMIExceptions -from lmi.scripts.common.configuration import Configuration -from lmi.scripts.common.lmi_logging import get_logger - -LOG = get_logger(__name__) - -def get_computer_system(ns): - """ - Obtain an instance of ``CIM_ComputerSystem`` or its subclass. Preferred - class name can be configured in configuration file. If such class does - not exist, a base class (``CIM_ComputerSystem``) is enumerated instead. - First feasible instance is cached and returned. - - :param ns: Namespace object where to look for computer system class. - :type ns: :py:class:`lmi.shell.LMINamespace` - :returns: Instance of ``CIM_ComputerSystem``. - :rtype: :py:class:`lmi.shell.LMIInstance`. - """ - if not isinstance(ns, LMINamespace): - raise TypeError("ns must be an instance of LMINamespace") - if not hasattr(get_computer_system, '_cs_cache'): - get_computer_system._cs_cache = {} - ns_path = ns.connection.uri + '/' + ns.name - if not ns_path in get_computer_system._cs_cache: - config = Configuration.get_instance() - try: - get_computer_system._cs_cache[ns_path] = cs = \ - getattr(ns, config.system_class_name).first_instance() - except LMIExceptions.LMIClassNotFound: - LOG().warn('Failed to get instance of %s on host "%s"' - ' - falling back to CIM_ComputerSystem.', - config.system_class_name, ns.connection.uri) - get_computer_system._cs_cache[ns_path] = cs = \ - ns.CIM_ComputerSystem.first_instance_name() - LOG().debug('Loaded instance of %s:%s for host "%s".', - ns.name, cs.classname, ns.connection.uri) - return get_computer_system._cs_cache[ns_path] diff --git a/lmi/scripts/common/command/__init__.py b/lmi/scripts/common/command/__init__.py deleted file mode 100644 index a7c2df4..0000000 --- a/lmi/scripts/common/command/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -This subpackage defines base classes and utility functions for declaring -commands. These serve as wrappers for functions in libraries specific to -particular provider. - -Tree of these commands build a command line interface for this library. -""" - -from lmi.scripts.common.command.base import LmiBaseCommand -from lmi.scripts.common.command.checkresult import LmiCheckResult -from lmi.scripts.common.command.endpoint import LmiEndPointCommand -from lmi.scripts.common.command.lister import LmiInstanceLister -from lmi.scripts.common.command.lister import LmiLister -from lmi.scripts.common.command.multiplexer import LmiCommandMultiplexer -from lmi.scripts.common.command.session import LmiSessionCommand -from lmi.scripts.common.command.select import LmiSelectCommand -from lmi.scripts.common.command.show import LmiShowInstance - -from lmi.scripts.common.command.helper import make_list_command -from lmi.scripts.common.command.helper import register_subcommands -from lmi.scripts.common.command.helper import select_command diff --git a/lmi/scripts/common/command/base.py b/lmi/scripts/common/command/base.py deleted file mode 100644 index cebf422..0000000 --- a/lmi/scripts/common/command/base.py +++ /dev/null @@ -1,341 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module defining base command class for all possible commands of ``lmi`` -meta-command. -""" - -import abc -import re - -# regular expression matching leading whitespaces not until the first line -# containing non-white-spaces -RE_LSPACES = re.compile(r'\A(\s*$.)*', re.DOTALL | re.MULTILINE) - -#: Default formatting options overriden by options passed onc ommand-line and -#: set in configuration file. -DEFAULT_FORMATTER_OPTIONS = { - 'no_headings' : False, - 'padding' : 0, - 'human_friendly' : False, -} - -class LmiBaseCommand(object): - """ - Abstract base class for all commands handling command line arguments. - Instances of this class are organized in a tree with root element being the - ``lmi`` meta-command (if not running in interactive mode). Each such - instance can have more child commands if its - :py:meth:`LmiBaseCommand.is_multiplexer` method return ``True``. Each has - one parent command except for the top level one, whose :py:attr:`parent` - property returns ``None``. - - Set of commands is organized in a tree, where each command (except for the - root) has its own parent. :py:meth:`is_end_point` method distinguishes - leaves from nodes. The path from root command to the leaf is a sequence of - commands passed to command line. - - There is also a special command called selector. Its :py:meth:`is_selector` - method returns ``True``. It selects proper command that shall be passed all - the arguments based on expression with profile requirements. It shares its - name and parent with selected child. - - If the :py:meth:`LmiBaseCommand.has_own_usage` returns ``True``, the parent - command won't process the whole command line and the remainder will be - passed as a second argument to the :py:meth:`LmiBaseCommand.run` method. - - :param app: Main application object. - :param string cmd_name: Name of command. - :param parent: Parent command. - :type parent: :py:class:`LmiBaseCommand` - """ - - __metaclass__ = abc.ABCMeta - - @classmethod - def get_description(cls): - """ - Return description for this command. This is usually a first line - of documentation string of a class. - - :rtype: string - """ - if cls.__doc__ is None: - return "" - return cls.__doc__.strip().split("\n", 1)[0] - - @classmethod - def is_end_point(cls): - """ - :returns: ``True``, if this command parses the rest of command line and - can not have any child subcommands. - :rtype: boolean - """ - return True - - @classmethod - def is_multiplexer(cls): - """ - Is this command a multiplexer? Note that only one of - :py:meth:`is_end_point`, :py:meth:`is_selector` and this method can - evaluate to``True``. - - :returns: ``True`` if this command is not an end-point command and it's - a multiplexer. It contains one or more subcommands. It consumes the - first argument from command-line arguments and passes the rest to - one of its subcommands. - :rtype: boolean - """ - return not cls.is_end_point() - - @classmethod - def is_selector(cls): - """ - Is this command a selector? - - :returns: ``True`` if this command is a subclass of - :py:class:`lmi.scripts.common.command.select.LmiSelectCommand`. - :rtype: boolean - """ - return not cls.is_end_point() and not cls.is_multiplexer() - - @classmethod - def has_own_usage(cls): - """ - :returns: ``True``, if this command has its own usage string, which is - returned by :py:meth:`LmiBaseCommand.get_description`. Otherwise - the parent command must be queried. - :rtype: boolean - """ - return False - - @classmethod - def child_commands(cls): - """ - Abstract class method returning dictionary of child commands with - structure: :: - - { "command-name" : cmd_factory, ... } - - Dictionary contains just a direct children (commands, which - may immediately follow this particular command on command line). - """ - raise NotImplementedError("child_commands() method must be overriden" - " in a subclass") - - def __init__(self, app, cmd_name, parent=None): - if not isinstance(cmd_name, basestring): - raise TypeError('cmd_name must be a string') - if parent is not None and not isinstance(parent, LmiBaseCommand): - raise TypeError('parent must be an LmiBaseCommand instance') - self._app = app - self._cmd_name = cmd_name.strip() - self._parent = parent - - @property - def app(self): - """ Return application object. """ - return self._app - - @property - def parent(self): - """ Return parent command. """ - return self._parent - - @property - def cmd_name(self): - """ Name of this subcommand as a single word. """ - return self._cmd_name - - @property - def cmd_name_parts(self): - """ - Convenience property calling :py:meth:`get_cmd_name_parts` to obtain - command path as a list of all preceding command names. - - :rtype: list - """ - return self.get_cmd_name_parts() - - @property - def format_options(self): - """ - Compose formatting options. Parent commands are queried for defaults. If - command has no parent, default options will be taken from - :py:attr:`DEFAULT_FORMATTER_OPTIONS` which are overriden by config - settings. - - :returns: Arguments passed to formatter factory when formatter is - for current command is constructed. - :rtype: dictionary - """ - if self.parent is None: - options = DEFAULT_FORMATTER_OPTIONS.copy() - options['no_headings'] = self.app.config.no_headings - options['human_friendly'] = self.app.config.human_friendly - else: - options = self.parent.format_options - return options - - @property - def session(self): - """ - :returns: Session object. Session for command and all of its children - may be overriden with a call to :py:meth:`set_session_proxy`. - :rtype: :py:class:`lmi.scripts.common.session.Session` - """ - proxy = getattr(self, '_session_proxy', None) - if proxy: - return proxy - if self.parent is not None: - return self.parent.session - return self.app.session - - def get_cmd_name_parts(self, all_parts=False, demand_own_usage=True, - for_docopt=False): - """ - Get name of this command as a list composed of names of all preceding - commands since the top level one. When in interactive mode, only - commands following the active one will be present. - - :param boolean full: Take no heed to the active command or interactive - mode. Return all command names since top level node inclusive. This - is overriden with *for_docopt* flag. - :param boolean demand_own_usage: Wether to continue the upward - traversal through command hieararchy past the active command until - the command with its own usage is found. This is the default behaviour. - :param boolean for_docopt: Docopt parser needs to be given arguments list - without the first item compared to command names in usage string - it receives. Thus this option causes skipping the first item that would - be otherwise included. - :returns: Command path. Returned list will always contain at least the - name of this command. - :rtype: list - """ - parts = [self.cmd_name] - cmd = self - own_usage = cmd.has_own_usage() - while ( cmd.parent is not None - and (all_parts or self.app.active_command not in (cmd, cmd.parent)) - or (demand_own_usage and not own_usage)): - cmd = cmd.parent - parts.append(cmd.cmd_name) - own_usage = own_usage or cmd.has_own_usage() - if for_docopt and parts: - parts.pop() - return list(reversed(parts)) - - def get_usage(self, proper=False): - """ - Get command usage. Return value of this function is used by docopt - parser as usage string. Command tree is traversed upwards until command - with defined usage string is found. End point commands (leaves) require - manually written usage, so the first command in the sequence of parents - with own usage string is obtained and its usage returned. For nodes - missing own usage string this can be generated based on its - subcommands. - - :param boolean proper: Says, whether the usage string written - manually is required or not. It applies only to node (not a leaf) - commands without its own usage string. - """ - if self.is_end_point() or self.has_own_usage() or proper: - # get proper (manually written) usage, also referred as *own* - cmd = self - while not cmd.has_own_usage() and cmd.parent is not None: - cmd = cmd.parent - if cmd.__doc__ is None: - docstr = "Usage: %s\n" % " ".join(self.cmd_name_parts) - else: - docstr = ( ( cmd.__doc__.rstrip() - % {'cmd' : " ".join(cmd.cmd_name_parts)} - )) - - match = RE_LSPACES.match(docstr) - if match: # strip leading newlines - docstr = docstr[match.end(0):] - - match = re.match(r'^ +', docstr) - if match: # unindent help message - re_lspaces = re.compile(r'^ {%s}' % match.end(0)) - docstr = "\n".join(re_lspaces.sub('', l) - for l in docstr.splitlines()) - docstr += "\n" - - else: - # generate usage string from what is known, applies to nodes - # without own usage - hlp = [] - if self.get_description(): - hlp.append(self.get_description()) - hlp.append("") - hlp.append("Usage:") - hlp.append(" %s <command> [<args> ...]" - % " ".join(self.cmd_name_parts)) - hlp.append("") - hlp.append("Commands:") - cmd_max_len = max(len(c) for c in self.child_commands()) - for name, cmd in sorted(self.child_commands().items()): - hlp.append((" %%-%ds %%s" % cmd_max_len) - % (name, cmd.get_description())) - docstr = "\n".join(hlp) + "\n" - - return docstr - - @abc.abstractmethod - def run(self, args): - """ - Handle the command line arguments. If this is not an end point - command, it will pass the unhandled arguments to one of it's child - commands. So the arguments are processed recursively by the instances - of this class. - - :param list args: Arguments passed to the command line that were - not yet parsed. It's the contents of ``sys.argv`` (if in - non-interactive mode) from the current command on. - :returns: Exit code of application. This maybe also be a boolean value - or ``None``. ``None`` and ``True`` are treated as a success causing - exit code to be 0. - :rtype: integer - """ - raise NotImplementedError("run method must be overriden in subclass") - - def set_session_proxy(self, session): - """ - Allows to override session object. This is useful for especially for - conditional commands (subclasses of - :py:class:`~lmi.scripts.common.command.select.LmiSelectCommand`) that devide - connections to groups satisfying particular expression. These groups - are turned into session proxies containing just a subset of connections - in global session object. - - :param session: Session object. - """ - self._session_proxy = session diff --git a/lmi/scripts/common/command/checkresult.py b/lmi/scripts/common/command/checkresult.py deleted file mode 100644 index 27e9549..0000000 --- a/lmi/scripts/common/command/checkresult.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -This module defines LmiCheckResult command class and related utilities. -""" - -import abc - -from lmi.scripts.common import Configuration -from lmi.scripts.common import get_logger -from lmi.scripts.common import formatter -from lmi.scripts.common import errors -from lmi.scripts.common.command import meta -from lmi.scripts.common.command.session import LmiSessionCommand - -LOG = get_logger(__name__) - -class LmiResultFailed(errors.LmiFailed): - """ - Exception raised when associated function returns unexpected result. This - is evaluated by :py:meth:`LmiCheckResult.check_result` method. - """ - pass - -def _make_result_failed(expected, result): - """ - Instantiate :py:exc:`LmiResultFailed` exception with descriptive message - composed of what was expected and what was returned instead. - - :rtype: :py:class:`LmiResultFailed` - """ - return LmiResultFailed('failed (%s != %s)' % (repr(expected), repr(result))) - -class LmiCheckResult(LmiSessionCommand): - """ - Run an associated action and check its result. It implicitly makes no - output if the invocation is successful and expected result matches. - - List of additional recognized properties: - - ``EXPECT`` : - Value, that is expected to be returned by invoked associated - function. This can also be a callable taking two arguments: - - 1. options - Dictionary with parsed command line options - returned by ``docopt``. - 2. result - Return value of associated function. - - Using metaclass: :py:class:`~.meta.CheckResultMetaClass`. - """ - __metaclass__ = meta.CheckResultMetaClass - - def __init__(self, *args, **kwargs): - LmiSessionCommand.__init__(self, *args, **kwargs) - - def formatter_factory(self): - return formatter.TableFormatter - - @abc.abstractmethod - def check_result(self, options, result): - """ - Check the returned value of associated function. - - :param dictionary options: Dictionary as returned by ``docopt`` parser - after running - :py:meth:`~.endpoint.LmiEndPointCommand.transform_options`. - :param result: Any return value that will be compared against what is - expected. - :returns: Whether the result is expected value or not. If ``tuple`` - is returned, it contains ``(passed_flag, error_description)``. - :rtype: boolean or tuple. - """ - raise NotImplementedError("check_result must be overriden in subclass") - - def take_action(self, connection, args, kwargs): - """ - Invoke associated method and check its return value for single host. - - :param list args: List of arguments to pass to the associated - function. - :param dictionary kwargs: Keyword arguments to pass to the associated - function. - :returns: Exit code (0 on success). - :rtype: integer - """ - try: - result = self.execute_on_connection(connection, *args, **kwargs) - passed = self.check_result(self._options, result) - if isinstance(passed, tuple): - if len(passed) != 2: - raise errors.LmiUnexpectedResult('check_result() must' - ' return either boolean or (passed_flag,' - ' error_description), not "%s"' % repr(passed)) - if not passed[0]: - raise LmiResultFailed(passed[1]) - elif not passed and hasattr(self.check_result, 'expected'): - err = _make_result_failed(self.check_result.expected, result) - raise err - except LmiResultFailed: - raise - except Exception as err: - LOG().debug("Failed to execute wrapped function.", exc_info=err) - raise - return 0 - - def process_host_result(self, hostname, success, result): - pass - - def process_session_results(self, session, results): - if len(self.session) > 1: - LOG().debug('Successful runs: %d\n', - len([r for r in results.values() if r[0]])) - LmiSessionCommand.process_session_results(self, session, results) diff --git a/lmi/scripts/common/command/endpoint.py b/lmi/scripts/common/command/endpoint.py deleted file mode 100644 index c744e34..0000000 --- a/lmi/scripts/common/command/endpoint.py +++ /dev/null @@ -1,367 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Defines base command class for all endpoint commands. Those having no children. -""" -import abc -import inspect -import re -from docopt import docopt - -from lmi.scripts.common import errors -from lmi.scripts.common import formatter -from lmi.scripts.common import get_logger -from lmi.scripts.common.formatter import command as fcmd -from lmi.scripts.common.command import base -from lmi.scripts.common.command import meta -from lmi.scripts.common.command import util - -LOG = get_logger(__name__) - -def opt_name_sanitize(opt_name): - """ - Make a function parameter name out of option name. This replaces any - character not suitable for python identificator with ``'_'`` and - make the whole string lowercase. - - :param string opt_name: Option name. - :returns: Modified option name. - :rtype: string - """ - return re.sub(r'[^a-zA-Z0-9]+', '_', opt_name).lower() - -def options_dict2kwargs(options): - """ - Convert option name from resulting ``docopt`` dictionary to a valid python - identificator token used as function argument name. - - :param dictionary options: Dictionary returned by docopt call. - :returns: New dictionary with keys passable to function as argument - names. - :rtype: dictionary - """ - # (new_name, value) for each pair in options dictionary - kwargs = {} - # (new_name, name) - orig_names = {} - for name, value in options.items(): - for (reg, func) in ( - (util.RE_OPT_BRACKET_ARGUMENT, lambda m: m.group('name')), - (util.RE_OPT_UPPER_ARGUMENT, lambda m: m.group('name')), - (util.RE_OPT_SHORT_OPTION, lambda m: m.group(0)), - (util.RE_OPT_LONG_OPTION, lambda m: m.group(0)), - (util.RE_COMMAND_NAME, lambda m: m.group(0))): - match = reg.match(name) - if match: - new_name = func(match) - break - else: - raise errors.LmiError( - 'Failed to convert argument "%s" to function option.' % - name) - if new_name == '--': - continue # ignore double dash - new_name = opt_name_sanitize(new_name) - if new_name in kwargs: - raise errors.LmiError('Option clash for "%s" and "%s", which both' - ' translate to "%s".' % (name, orig_names[new_name], new_name)) - kwargs[new_name] = value - orig_names[new_name] = name - return kwargs - -class LmiEndPointCommand(base.LmiBaseCommand): - """ - Base class for any leaf command. - - List of additional recognized properties: - - ``CALLABLE`` : ``tuple`` - Associated function. Will be wrapped in - :py:meth:`LmiEndPointCommand.execute` method and will be accessible - directly as a ``cmd.execute.dest`` property. It may be specified - either as a string in form ``"<module_name>:<callable>"`` or as a - reference to callable itself. - ``ARG_ARRAY_SUFFIX`` : ``str`` - String appended to every option parsed by ``docopt`` having list as - an associated value. It defaults to empty string. This modification - is applied before calling - :py:meth:`LmiEndPointCommand.verify_options` and - :py:meth:`LmiEndPointCommand.transform_options`. - ``FORMATTER`` : callable - Default formatter factory for instances of given command. This - factory accepts an output stream as the only parameter and returns - an instance of :py:class:`~lmi.scripts.common.formatter.Formatter`. - - Using metaclass: - :py:class:`.meta.EndPointCommandMetaClass`. - """ - __metaclass__ = meta.EndPointCommandMetaClass - - def __init__(self, *args, **kwargs): - super(LmiEndPointCommand, self).__init__(*args, **kwargs) - self._formatter = None - # saved options dictionary after call to transform_options() - self._options = None - - @abc.abstractmethod - def execute(self, *args, **kwargs): - """ - Subclasses must override this method to pass given arguments to - command library function. This function shall be specified in - ``CALLABLE`` property. - """ - raise NotImplementedError("execute method must be overriden" - " in subclass") - - def formatter_factory(self): - """ - Subclasses shall override this method to provide default formatter - factory for printing output. - - :returns: Subclass of basic formatter. - """ - return formatter.Formatter - - @classmethod - def dest_pos_args_count(cls): - """ - Number of positional arguments the associated function takes from - command. These arguments are created by the command alone -- they do - not belong to options in usage string. Function can take additional - positional arguments that need to be covered by usage string. - - :rtype: integer - """ - dest = getattr(cls.execute, "dest", cls.execute) - abstract = dest == cls.execute and util.is_abstract_method( - cls, 'execute', True) - # if the destination function is not yet defined (abstract is True) - # let's assume it's not a method => 0 positional arguments needed - return 1 if not abstract and inspect.ismethod(dest) else 0 - - def run_with_args(self, args, kwargs): - """ - Process end-point arguments and exit. - - :param list args: Positional arguments to pass to associated - function in command library. - :param dictionary kwargs: Keyword arguments as a dictionary. - :returns: Exit code of application. - :rtype: integer - """ - return self.execute(*args, **kwargs) - - @property - def formatter(self): - """ - Return instance of default formatter. - - :rtype: :py:class:`~lmi.scripts.common.formatter.Formatter` - """ - if self._formatter is None: - opts = self.format_options - factory = self.formatter_factory() - argspec = inspect.getargspec( - factory.__init__ if type(factory) is type else factory) - if not argspec.keywords: - kwargs = {k: v for k, v in opts.items() if k in argspec.args} - self._formatter = factory(self.app.stdout, **kwargs) - return self._formatter - - def _make_end_point_args(self, options): - """ - Creates a pair of positional and keyword arguments for a call to - associated function from command line options. All keyword - options not expected by target function are removed. - - :param dictionary options: Output of ``docopt`` parser. - :returns: Positional and keyword arguments as a pair. - :rtype: tuple - """ - # if execute method does not have a *dest* attribute, then it's - # itself a destination - dest = getattr(self.execute, "dest", self.execute) - argspec = inspect.getargspec(dest) - kwargs = options_dict2kwargs(options) - # number of positional arguments not covered by usage string - pos_args_count = self.dest_pos_args_count() - to_remove = [] - # if associated function takes keyword arguments in a single - # dictionary (kwargs), we can pass all options - if argspec.keywords is None: - # otherwise we need to remove any unhandled - for opt_name in kwargs: - if opt_name not in argspec.args[pos_args_count:]: - if opt_name not in self.cmd_name_parts: - LOG().debug('Option "%s" not handled in function "%s",' - ' ignoring.', opt_name, self.cmd_name) - to_remove.append(opt_name) - for opt_name in to_remove: - # remove options unhandled by function - del kwargs[opt_name] - args = [] - for arg_name in argspec.args[pos_args_count:]: - if arg_name not in kwargs: - raise errors.LmiCommandError( - self.__module__, self.__class__.__name__, - 'registered command "%s" expects option "%s", which' - ' is not covered in usage string' - % (self.cmd_name, arg_name)) - args.append(kwargs.pop(arg_name)) - return args, kwargs - - def _preprocess_options(self, options): - """ - This method may be overriden by - :py:class:`~.meta.EndPointCommandMetaClass` - as a result of processing ``ARG_ARRAY_SUFFIX`` and other properties - modifying names of parsed options. - - This should not be overriden in command class's body. - - :param dictionary options: The result of ``docopt`` parser invocation - which can be modified by this method. - """ - pass - - def _parse_args(self, args): - """ - Run ``docopt`` command line parser on given list of arguments. - Removes all unrelated commands from created dictionary of options. - - :param list args: List of command line arguments just after the - current command. - :returns: Dictionary with parsed options. Please refer to - docopt_ documentation for more informations. - :rtype: dictionary - - .. _docopt: http://docopt.org/ - """ - full_args = self.get_cmd_name_parts(for_docopt=True) + args - options = docopt(self.get_usage(), full_args, help=False) - self._preprocess_options(options) - - # remove all command names from options - cmd = self.parent - while cmd is not None and not cmd.has_own_usage(): - cmd = cmd.parent - if cmd is not None: - for scn in cmd.child_commands(): - try: - del options[scn] - except KeyError: - LOG().warn('Usage string of "%s.%s" command does not' - ' contain registered command "%s" command.', - cmd.__module__, cmd.__class__.__name__, scn) - # remove also the root command name from options - if cmd is not None and cmd.cmd_name in options: - del options[cmd.cmd_name] - return options - - def verify_options(self, options): - """ - This method can be overriden in subclasses to check, whether the - options given on command line are valid. If any flaw is discovered, an - :py:exc:`~lmi.scripts.common.errors.LmiInvalidOptions` exception shall - be raised. Any returned value is ignored. - - .. note:: - This is run before :py:meth:`transform_options()` method. - - :param dictionary options: Dictionary as returned by ``docopt`` parser. - """ - pass - - def transform_options(self, options): - """ - This method can be overriden in subclasses if options shall be somehow - modified before passing them associated function. - - .. note:: - Run after :py:meth:`verify_options()` method. - - :param dictionary options: Dictionary as returned by ``docopt`` parser. - """ - pass - - def produce_output(self, data): - """ - This method can be use to render and print results with default - formatter. - - :param data: Is an object expected by the - :py:meth:`~lmi.scripts.common.formatter.Formatter.produce_output` - method of formatter. - """ - self.formatter.produce_output(data) - - def run(self, args): - """ - Create options dictionary from input arguments, verify them, - transform them, make positional and keyword arguments out of them and - pass them to ``process_session()``. - - :param list args: List of command arguments. - :returns: Exit code of application. - :rtype: integer - """ - options = self._parse_args(args) - self.verify_options(options) - self.transform_options(options) - self._options = options.copy() - args, kwargs = self._make_end_point_args(options) - return self.run_with_args(args, kwargs) - - def _print_errors(self, error_list, new_line=True): - """ - Print list of errors. - - :param list errors: Errors to print. Each error is a ``tuple``: :: - - (hostname, [error, error]) - - Where ``error`` may be a test description or an instance of - exception. - :param new_line: Whether to print the new line before new error - table is printed. - """ - fmt = formatter.ErrorFormatter(self.app.stderr) - if new_line: - fmt.out.write('\n') - if error_list: - new_table_cmd = fcmd.NewTableCommand("There %s %d error%s" % - ( 'were' if len(error_list) > 1 else 'was' - , len(error_list) - , 's' if len(error_list) > 1 else '')) - fmt.produce_output((new_table_cmd, )) - for hostname, host_errors in error_list: - fmt.produce_output((fcmd.NewHostCommand(hostname), )) - fmt.produce_output(host_errors) - diff --git a/lmi/scripts/common/command/helper.py b/lmi/scripts/common/command/helper.py deleted file mode 100644 index 578fa8b..0000000 --- a/lmi/scripts/common/command/helper.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module with convenient function for defining user commands. -""" - -from lmi.scripts.common.command import LmiLister -from lmi.scripts.common.command import LmiCommandMultiplexer -from lmi.scripts.common.command import LmiSelectCommand -from lmi.scripts.common.command import util - -def make_list_command(func, - name=None, - columns=None, - verify_func=None, - transform_func=None): - """ - Create a command subclassed from :py:class:`~.lister.LmiLister`. Please - refer to this class for detailed usage. - - :param func: Contents of ``CALLABLE`` property. - :type func: string or callable - :param string name: Optional name of resulting class. If not given, - it will be made from the name of associated function. - :param tuple columns: Contents of ``COLUMNS`` property. - :param callable verify_func: Callable overriding - py:meth:`~.endpoint.LmiEndPointCommand.verify_options` method. - :param callable transform_func: Callable overriding - :py:meth:`~.endpoint.LmiEndPointCommand.transform_options` method. - :returns: Subclass of :py:class:`~.lister.LmiLister`. - :rtype: type - """ - if name is None: - if isinstance(func, basestring): - name = func.split('.')[-1] - else: - name = func.__name__ - if not name.startswith('_'): - name = '_' + name.capitalize() - props = { 'COLUMNS' : columns - , 'CALLABLE' : func - , '__module__' : util.get_module_name() } - if verify_func: - props['verify_options'] = verify_func - if transform_func: - props['transform_options'] = transform_func - return LmiLister.__metaclass__(name, (LmiLister, ), props) - -def register_subcommands(command_name, usage, command_map, - fallback_command=None): - """ - Create a multiplexer command (a node in a tree of commands). - - :param string command_name: Name of created command. The same as will - be given on a command line. - :param string usage: Usage string parseable by ``docopt``. - :param dictionary command_map: Dictionary of subcommands. Associates - command names to their factories. It's assigned to ``COMMANDS`` - property. - :param fallback_command: Command factory used when no command is given - on command line. - :type fallback_command: :py:class:`~.endpoint.LmiEndPointCommand` - :returns: Subclass of :py:class:`~.multiplexer.LmiCommandMultiplexer`. - :rtype: type - """ - props = { 'COMMANDS' : command_map - , 'OWN_USAGE' : True - , '__doc__' : usage - , '__module__' : util.get_module_name() - , 'FALLBACK_COMMAND' : fallback_command } - return LmiCommandMultiplexer.__metaclass__(command_name, - (LmiCommandMultiplexer, ), props) - -def select_command(command_name, *args, **kwargs): - """ - Create command selector that loads command whose requirements are met. - - Example of invocation: :: - - Hardware = select_command('Hardware', - ("Openlmi-Hardware >= 0.4.2", "lmi.scripts.hardware.current.Cmd"), - ("Openlmi-Hardware < 0.4.2" , "lmi.scripts.hardware.pre042.Cmd"), - default=HwMissing - ) - - Above example checks remote broker for OpenLMI-Hardware provider. If it is - installed and its version is equal or higher than 0.4.2, command from - ``current`` module will be used. For older registered versions command - contained in ``pre042`` module will be loaded. If hardware provider is not - available, HwMissing command will be loaded instead. - - .. seealso:: - Check out the grammer describing language used in these conditions at - :py:mod:`lmi.scripts.common.versioncheck.parser`. - - :param args: List of pairs ``(condition, command)`` that are inspected in - given order until single condition is satisfied. Associated command is - then loaded. Command is either a reference to command class or path to - it given as string. In latter case last dot divides module's import - path and command name. - :param default: This command will be loaded when no condition from *args* - is satisfied. - """ - props = { 'SELECT' : args - , 'DEFAULT' : kwargs.get('default', None) - , '__module__' : util.get_module_name() - } - return LmiSelectCommand.__metaclass__(command_name, - (LmiSelectCommand, ), props) - diff --git a/lmi/scripts/common/command/lister.py b/lmi/scripts/common/command/lister.py deleted file mode 100644 index 6b36168..0000000 --- a/lmi/scripts/common/command/lister.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Defines command classes producing tablelike output. -""" - -import abc -from itertools import chain - -from lmi.scripts.common import errors -from lmi.scripts.common import formatter -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import meta -from lmi.scripts.common.command.session import LmiSessionCommand -from lmi.scripts.common.configuration import Configuration -from lmi.scripts.common.formatter import command as fcmd - -LOG = get_logger(__name__) - -class LmiBaseListerCommand(LmiSessionCommand): - """ - Base class for all lister commands. - """ - - @classmethod - def get_columns(cls): - """ - :returns: Column names for resulting table. ``COLUMNS`` property - will be converted to this class method. If ``None``, the associated - function shall return column names as the first tuple of returned - list. If empty tuple or list, no header shall be printed and associated - function returns just data rows. - :rtype: list or tuple or None - """ - return None - - def formatter_factory(self): - if self.app.config.lister_format == Configuration.LISTER_FORMAT_CSV: - return formatter.CsvFormatter - else: - return formatter.TableFormatter - -class LmiLister(LmiBaseListerCommand): - """ - End point command outputting a table for each host. Associated function - shall return a list of rows. Each row is represented as a tuple holding - column values. - - List of additional recognized properties: - - ``COLUMNS`` : ``tuple`` - Column names. It's a tuple with name for each column. Each row - shall then contain the same number of items as this tuple. If - omitted, associated function is expected to provide them in the - first row of returned list. It's translated to ``get_columns()`` - class method. - - Using metaclass: :py:class:`~.meta.ListerMetaClass`. - """ - __metaclass__ = meta.ListerMetaClass - - def take_action(self, connection, args, kwargs): - """ - Collects results of single host. - - :param connection: Connection to a single host. - :type connection: :py:class:`lmi.shell.LMIConnection` - :param list args: Positional arguments for associated function. - :param dictionary kwargs: Keyword arguments for associated function. - :returns: Column names and item list as a pair. - :rtype: tuple - """ - res = self.execute_on_connection(connection, *args, **kwargs) - columns = self.get_columns() - if isinstance(columns, (tuple, list)) and columns: - command = fcmd.NewTableHeaderCommand(columns) - res = chain((command, ), res) - elif columns is None: - resi = iter(res) - command = fcmd.NewTableHeaderCommand(resi.next()) - res = chain((command, ), resi) - return res - -class LmiInstanceLister(LmiBaseListerCommand): - """ - End point command outputting a table of instances for each host. - Associated function shall return a list of instances. They may be - prepended with column names depending on value of ``DYNAMIC_PROPERTIES``. - Each instance will occupy single row of table with property values being a - content of cells. - - List of additional recognized properties is the same as for - :py:class:`~.show.LmiShowInstance`. There is just one difference. Either - ``DYNAMIC_PROPERTIES`` must be ``True`` or ``PROPERTIES`` must be filled. - - Using metaclass: :py:class:`~.meta.InstanceListerMetaClass`. - """ - __metaclass__ = meta.InstanceListerMetaClass - - @abc.abstractmethod - def render(self, result): - """ - This method can either be overriden in a subclass or left alone. In the - latter case it will be generated by - :py:class:`~.meta.InstanceListerMetaClass` metaclass with regard to - ``PROPERTIES`` and ``DYNAMIC_PROPERTIES``. - - :param result: Either an instance to render or pair of properties and - instance. - :type result: :py:class:`lmi.shell.LMIInstance` or tuple - :returns: List of pairs, where the first item is a label and second a - value to render. - :rtype: list - """ - raise NotImplementedError( - "render method must be overriden in subclass") - - def take_action(self, connection, args, kwargs): - """ - Collects results of single host. - - :param connection: Connection to a single host. - :type connection: :py:class:`lmi.shell.LMIConnection` - :param list args: Positional arguments for associated function. - :param dictionary kwargs: Keyword arguments for associated function. - :returns: Column names and item list as a pair. - :rtype: tuple - """ - cols = self.get_columns() - if cols is None: - result = self.execute_on_connection( - connection, *args, **kwargs) - if not isinstance(result, tuple) or len(result) != 2: - raise errors.LmiUnexpectedResult( - self.__class__, "(properties, instances)", result) - cols, data = result - if not isinstance(cols, (tuple, list)): - raise errors.LmiUnexpectedResult( - self.__class__, "(tuple, ...)", (cols, '...')) - header = [c if isinstance(c, basestring) else c[0] for c in cols] - cmd = fcmd.NewTableHeaderCommand(columns=header) - return chain((cmd, ), (self.render((cols, inst)) for inst in data)) - else: - data = self.execute_on_connection(connection, *args, **kwargs) - if not hasattr(data, '__iter__'): - raise errors.LmiUnexpectedResult( - self.__class__, 'list or generator', data) - cmd = fcmd.NewTableHeaderCommand(columns=cols) - return chain((cmd, ), (self.render(inst) for inst in data)) diff --git a/lmi/scripts/common/command/meta.py b/lmi/scripts/common/command/meta.py deleted file mode 100644 index 7d8213e..0000000 --- a/lmi/scripts/common/command/meta.py +++ /dev/null @@ -1,839 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Meta classes simplifying declaration of user commands. - -Each command is defined as a class with a set of properties. Some are -mandatory, the others have some default values. Each of them is transformed by -metaclasse to some function, class method or other property depending on -command type and semantic of property. Property itself is removed from -resulting class after being processed by meta class. -""" - -import abc -import inspect -import re - -from lmi.scripts.common import Configuration -from lmi.scripts.common import get_logger -from lmi.scripts.common import errors -from lmi.scripts.common.command import base -from lmi.scripts.common.command import util -from lmi.shell import LMIInstance -from lmi.shell.LMIReturnValue import LMIReturnValue - -RE_CALLABLE = re.compile( - r'^(?P<module>[a-z_]+(?:\.[a-z_]+)*):(?P<func>[a-z_]+)$', - re.IGNORECASE) -RE_ARRAY_SUFFIX = re.compile(r'^(?:[a-z_]+[a-z0-9_]*)?$', re.IGNORECASE) -RE_OPTION = re.compile(r'^-+(?P<name>[^-+].*)$') -RE_MODULE_PATH = re.compile(r'([a-zA-z_]\w+\.)+[a-zA-z_]\w+') - -FORMAT_OPTIONS = ('no_headings', 'human_friendly') - -LOG = get_logger(__name__) - -def _handle_usage(name, bases, dcl): - """ - Take care of ``OWN_USAGE`` property. Supported values: - - `True`` : - Means that documentation string of class is a usage string. - ``False`` : - No usage string for this command is defined. - ``"usage string"`` : - This property is a usage string. - - Defaults to ``False``. - - Usage string is an input parameter to ``docopt`` command-line options - parser. - - :param string name: Name o command class. - :param dictionary dcl: Class dictionary, which is modified by this - function. - """ - has_own_usage = False - hlp = dcl.pop('OWN_USAGE', False) - if hlp is True: - if dcl['__doc__'] is None: - raise errors.LmiCommandInvalidProperty(dcl['__module__'], name, - "OWN_USAGE set to True, but no __doc__ string present!") - has_own_usage = True - elif isinstance(hlp, basestring): - if not '__doc__' in dcl: - dcl['__doc__'] = hlp - else: - if not 'get_usage' in dcl: - def _new_get_usage(_self, proper=False): - """ Get the usage string for ``doctopt`` parser. """ - return hlp - dcl['get_usage'] = _new_get_usage - has_own_usage = True - elif ( dcl.get('__node__', None) is None - and any(getattr(b, 'has_own_usage', lambda: False)() for b in bases)): - # inherit doc string of base class - dcl['__doc__'] = ( b.__doc__ for b in bases - if getattr(b, 'has_own_usage', lambda: False)()).next() - has_own_usage = True - if has_own_usage: - if not 'has_own_usage' in dcl: - dcl['has_own_usage'] = classmethod(lambda _cls: True) - -def _make_execute_method(bases, dcl, func): - """ - Creates ``execute()`` method of a new end point command. - - :param tuple bases: Base classes of new command. - :param dictionary dcl: Class dictionary being modified by this method. - :param callable func: A callable wrapped by this new command. It's usually - being referred to as *associated function*. If ``None``, no function - will be created -- ``dcl`` won't be modified. - """ - if func is not None and util.is_abstract_method( - bases, 'execute', missing_is_abstract=True): - del dcl['CALLABLE'] - def _execute(__self__, __connection__, *args, **kwargs): - """ Invokes associated function with given arguments. """ - return func(__connection__, *args, **kwargs) - _execute.dest = func - dcl['execute'] = _execute - -def _handle_namespace(dcl): - """ - Overrides ``cim_namespace()`` class method if ``NAMESPACE`` property - is given. - - :param dictionary dcl: Class dictionary being modified by this method. - """ - if 'NAMESPACE' in dcl: - namespace = dcl.pop('NAMESPACE') - def _new_cim_namespace(_cls): - """ Returns cim namespace used to modify connection object. """ - return namespace - dcl['cim_namespace'] = classmethod(_new_cim_namespace) - -def _handle_callable(name, bases, dcl): - """ - Process the ``CALLABLE`` property of end-point command. Create the - ``execute()`` method based on it. - - :param string name: Name of command class to create. - :param tuple bases: Base classes of new command. - :param dictionary dcl: Class dictionary being modified by this method. - """ - try: - func = dcl.get('CALLABLE') - if isinstance(func, basestring): - match = RE_CALLABLE.match(func) - if not match: - raise errors.LmiCommandInvalidCallable( - dcl['__module__'], name, - 'Callable "%s" has invalid format (\':\' expected)' - % func) - mod_name = match.group('module') - try: - func = getattr(__import__(mod_name, globals(), locals(), - [match.group('func')], 0), - match.group('func')) - except (ImportError, AttributeError): - raise errors.LmiImportCallableFailed( - dcl['__module__'], name, func) - except KeyError: - raise errors.LmiCommandMissingCallable(dcl['__module__'], name) - if func is not None and not callable(func): - raise errors.LmiCommandInvalidCallable( - '"%s" is not a callable object or function.' % ( - func.__module__ + '.' + func.__name__)) - - _make_execute_method(bases, dcl, func) - -def _make_render_all_properties(bases): - """ - Creates ``render()`` method, rendering all properties of instance. - - :param tuple bases: Base classes of new command class. - :returns: Rendering method taking CIM instance as an - argument. - :rtype: function - """ - if util.is_abstract_method(bases, 'render', missing_is_abstract=True): - def _render(_self, inst): - """ - Return tuple of ``(column_names, values)`` ready for output by - formatter. - """ - column_names, values = [], [] - for prop_name, value in sorted(inst.properties_dict().iteritems()): - column_names.append(prop_name) - if value is None: - value = '' - values.append(value) - return (column_names, values) - - return _render - -def _make_render_with_properties(properties, target_formatter_lister=False): - """ - Creates ``render()`` method, rendering given instance properties. - - :param properties: (``list``) List of properties to render. - :param target_formatter_lister: (``bool``) Whether the output is targeted - for Show command or Lister. The former expects a pair of column_names - and values. The latter expects just values. - :rtype: (``function``) Rendering method taking CIM instance as an - argument. - """ - def _process_property(prop, inst): - """ - Takes a single property and instance. Returns computed value. - - :rtype: ``(str, any)`` A pair of property name and value. - """ - if isinstance(prop, basestring): - prop_name = prop - if not prop in inst.properties(): - LOG().warn('Property "%s" not present in instance of "%s".', - prop, inst.path) - value = "UNKNOWN" - else: - value = getattr(inst, prop) - else: - if not isinstance(prop, (tuple, list)): - raise TypeError("prop must be a string or tuple, not %s" % - repr(prop)) - prop_name = prop[0] - try: - if callable(prop[1]): - value = prop[1](inst) - else: - value = getattr(inst, prop[1]) - except Exception as exc: - LOG().exception('Failed to render property "%s": %s', - prop[0], exc) - value = "ERROR" - if value is None: - value = '' - return prop_name, value - - if target_formatter_lister: - def _render(self, inst): - """ - Renders a limited set of properties and returns a row for instance - table composed of property values. - """ - if not isinstance(inst, LMIInstance): - raise errors.LmiUnexpectedResult( - self.__class__, 'LMIInstance object', inst) - return tuple(_process_property(p, inst)[1] for p in properties) - - else: - def _render(self, inst): - """ - Renders a limited set of properties and returns a pair of - column names and values. - """ - if not isinstance(inst, LMIInstance): - raise errors.LmiUnexpectedResult( - self.__class__, 'LMIInstance object', inst) - column_names, values = [], [] - for prop in properties: - prop_name, value = _process_property(prop, inst) - column_names.append(prop_name) - values.append(value) - return (column_names, values) - - return _render - -def _check_render_properties(name, dcl, props): - """ - Make sanity check for ``PROPERTIES`` class property. Exception will be - raised when any flaw discovered. - - :param string name: Name of class to be created. - :param dictionary dcl: Class dictionary. - :param list props: List of properties or ``None``. - """ - if props is not None: - for prop in props: - if not isinstance(prop, (basestring, tuple, list)): - raise errors.LmiCommandInvalidProperty( - dcl['__module__'], name, - 'PROPERTIES must be a list of strings or tuples') - if isinstance(prop, (tuple, list)): - if ( len(prop) != 2 - or not isinstance(prop[0], basestring) - or ( not callable(prop[1]) - and not isinstance(prop[1], basestring))): - raise errors.LmiCommandInvalidProperty( - dcl['__module__'], name, - 'tuples in PROPERTIES must be: ("name",' - ' callable or property_name)') - -def _handle_render_properties(name, bases, dcl, target_formatter_lister=False): - """ - Process properties related to rendering function for commands operating - on CIM instances. Result of this function a ``render()`` and - ``get_columns()`` functions being added to class's dictionary with - regard to handled properties. - - Currently handled properties are: - - ``DYNAMIC_PROPERTIES`` : ``bool`` - Whether the associated function itself provides list of - properties. Optional property. - ``PROPERTIES`` : ``bool`` - List of instance properties to print. Optional property. - - :param string name: Name of class to be created. - :param tuple bases: Base classes of new command. - :param dictionary dcl: Class dictionary being modified by this method. - :param boolean target_formatter_lister: Whether the output is targeted - for *Show* command or *Lister*. The former expects a pair of - column_names and values. The latter expects just values. - """ - dynamic_properties = dcl.pop('DYNAMIC_PROPERTIES', False) - if dynamic_properties and 'PROPERTIES' in dcl: - raise errors.LmiCommandError( - dcl['__module__'], name, - 'DYNAMIC_PROPERTIES and PROPERTIES are mutually exclusive') - - properties = dcl.pop('PROPERTIES', None) - _check_render_properties(name, dcl, properties) - - renderer = None - get_columns = lambda cls: None - if properties is None and not dynamic_properties: - if ( target_formatter_lister - and dcl.get('__metaclass__', None) is not InstanceListerMetaClass): - raise errors.LmiCommandError(dcl['__module__'], name, - "either PROPERTIES must be declared or" - " DYNAMIC_PROPERTIES == True for InstanceLister" - " commands") - renderer = _make_render_all_properties(bases) - elif properties is None and dynamic_properties: - def _render_dynamic(self, return_value): - """ Renderer of dynamic properties. """ - properties, inst = return_value - return _make_render_with_properties(properties, - target_formatter_lister)(self, inst) - renderer = _render_dynamic - elif properties is not None: - renderer = _make_render_with_properties(properties, - target_formatter_lister) - get_columns = (lambda cls: - tuple((p[0] if isinstance(p, tuple) else p) - for p in properties)) - if renderer is not None: - dcl['render'] = classmethod(renderer) - if target_formatter_lister: - dcl['get_columns'] = get_columns - -def _handle_opt_preprocess(name, dcl): - """ - Process properties, that cause modification of parsed argument names before - passing them to ``verify_options()`` or ``transform_options()``. If any of - handled properties is supplied, it causes ``_preprocess_options()`` to be - overriden, where all of desired name modifications will be made. - Currently handled properties are: - - ``OPT_NO_UNDERSCORES`` : ``bool`` - When making a function's parameter name out of option, the leading - dashes are replaced with underscore. If this property is True, - dashes will be removed completely with no replacement. - ``ARG_ARRAY_SUFFIX`` : ``bool`` - Add given suffix to all arguments resulting in list objects. - - :param string name: Command class name. - :param dictionary dcl: Class dictionary being modified by this method. - """ - if ( dcl.get('__metaclass__', None) is not EndPointCommandMetaClass - and '_preprocess_options' in dcl): - raise errors.LmiCommandError(dcl['__module__'], name, - '_preprocess_options() method must not be overriden in the' - 'body of command class; use transform_options() instead') - arr_suffix = dcl.pop('ARG_ARRAY_SUFFIX', '') - if ( not isinstance(arr_suffix, str) - or not RE_ARRAY_SUFFIX.match(arr_suffix)): - raise errors.LmiCommandInvalidProperty(dcl['__module__'], name, - 'ARG_ARRAY_SUFFIX must be a string matching regular' - ' expression "%s"' % RE_ARRAY_SUFFIX.pattern) - opt_no_underscores = dcl.pop('OPT_NO_UNDERSCORES', False) - if arr_suffix or opt_no_underscores: - def _new_preprocess_options(_self, options): - """ Modify (in-place) given options dictionary by renaming keys. """ - for do_it, cond, transform in ( - ( arr_suffix - , lambda _, v: isinstance(v, list) - , lambda n : - ('<' + util.RE_OPT_BRACKET_ARGUMENT.match(n) - .group(1) + arr_suffix + '>') - if util.RE_OPT_BRACKET_ARGUMENT.match(n) - else (n + arr_suffix)) - , ( opt_no_underscores - , lambda n, _: RE_OPTION.match(n) - , lambda n : RE_OPTION.match(n).group('name')) - ): - if not do_it: - continue - to_rename = ( name for name, value in options.items() - if cond(name, value)) - for name in to_rename: - new_name = transform(name) - LOG().debug('Renaming option "%s" to "%s".', name, new_name) - if new_name in options: - LOG().warn( - 'Existing option named "%s" replaced with "%s".', - new_name, name) - options[new_name] = options.pop(name) - - dcl['_preprocess_options'] = _new_preprocess_options - -def _handle_fallback_command(name, bases, dcl): - """ - Process ``FALLBACK_COMMAND`` property of multiplexer command. It's turned - into a :py:meth:`~.multiplexer.LmiCommandMultiplexer.fallback_command` - class method. It needs to be called after the usage string is handled. - - .. seealso:: - :py:func:`_handle_usage` - """ - fallback = dcl.pop('FALLBACK_COMMAND', None) - if fallback is not None: - if not issubclass(type(fallback), EndPointCommandMetaClass): - raise errors.LmiCommandInvalidProperty(dcl['__module__'], name, - "FALLBACK_COMMAND must be a command class" - " (subclass of LmiEndPointCommand) not %s" % repr(fallback)) - if not fallback.has_own_usage(): - usage_string = dcl.get('__doc__', None) - if not usage_string: - for base_cls in bases: - if not issubclass(base_cls, base.LmiBaseCommand): - continue - cmd = base_cls - while not cmd.has_own_usage() and cmd.parent is not None: - cmd = cmd.parent - usage_string = cmd.__doc__ - if not usage_string: - errors.LmiCommandError(dcl['__module__'], name, - "Missing usage string.") - fallback.__doc__ = usage_string - fallback.has_own_usage = lambda cls: True - dcl['fallback_command'] = staticmethod(lambda: fallback) - -def _handle_format_options(name, bases, dcl): - """ - Process any ``FMT_*`` properties. This overrides ``format_options`` - property which returns dictionary of arguments passed to formatter factory. - - These properties are removed from class' dictionary. - """ - format_options = {} - for key, value in dcl.items(): - if key.startswith("FMT_"): - opt_name = key[4:].lower() - if opt_name not in FORMAT_OPTIONS: - raise errors.LmiCommandInvalidProperty( - dcl['__module__'], name, - 'Formatting option "%s" is not supported.' % - opt_name) - if ( opt_name in ('no_headings', 'human_friendly') - and not isinstance(value, bool)): - raise errors.LmiCommandInvalidProperty( - dcl['__module__'], name, - '"%s" property must be a boolean') - format_options[opt_name] = value - - if format_options: - def _new_format_options(self): - """ :returns: Dictionary of options for formatter object. """ - basecls = [b for b in bases if issubclass(b, base.LmiBaseCommand)][0] - opts = basecls.format_options.fget(self) - opts.update(format_options) - return opts - if 'format_options' in dcl: - raise errors.LmiCommandError(dcl['__module__'], name, - 'can not define both FMT_ options and "format_options" in' - ' the same class, choose just one of them') - dcl['format_options'] = property(_new_format_options) - - for key in format_options: - dcl.pop('FMT_' + key.upper()) - -def _handle_select(name, dcl): - """ - Process properties of :py:class:`.select.LmiSelectCommand`. - Currently handled properties are: - - ``SELECT`` : ``list`` - Is a list of pairs ``(condition, command)`` where ``condition`` is - an expression in *LMIReSpL* language. And ``command`` is either a - string with absolute path to command that shall be loaded or the - command class itself. - - Small example: :: - - SELECT = [ - ( 'OpenLMI-Hardware < 0.4.2' - , 'lmi.scripts.hardware.pre042.PreCmd' - ) - , ('OpenLMI-Hardware >= 0.4.2 & class LMI_Chassis == 0.3.0' - , HwCmd - ) - ] - - It says: Let the ``PreHwCmd`` command do the job on brokers having - ``openlmi-hardware`` package older than ``0.4.2``. Use the - ``HwCmd`` anywhere else where also the ``LMI_Chassis`` CIM class in - version ``0.3.0`` is available. - - First matching condition wins and assigned command will be passed - all the arguments. - - ``DEFAULT`` : ``str`` or :py:class:`~.base.LmiBaseCommand` - Defines fallback command used in case no condition can be - satisfied. - - They will be turned into ``get_conditionals()`` method. - """ - module_name = dcl.get('__module__', name) - if not 'SELECT' in dcl: - raise errors.LmiCommandError(module_name, name, - "Missing SELECT property.") - def inv_prop(msg, *args): - return errors.LmiCommandInvalidProperty(module_name, name, msg % args) - expressions = dcl.pop('SELECT') - if not isinstance(expressions, (list, tuple)): - raise inv_prop('SELECT must be list or tuple.') - if len(expressions) < 1: - raise inv_prop('SELECT must contain at least one condition!') - for index, item in enumerate(expressions): - if not isinstance(item, tuple): - raise inv_prop('Items of SELECT must be tuples, not %s!' % - getattr(type(item), '__name__', 'UNKNOWN')) - if len(item) != 2: - raise inv_prop('Expected pair in SELECT on index %d!' % index) - expr, cmd = item - if not isinstance(expr, basestring): - raise inv_prop('Expected expression string on index %d' - ' in SELECT!' % index) - if isinstance(cmd, basestring) and not RE_MODULE_PATH.match(cmd): - raise inv_prop('Second item of conditional pair on index %d' - ' in SELECT does not look as an importable path!' % cmd) - if ( not isinstance(cmd, basestring) - and not issubclass(cmd, (basestring, base.LmiBaseCommand))): - raise inv_prop('Expected subclass of LmiBaseCommand (or its import' - ' path) as a second item of a pair on index %d in SELECT!' - % index) - - default = dcl.pop('DEFAULT', None) - if isinstance(default, basestring) and not RE_MODULE_PATH.match(default): - raise inv_prop('DEFAULT "%s" does not look as an importable path!' - % default) - if ( default is not None and not isinstance(default, basestring) - and not issubclass(default, (basestring, base.LmiBaseCommand))): - raise inv_prop('Expected subclass of LmiBaseCommand' - ' (or its import path) as a value of DEFAULT!') - def _new_get_conditionals(self): - return expressions, default - - dcl['get_conditionals'] = _new_get_conditionals - -class EndPointCommandMetaClass(abc.ABCMeta): - """ - End point command does not have any subcommands. It's a leaf of - command tree. It wraps some function in command library being - referred to as an *associated function*. It handles following class - properties: - - ``CALLABLE`` : ``str`` or callable - An associated function. Mandatory property. - ``OWN_USAGE`` : ``bool`` or ``str`` - Usage string. Optional property. - ``ARG_ARRAY_SUFFIX`` : ``str`` - Suffix added to argument names containing array of values. - Optional property. - ``FMT_NO_HEADINGS`` : ``bool`` - Allows to force printing of table headers on and off for - this command. Default is to print them. - ``FMT_HUMAN_FRIENDLY`` : ``bool`` - Tells formatter to make the output more human friendly. The result - is dependent on the type of formatter used. - """ - - def __new__(mcs, name, bases, dcl): - _handle_usage(name, bases, dcl) - _handle_callable(name, bases, dcl) - _handle_opt_preprocess(name, dcl) - _handle_format_options(name, bases, dcl) - - cls = super(EndPointCommandMetaClass, mcs).__new__( - mcs, name, bases, dcl) - - # make additional check for arguments count - dest = getattr(cls.execute, "dest", cls.execute) - argspec = inspect.getargspec(dest) - if ( not argspec.varargs - and len(argspec.args) < cls.dest_pos_args_count()): - raise errors.LmiCommandInvalidCallable( - dcl['__module__'], name, - 'Callable must accept at least %d positional arguments' % - cls.dest_pos_args_count()) - - return cls - -class SessionCommandMetaClass(EndPointCommandMetaClass): - """ - Meta class for commands operating upon a session object. - All associated functions take as first argument an namespace abstraction - of type ``lmi.shell``. - - Handles following class properties: - - ``NAMESPACE`` : ``str`` - CIM namespace abstraction that will be passed to associated - function. Defaults to ``"root/cimv2"``. If ``False``, raw - :py:class:`lmi.shell.LMIConnection` object will be passed to - associated function. - """ - def __new__(mcs, name, bases, dcl): - _handle_usage(name, bases, dcl) - _handle_namespace(dcl) - _handle_callable(name, bases, dcl) - - return EndPointCommandMetaClass.__new__(mcs, name, bases, dcl) - -class ListerMetaClass(SessionCommandMetaClass): - """ - Meta class for end-point lister commands. Handles following class - properties: - - ``COLUMNS`` : ``tuple`` - List of column names. Optional property. There are special values - such as: - - ``None`` or omitted - Associated function provides column names in a first row of - returned list or generator. - - empty list, empty tuple or ``False`` - They mean that no headers shall be printed. It is simalar - to using ``FMT_NO_HEADINGS = True``. But in this case all - the rows returned from associated functions are treated as - data. - """ - - def __new__(mcs, name, bases, dcl): - cols = dcl.pop('COLUMNS', None) - if cols is not None: - if not isinstance(cols, (list, tuple)): - raise errors.LmiCommandInvalidProperty(dcl['__module__'], name, - 'COLUMNS class property must be either list or tuple') - if len(cols) < 1 or cols is False: - dcl['FMT_NO_HEADINGS'] = True - cols = tuple() - elif not all(isinstance(c, basestring) for c in cols): - raise errors.LmiCommandInvalidProperty(dcl['__module__'], name, - 'COLUMNS must contain just column names as strings') - def _new_get_columns(_cls): - """ Return column names. """ - return cols - dcl['get_columns'] = classmethod(_new_get_columns) - - return super(ListerMetaClass, mcs).__new__(mcs, name, bases, dcl) - -class ShowInstanceMetaClass(SessionCommandMetaClass): - """ - Meta class for end-point show instance commands. Additional handled - properties: - - ``DYNAMIC_PROPERTIES`` : ``bool`` - Whether the associated function itself provides list of - properties. Optional property. - ``PROPERTIES`` : ``tuple`` - List of instance properties to print. Optional property. - - These are translated in a :py:meth:`~.show.LmiShowInstance.render`, which - should be marked as abstract in base lister class. - """ - - def __new__(mcs, name, bases, dcl): - _handle_render_properties(name, bases, dcl) - - return super(ShowInstanceMetaClass, mcs).__new__( - mcs, name, bases, dcl) - -class InstanceListerMetaClass(SessionCommandMetaClass): - """ - Meta class for instance lister command handling the same properties - as :py:class:`ShowInstanceMetaClass`. - """ - - def __new__(mcs, name, bases, dcl): - _handle_render_properties(name, bases, dcl, True) - - return super(InstanceListerMetaClass, mcs).__new__( - mcs, name, bases, dcl) - -class CheckResultMetaClass(SessionCommandMetaClass): - """ - Meta class for end-point command "check result". Additional handled - properties: - - ``EXPECT`` : - Value to compare against the return value. Mandatory property. - - ``EXPECT`` property is transformed into a - :py:meth:`.checkresult.LmiCheckResult.check_result` method taking two - arguments ``(options, result)`` and returning a boolean. - """ - - def __new__(mcs, name, bases, dcl): - try: - expect = dcl['EXPECT'] - if callable(expect): - def _new_expect(_self, options, result): - """ - Comparison function testing return value with *expect* - function. - """ - if isinstance(result, LMIReturnValue): - result = result.rval - passed = expect(options, result) - if not passed: - LOG().info('Got unexpected result "%s".') - return passed - else: - def _new_expect(_self, _options, result): - """ Comparison function testing by equivalence. """ - if isinstance(result, LMIReturnValue): - result = result.rval - passed = expect == result - if not passed: - LOG().info('Expected "%s", got "%s".', expect, result) - return (False, '%s != %s' % (expect, result)) - return passed - _new_expect.expected = expect - del dcl['EXPECT'] - dcl['check_result'] = _new_expect - except KeyError: - # EXPECT might be defined in some subclass - pass - - return super(CheckResultMetaClass, mcs).__new__(mcs, name, bases, dcl) - -class MultiplexerMetaClass(abc.ABCMeta): - """ - Meta class for node command (not an end-point command). It handles - following class properties: - - ``COMMANDS`` : ``dict`` - Command names with assigned command classes. Each of them is a - direct subcommands of command with this property. Mandatory - property. - - ``FALLBACK_COMMAND`` : :py:class:`~.endpoint.LmiEndPointCommand` - Command factory to use in case that no command is passed on command - line. - - Formatting options (starting with ``FMT_`` are also accepted, and may used - to set defaults for all subcommands. - """ - - def __new__(mcs, name, bases, dcl): - if dcl.get('__metaclass__', None) is not MultiplexerMetaClass: - module_name = dcl.get('__module__', name) - # check COMMANDS property and make it a classmethod - if not 'COMMANDS' in dcl: - raise errors.LmiCommandError(module_name, name, - 'Missing COMMANDS property.') - cmds = dcl.pop('COMMANDS') - if not isinstance(cmds, dict): - raise errors.LmiCommandInvalidProperty(module_name, name, - 'COMMANDS must be a dictionary') - if not all(isinstance(c, basestring) for c in cmds.keys()): - raise errors.LmiCommandInvalidProperty(module_name, name, - 'Keys of COMMANDS dictionary must contain command' - ' names as strings.') - for cmd_name, cmd in cmds.items(): - if not util.RE_COMMAND_NAME.match(cmd_name): - raise errors.LmiCommandInvalidName( - module_name, name, cmd_name) - if not issubclass(cmd, base.LmiBaseCommand): - raise errors.LmiCommandError(module_name, name, - 'COMMANDS dictionary must be composed of' - ' LmiBaseCommand subclasses, failed class: "%s"' - % cmd.__name__) - if cmd.is_multiplexer() and not cmd.has_own_usage(): - LOG().warn('Command "%s.%s" is missing usage string.' - ' It will be inherited from parent command.', - cmd.__module__, cmd.__name__) - cmd.__doc__ = dcl['__doc__'] - def _new_child_commands(_cls): - """ Returns list of subcommands. """ - return cmds - dcl['child_commands'] = classmethod(_new_child_commands) - - _handle_usage(name, bases, dcl) - _handle_fallback_command(name, bases, dcl) - _handle_format_options(name, bases, dcl) - - return super(MultiplexerMetaClass, mcs).__new__(mcs, name, bases, dcl) - -class SelectMetaClass(abc.ABCMeta): - """ - Meta class for select commands with guarded commands. Additional handled - properties: - - ``SELECT`` : ``list`` - List of commands guarded with expressions representing requirements - on server's side that need to be met. - ``DEFAULT`` : ``str`` or :py:class:`~.base.LmiBaseCommand` - Defines fallback command used in case no condition can is - satisfied. - """ - - def __new__(mcs, name, bases, dcl): - if dcl.get('__metaclass__', None) is not SelectMetaClass: - module_name = dcl.get('__module__', name) - if not '__doc__' in dcl: - LOG().warn('Command selector "%s.%s" is missing short' - ' description string (__doc__).', - module_name, name) - default = dcl.get('DEFAULT', None) - if ( default is not None - and issubclass(default, base.LmiBaseCommand) - and getattr(dcl['DEFAULT'], '__doc__', None)): - LOG().warn('Using __doc__ string from default command for' - ' selector "%s.%s".', module_name, name) - dcl['__doc__'] = dcl['DEFAULT'].__doc__ - _handle_select(name, dcl) - return super(SelectMetaClass, mcs).__new__(mcs, name, bases, dcl) - diff --git a/lmi/scripts/common/command/multiplexer.py b/lmi/scripts/common/command/multiplexer.py deleted file mode 100644 index 61ebcd7..0000000 --- a/lmi/scripts/common/command/multiplexer.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Defines command class used to nest multiple commands under one. -""" - -from docopt import docopt - -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import base -from lmi.scripts.common.command import meta - -LOG = get_logger(__name__) - -class LmiCommandMultiplexer(base.LmiBaseCommand): - """ - Base class for node commands. It consumes just part of command line - arguments and passes the remainder to one of its subcommands. - - Example usage: :: - - class MyCommand(LmiCommandMultiplexer): - ''' - My command description. - - Usage: %(cmd)s mycommand (subcmd1 | subcmd2) - ''' - COMMANDS = {'subcmd1' : Subcmd1, 'subcmd2' : Subcmd2} - - Where ``Subcmd1`` and ``Subcmd2`` are some other ``LmiBaseCommand`` - subclasses. Documentation string must be parseable with ``docopt``. - - Recognized properties: - - ``COMMANDS`` : ``dictionary`` - property will be translated to - :py:meth:`LmiCommandMultiplexer.child_commands` class method by - :py:class:`~.meta.MultiplexerMetaClass`. - - Using metaclass: :py:class:`.meta.MultiplexerMetaClass`. - """ - __metaclass__ = meta.MultiplexerMetaClass - - @classmethod - def child_commands(cls): - """ - Abstract class method, that needs to be implemented in subclass. - This is done by associated meta-class, when defining a command with - assigned ``COMMANDS`` property. - - :returns: Dictionary of subcommand names with assigned command - factories. - :rtype: dictionary - """ - raise NotImplementedError("child_commands must be implemented in" - " a subclass") - - @classmethod - def fallback_command(cls): - """ - This is overriden by :py:class:`~.meta.MultiplexerMetaClass` when - the ``FALLBACK_COMMAND`` gets processed. - - :returns: Command factory invoked for missing command on command line. - :rtype: :py:class:`~.endpoint.LmiEndPointCommand` - """ - return None - - @classmethod - def is_end_point(cls): - return False - - def run_subcommand(self, cmd_name, args): - """ - Pass control to a subcommand identified by given name. - - :param string cmd_name: Name of direct subcommand, whose - :py:meth:`~.base.LmiBaseCommand.run` method shall be invoked. - :param list args: List of arguments for particular subcommand. - :returns: Application exit code. - :rtype: integer - """ - if not isinstance(cmd_name, basestring): - raise TypeError("cmd_name must be a string, not %s" % - repr(cmd_name)) - if not isinstance(args, (list, tuple)): - raise TypeError("args must be a list, not %s" % repr(args)) - try: - cmd_cls = self.child_commands()[cmd_name] - cmd = cmd_cls(self.app, cmd_name, self) - except KeyError: - self.app.stderr.write(self.get_usage()) - LOG().critical('Unexpected command "%s".', cmd_name) - return 1 - return cmd.run(args) - - def run(self, args): - """ - Handle optional parameters, retrieve desired subcommand name and - pass the remainder of arguments to it. - - :param list args: List of arguments with at least subcommand name. - """ - if not isinstance(args, (list, tuple)): - raise TypeError("args must be a list") - full_args = self.get_cmd_name_parts(for_docopt=True) + args - docopt_kwargs = { - # check the --help ourselves (the default docopt behaviour checks - # also for --version) - 'help' : False, - # let's ignore options following first command for generated - # usage string and when a height of this branch is > 2 - 'options_first' : not self.has_own_usage() - or any( not cmd.is_end_point() - for cmd in self.child_commands().values() - if not args or args[0] not in self.child_commands() - or self.child_commands()[args[0]] is cmd) - } - options = docopt(self.get_usage(), full_args, **docopt_kwargs) - if options.pop('--help', False) or (args and args[0] == '--help'): - self.app.stdout.write(self.get_usage(proper=True)) - return 0 - if ( self.fallback_command() is not None - and (not args or args[0] not in self.child_commands())): - cmd_cls = self.fallback_command() - cmd = cmd_cls(self.app, self.cmd_name, self.parent) - return cmd.run(args) - return self.run_subcommand(args[0], args[1:]) diff --git a/lmi/scripts/common/command/select.py b/lmi/scripts/common/command/select.py deleted file mode 100644 index 5f89994..0000000 --- a/lmi/scripts/common/command/select.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Defines command class used to choose other commands depending on -profile and class requirements. -""" - -from docopt import docopt -from pyparsing import ParseException - -from lmi.scripts.common import get_logger -from lmi.scripts.common import errors -from lmi.scripts.common.command import base -from lmi.scripts.common.command import meta -from lmi.scripts.common.session import SessionProxy -from lmi.scripts.common.versioncheck import eval_respl - -class LmiSelectCommand(base.LmiBaseCommand): - """ - Base class for command selectors. It does not process command line - arguments. Thery are passed unchanged to selected command whose - requirements are met. Its doc string is not interpreted in any way. - - If there are more hosts, conditions are evaluated per each. They are then - split into groups, each fulfilling particular condition. Associated - commands are then invoked on these groups separately. - - Example usage: :: - - class MySelect(LmiSelectCommand): - SELECT = [ - ( 'OpenLMI-Hardware >= 0.4.2' - , 'lmi.scripts.hardware.current.Cmd'), - ('OpenLMI-Hardware', 'lmi.scripts.hardware.pre042.Cmd') - ] - DEFAULT = MissingHwProviderCmd - - Using metaclass: :py:class:`.meta.SelectMetaClass`. - """ - __metaclass__ = meta.SelectMetaClass - - @classmethod - def is_end_point(cls): - return False - - @classmethod - def is_multiplexer(cls): - return False - - @classmethod - def get_conditionals(cls): - """ - Get the expressions with associated commands. This shall be overriden - by a subclass. - - :returns: Pair of ``(expressions, default)``. - Where ``expressions`` is a list of pairs ``(condition, command)``. - And ``default`` is the same as ``command`` used in case no - ``condition`` is satisfied. - :rtype: list - """ - raise NotImplementedError( - "get_conditionals needs to be defined in subclass") - - def eval_expr(self, expr, hosts, cache=None): - """ - Evaluate expression on group of hosts. - - :param string expr: Expression to evaluate. - :param list hosts: Group of hosts that shall be checked. - :param dictionary cache: Optional cache object speeding up evaluation - by reducing number of queries to broker. - :returns: Subset of hosts satisfying *expr*. - :rtype: list - """ - if cache is None: - cache = dict() - session = self.session - satisfied = [] - try: - for host in hosts: # TODO: could be done concurrently - conn = session[host] - if not conn: - continue - if eval_respl(expr, conn, cache=cache): - satisfied.append(host) - except ParseException: - raise errors.LmiBadSelectExpression(self.__class__.__module__, - self.__class__.__name__, "Bad select expression: %s" % expr) - return satisfied - - def select_cmds(self, cache=None): - """ - Generator of command factories with associated groups of hosts. It - evaluates given expressions on session. In this process all expressions - from :py:meth:`get_conditionals` are checked in a row. Host satisfying - some expression is added to group associated with it and is excluded - from processing following expressions. - - :param dictionary cache: Optional cache object speeding up the evaluation - by reducing number of queries to broker. - :returns: Pairs in form ``(command_factory, session_proxy)``. - :rtype: generator - :raises: - * :py:class:`~lmi.scripts.common.errors.LmiUnsatisfiedDependencies` - if no condition is satisfied for at least one host. Note that - this exception is raised at the end of evaluation. This lets - you choose whether you want to process satisfied hosts - by - processing the generator at once. Or whether you want to be - sure it is satisfied by all of them - you turn the generator - into a list. - * :py:class:`~lmi.scripts.common.errors.LmiNoConnections` - if no successful connection was done. - """ - if cache is None: - cache = dict() - conds, default = self.get_conditionals() - def get_cmd_factory(cmd): - if isinstance(cmd, basestring): - i = cmd.rindex('.') - module = __import__(cmd[:i], fromlist=cmd[i+1:]) - return getattr(module, cmd[i+1:]) - else: - return cmd - - session = self.session - unsatisfied = set(session.hostnames) - - for expr, cmd in conds: - hosts = self.eval_expr(expr, unsatisfied, cache) - if hosts: - yield get_cmd_factory(cmd), SessionProxy(session, hosts) - hosts = set(hosts).union(set(session.get_unconnected())) - unsatisfied.difference_update(hosts) - if not unsatisfied: - break - if default is not None and unsatisfied: - yield get_cmd_factory(default), SessionProxy(session, unsatisfied) - unsatisfied.clear() - if len(unsatisfied): - raise errors.LmiUnsatisfiedDependencies(unsatisfied) - if len(session) == len(session.get_unconnected()): - raise errors.LmiNoConnections("No successful connection!") - - def get_usage(self, proper=False): - """ - Try to get usage of any command satisfying some expression. - - :raises: Same exceptions as :py:meth:`select_cmds`. - """ - for cmd_cls, _ in self.select_cmds(): - cmd = cmd_cls(self.app, self.cmd_name, self.parent) - return cmd.get_usage(proper) - - def run(self, args): - """ - Iterate over command factories with associated sessions and - execute them with unchanged *args*. - """ - result = 0 - for cmd_cls, session in self.select_cmds(): - cmd = cmd_cls(self.app, self.cmd_name, self.parent) - cmd.set_session_proxy(session) - ret = cmd.run(args) - if result == 0: - result = ret - return result - diff --git a/lmi/scripts/common/command/session.py b/lmi/scripts/common/command/session.py deleted file mode 100644 index 9d88bbe..0000000 --- a/lmi/scripts/common/command/session.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Defines a base class for all command classes operating upon a -:py:class:`~lmi.scripts.common.session.Session` object. -""" -import abc -from collections import OrderedDict - -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import meta -from lmi.scripts.common.command.endpoint import LmiEndPointCommand -from lmi.scripts.common.configuration import Configuration -from lmi.scripts.common.session import Session -from lmi.shell import LMIConnection -from lmi.shell import LMIUtil - -LOG = get_logger(__name__) - -class LmiSessionCommand(LmiEndPointCommand): - """ - Base class for end-point commands operating upon a session object. - - Using metaclass: :py:class:`~.meta.SessionCommandMetaClass`. - """ - __metaclass__ = meta.SessionCommandMetaClass - - @classmethod - def cim_namespace(cls): - """ - This returns default cim namespace, the connection object will be - nested into before being passed to associated function. - It can be overriden in few ways: - - 1. by setting ``[CIM] Namespace`` option in configuration - 2. by giving ``--namespace`` argument on command line to the - ``lmi`` meta-command - 3. by setting ``NAMESPACE`` property in declaration of command - - Higher number means higher priority. - """ - return Configuration.get_instance().namespace - - @classmethod - def dest_pos_args_count(cls): - """ - There is a namespace/connection object passed as the first positional - argument. - """ - return LmiEndPointCommand.dest_pos_args_count.im_func(cls) + 1 - - def process_session(self, session, args, kwargs): - """ - Process each host of given session, call the associated command - function, collect results and print it to standard output. - - :param session: Session object with set of hosts. - :type session: :py:class:`~lmi.scripts.common.session.Session` - :param list args: Positional arguments to pass to associated function - in command library. - :param dictionary kwargs: Keyword arguments as a dictionary. - :returns: Exit code of application. - :rtype: integer - """ - if not isinstance(session, Session): - raise TypeError("session must be an object of Session, not %s" - % repr(session)) - # { ( hostname : (passed, result) ), ... } - # where passed is a boolean and result is returned value if passed is - # True and exception otherwise - results = OrderedDict() - for connection in session: - try: - result = self.take_action(connection, args, kwargs) - # result may be a generator which may throw in the following - # function - results[connection.uri] = (True, result) - self.process_host_result(connection.uri, True, result) - except Exception as exc: - if len(session) > 1: - LOG().exception('Invocation failed for host "%s": %s', - connection.uri, exc) - else: - LOG().exception(exc) - results[connection.uri] = (False, exc) - self.process_host_result(connection.uri, False, exc) - self.process_session_results(session, results) - return all(r[0] for r in results.values()) - - def process_host_result(self, hostname, success, result): - """ - Called from :py:meth:`process_session` after single host gets - processed. By default this prints obtained *result* with default - formatter if the execution was successful. Children of this class may - want to override this. - - :param string hostname: Name of host involved. - :param boolean success: Whether the action on host succeeded. - :param result: Either the value returned by associated function upon a - successful invocation or an exception. - """ - if success: - if len(self.session) > 1: - self.formatter.print_host(hostname) - self.produce_output(result) - - def process_session_results(self, session, results): - """ - Called at the end of :py:meth:`process_session`'s execution. It's - supposed to do any summary work upon results from all hosts. By default - it just prints errors in a form of list. - - :param session: Session object. - :type session: :py:class:`lmi.scripts.common.session.Session` - :param dictionary results: Dictionary of form: :: - - { 'hostname' : (success_flag, result), ... } - - where *result* is either an exception or returned value of - associated function, depending on *success_flag*. See the - :py:meth:`process_host_result`. - """ - if not isinstance(session, Session): - raise TypeError("session must be a Session object") - if not isinstance(results, dict): - raise TypeError("results must be a dictionary") - # check whether any output has been produced - if ( len(session.get_unconnected()) - or any(not r[0] for r in results.values())): - data = [] - for hostname in session.get_unconnected(): - data.append((hostname, ['failed to connect'])) - for hostname, (success, error) in results.items(): - if not success: - data.append((hostname, [error])) - if len(session) > 1: - self._print_errors(data, - new_line=any(r[0] for r in results.values())) - - @abc.abstractmethod - def take_action(self, connection, args, kwargs): - """ - Executes an action on single host and collects results. - - :param connection: Connection to a single host. - :type connection: :py:class:`lmi.shell.LMIConnection` - :param list args: Positional arguments for associated function. - :param dictionary kwargs: Keyword arguments for associated function. - :returns: Column names and item list as a pair. - :rtype: tuple - """ - raise NotImplementedError("take_action must be implemented in subclass") - - def execute_on_connection(self, connection, *args, **kwargs): - """ - Wraps the :py:meth:`~.endpoint.LmiEndPointCommand.execute` method with - connection adjustments. Connection object is not usually passed - directly to associated function. Mostly it's the namespace object that - is expected. This method checks, whether the namespace object is - desired and modifies connection accordingly. - - :param connection: Connection to single host. - :type connection: :py:class:`lmi.shell.LMIConnection` - :param list args: Arguments handed over to associated function. - :param dictionary kwargs: Keyword arguments handed over to associated - function. - """ - if not isinstance(connection, LMIConnection): - raise TypeError("expected an instance of LMIConnection for" - " connection argument, not %s" % repr(connection)) - namespace = self.cim_namespace() - if namespace is not None: - connection = LMIUtil.lmi_wrap_cim_namespace( - connection, namespace) - return self.execute(connection, *args, **kwargs) - - def run_with_args(self, args, kwargs): - return self.process_session(self.session, args, kwargs) - diff --git a/lmi/scripts/common/command/show.py b/lmi/scripts/common/command/show.py deleted file mode 100644 index 1698fec..0000000 --- a/lmi/scripts/common/command/show.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Contains command classes producing key-value pairs to output. -""" -import abc - -from lmi.scripts.common import formatter -from lmi.scripts.common import get_logger -from lmi.scripts.common.command import meta -from lmi.scripts.common.command.session import LmiSessionCommand - -LOG = get_logger(__name__) - -class LmiShowInstance(LmiSessionCommand): - """ - End point command producing a list of properties of particular CIM - instance. Either reduced list of properties to print can be specified, or - the associated function alone can decide, which properties shall be - printed. Associated function is expected to return CIM instance as a - result. - - List of additional recognized properties: - - ``DYNAMIC_PROPERTIES`` : ``bool`` - A boolean saying, whether the associated function alone shall - specify the list of properties of rendered instance. If ``True``, - the result of function must be a pair: :: - - (props, inst) - - Where props is the same value as can be passed to ``PROPERTIES`` - property. Defaults to ``False``. - ``PROPERTIES`` : ``tuple`` - May contain list of instance properties, that will be produced in - the same order as output. Each item of list can be either: - - name : ``str`` - Name of property to render. - pair : ``tuple`` - A tuple ``(Name, render_func)``, where former item an - arbitraty name for rendered value and the latter is a - function taking as the only argument particular instance - and returning value to render. - - ``DYNAMIC_PROPERTIES`` and ``PROPERTIES`` are mutually exclusive. If none - is given, all instance properties will be printed. - - Using metaclass: :py:class:`~.meta.ShowInstanceMetaClass`. - """ - __metaclass__ = meta.ShowInstanceMetaClass - - def formatter_factory(self): - return formatter.SingleFormatter - - @abc.abstractmethod - def render(self, result): - """ - This method can either be overriden in a subclass or left alone. In the - latter case it will be generated by - :py:class:`~.meta.ShowInstanceMetaClass` metaclass with regard to - ``PROPERTIES`` and ``DYNAMIC_PROPERTIES``. - - :param result: Either an instance to - render or pair of properties and instance. - :type: :py:class:`lmi.shell.LMIInstance` or ``tuple`` - :returns: List of pairs, where the first item is a label and second a - value to render. - :rtype: list - """ - raise NotImplementedError( - "render method must be overriden in subclass") - - def take_action(self, connection, args, kwargs): - """ - Process single connection to a host, render result and return a value - to render. - - :returns: List of pairs, where the first item is a label and - second a value to render. - :rtype: list - """ - res = self.execute_on_connection(connection, *args, **kwargs) - return self.render(res) - diff --git a/lmi/scripts/common/command/util.py b/lmi/scripts/common/command/util.py deleted file mode 100644 index 914850a..0000000 --- a/lmi/scripts/common/command/util.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Utility functions used in command sub-package. -""" - -import inspect -import os -import re - -#: Regular expression matching bracket argument such as ``<arg_name>``. -RE_OPT_BRACKET_ARGUMENT = re.compile('^<(?P<name>[^>]+)>$') -#: Regular expression matching argument written in upper case such as -#:``ARG_NAME``. -RE_OPT_UPPER_ARGUMENT = re.compile('^(?P<name>[A-Z0-9]+(?:[_-][A-Z0-9]+)*)$') -#: Regular expression matching showt options. They are one character -#: long, prefixed with single dash. -RE_OPT_SHORT_OPTION = re.compile('^-(?P<name>[a-z])$', re.IGNORECASE) -#: Regular expression matching long options (prefixed with double dash). -RE_OPT_LONG_OPTION = re.compile('^--(?P<name>[a-z0-9_-]+)$', re.IGNORECASE) -#: Command name can also be a single or double dash. -RE_COMMAND_NAME = re.compile(r'^([a-z]+(-[a-z0-9]+)*|--?)$') - -def is_abstract_method(clss, method, missing_is_abstract=False): - """ - Check, whether the given method is abstract in given class or list of - classes. May be used to check, whether we should override particular - abstract method in a meta-class in case that no non-abstract - implementation is defined. - - :param clss: Class or list of classes that is - searched for non-abstract implementation of particular method. - If the first class having particular method in this list contain - non-abstract implementation, ``False`` is returned. - :type clss: type or tuple - :param string method: Name of method to look for. - :param boolean missing_is_abstract: This is a value returned, when - not such method is defined in a set of given classes. - :returns: Are all occurences of given method abstract? - :rtype: boolean - """ - if ( not isinstance(clss, (list, tuple, set)) - and not isinstance(clss, type)): - raise TypeError("clss must be either a class or a tuple of classes") - if not isinstance(method, basestring): - raise TypeError("method must be a string") - if isinstance(clss, type): - clss = [clss] - for cls in clss: - if hasattr(cls, method): - if getattr(getattr(cls, method), "__isabstractmethod__", False): - return True - else: - return False - return missing_is_abstract - -def get_module_name(frame_level=2): - """ - Get a module name of caller from particular outer frame. - - :param integer frame_level: Number of nested frames to skip when searching - for called function scope by inspecting stack upwards. When the result - of this function is applied directly on the definition of function, - it's value should be 1. When used from inside of some other factory, it - must be increased by 1. - - Level 0 returns name of this module. Level 1 returns module name of - caller. Level 2 returns module name of caller's caller. - :returns: Module name. - :rtype: string - """ - frame = inspect.currentframe() - while frame_level > 0 and frame.f_back: - frame = frame.f_back - frame_level -= 1 - module = getattr(frame, 'f_globals', {}).get('__name__', None) - if module is None: - if hasattr(frame, 'f_code'): - module = os.path.basename(frame.f_code.co_filename.splitext())[0] - else: - module = '_unknown_' - return module diff --git a/lmi/scripts/common/configuration.py b/lmi/scripts/common/configuration.py deleted file mode 100644 index 97ebebe..0000000 --- a/lmi/scripts/common/configuration.py +++ /dev/null @@ -1,311 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module for Configuration class. - -Configuration ---------------------- - -.. autoclass:: Configuration - :members: - -""" - -import os -from lmi.base.BaseConfiguration import BaseConfiguration - -LISTER_FORMATS = ['csv', 'table'] - -#: Default format string to use in stderr handlers. -DEFAULT_FORMAT_STRING = "%(cseq)s%(levelname_)-8s:%(creset)s %(message)s" - -class Configuration(BaseConfiguration): - """ - Configuration class specific to software providers. - *OpenLMI* configuration file should reside in: :: - - /etc/openlmi/scripts/lmi.conf - - :param string user_config_file_path: Path to the user configuration - options. - """ - - USER_CONFIG_FILE_PATH = "~/.lmirc" - HISTORY_FILE = "~/.lmi_history" - - OUTPUT_SILENT = -1 - OUTPUT_WARNING = 0 - OUTPUT_INFO = 1 - OUTPUT_DEBUG = 2 - - # indexes to LISTER_FORMATS - LISTER_FORMAT_CSV = 0 - LISTER_FORMAT_TABLE = 1 - - def __init__(self, user_config_file_path=USER_CONFIG_FILE_PATH, **kwargs): - self._user_config_file_path = os.path.expanduser(user_config_file_path) - BaseConfiguration.__init__(self, **kwargs) - self._verbosity = None - self._trace = None - self._verify_server_cert = None - self._cim_namespace = None - self._human_friendly = None - self._lister_format = None - self._no_headings = None - self._log_file = None - self._history_max_length = None - - @classmethod - def provider_prefix(cls): - return "scripts" - - @classmethod - def default_options(cls): - """ - :returns: Dictionary of default values. - :rtype: dictionary - """ - defaults = BaseConfiguration.default_options().copy() - # [Main] options - defaults["CommandNamespace"] = 'lmi.scripts.cmd' - defaults["Trace"] = "False" - defaults["Verbosity"] = "0" - defaults["HistoryMaxLength"] = "4000" - # [Log] options - defaults['ConsoleFormat'] = DEFAULT_FORMAT_STRING - defaults['ConsoleInfoFormat'] = '%(message)s' - defaults['FileFormat'] = \ - "%(asctime)s:%(levelname)-8s:%(name)s:%(lineno)d - %(message)s" - defaults['LogToConsole'] = 'True' - defaults['OutputFile'] = '' - # [SSL] options - defaults['VerifyServerCertificate'] = 'True' - # [Format] options - defaults['HumanFriendly'] = 'False' # be ugly by default - defaults['ListerFormat'] = 'table' - defaults['NoHeadings'] = 'False' - return defaults - - @classmethod - def mandatory_sections(cls): - sects = set(BaseConfiguration.mandatory_sections()) - sects.add('Main') - sects.add('SSL') - sects.add('Format') - return list(sects) - - def load(self): - """ Read additional user configuration file if it exists. """ - BaseConfiguration.load(self) - self.config.read(self._user_config_file_path) - - # ************************************************************************* - # [CIM] options - # ************************************************************************* - @property - def namespace(self): - if self._cim_namespace is None: - return BaseConfiguration.namespace.fget(self) - return self._cim_namespace - @namespace.setter - def namespace(self, namespace): - if not isinstance(namespace, basestring) and namespace is not None: - raise TypeError("namespace must be a string") - self._cim_namespace = namespace - - # ************************************************************************* - # [Main] options - # ************************************************************************* - @property - def history_file(self): - """ Path to a file with history of interactive mode. """ - return os.path.expanduser(self.HISTORY_FILE) - - @property - def history_max_length(self): - """ Maximum number of lines kept in history file. """ - return self.get_safe('Main', 'HistoryMaxLength', int) - - @property - def silent(self): - """ Whether to suppress all output messages except for errors. """ - return self.verbosity <= self.OUTPUT_SILENT - - @property - def trace(self): - """ Whether the tracebacks shall be printed upon errors. """ - if self._trace is not None: - return self._trace - return self.get_safe('Main', 'Trace', bool) - - @trace.setter - def trace(self, trace): - """ Allow to override configuration option Trace. """ - if trace is not None: - trace = bool(trace) - self._trace = trace - - @property - def verbose(self): - """ Whether to output more details. """ - return self.verbosity >= self.OUTPUT_INFO - - @property - def verbosity(self): - """ Return integer indicating verbosity level of output to console. """ - if self._verbosity is None: - return self.get_safe('Main', 'Verbosity', int) - return self._verbosity - - @verbosity.setter - def verbosity(self, level): - """ Allow to set verbosity without modifying configuration values. """ - if not isinstance(level, (long, int)) and level is not None: - raise TypeError("level must be integer") - if level is not None: - if level < self.OUTPUT_SILENT: - level = self.OUTPUT_SILENT - elif level > self.OUTPUT_DEBUG: - level = self.OUTPUT_DEBUG - self._verbosity = level - - # ************************************************************************* - # [Log] options - # ************************************************************************* - @property - def log_file(self): - """ Path to a file, where logging messages shall be written. """ - if self._log_file is None: - return self.get_safe('Log', 'OutputFile') - return self._log_file - @log_file.setter - def log_file(self, log_file): - """ Override logging file path. """ - if log_file is not None and not isinstance(log_file, basestring): - raise TypeError("log_file must be a string") - self._log_file = log_file - - # ************************************************************************* - # [SSL] options - # ************************************************************************* - @property - def verify_server_cert(self): - """ - Return boolean saying, whether the server-side certificate should be - checked. - """ - if self._verify_server_cert is None: - return self.get_safe('SSL', 'VerifyServerCertificate', bool) - return self._verify_server_cert - @verify_server_cert.setter - def verify_server_cert(self, value): - """ Allows to override configuration option value. """ - if value is not None: - value = bool(value) - self._verify_server_cert = value - - # ************************************************************************* - # [Format] options - # ************************************************************************* - @property - def human_friendly(self): - """ Whether to print human-friendly values. """ - if self._human_friendly is None: - return self.get_safe('Format', 'HumanFriendly', bool) - return self._human_friendly - @human_friendly.setter - def human_friendly(self, value): - """ Allows to override configuration option value. """ - if value is not None: - value = bool(value) - self._human_friendly = value - - @property - def lister_format(self): - """ - Output format used for lister commands. Returns one of - * LISTER_FORMAT_CSV - * LISTER_FORMAT_TABLE - - :rtype: integer - """ - if self._lister_format is None: - value = self.get_safe('Format', 'ListerFormat') - try: - return LISTER_FORMATS.index(value.lower()) - except ValueError: - value = self.default_options()['ListerFormat'] - return LISTER_FORMATS.index(value.lower()) - return self._lister_format - @lister_format.setter - def lister_format(self, value): - """ - Allows to override configuration option. - - :param value: One of items from ``LISTER_FORMATS`` array or an index - to it. - :type value: integer or string - """ - if ( value is not None - and ( not isinstance(value, int) - or (value < 0 or value >= len(LISTER_FORMATS))) - and ( not isinstance(value, basestring) - or value.lower() not in LISTER_FORMATS)) : - raise TypeError("value must be an integer or one of: %s" % - LISTER_FORMATS) - if isinstance(value, basestring): - value = LISTER_FORMATS.index(value.lower()) - self._lister_format = value - - @property - def no_headings(self): - """ Whether to print headings of tables. """ - if self._no_headings is None: - return self.get_safe('Format', 'NoHeadings', bool) - return self._no_headings - @no_headings.setter - def no_headings(self, value): - """ Allows to override configuration option. """ - if value is not None: - value = bool(value) - self._no_headings = value - -# There were some path changes in BaseConfiguration after 0.2.0 release. -# Let's fallback to older variable name when the new one is not present. -if hasattr(BaseConfiguration, 'CONFIG_DIRECTORY_TEMPLATE_PROVIDER'): - setattr( Configuration - , 'CONFIG_FILE_PATH_TEMPLATE_PROVIDER' - , getattr(BaseConfiguration, 'CONFIG_DIRECTORY_TEMPLATE_PROVIDER') - + 'lmi.conf') -else: # fallback - setattr( Configuration - , 'CONFIG_FILE_PATH_TEMPLATE' - , getattr(BaseConfiguration, 'CONFIG_DIRECTORY_TEMPLATE') - + 'lmi.conf') diff --git a/lmi/scripts/common/errors.py b/lmi/scripts/common/errors.py deleted file mode 100644 index 5162cf8..0000000 --- a/lmi/scripts/common/errors.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module with predefined exceptions for use in scripts. -""" - -class LmiError(Exception): - """ - The base Lmi scripts error. - All the other exceptions inherit from it. - """ - pass - -class LmiFailed(LmiError): - """ - Raised, when operation on remote host failes. - It's supposed to be used especially in command libraries. - """ - pass - -class LmiUnsatisfiedDependencies(LmiFailed): - """ - Raised when no guarded command in - :py:class:`~.command.select.LmiSelectCommand` can be loaded due to - unsatisfied requirements. - """ - def __init__(self, uris): - LmiFailed.__init__(self, "Profile and class dependencies were not" - " satisfied for this session (%s)." % ', '.join(uris)) - -class LmiUnexpectedResult(LmiError): - """ - Raised, when command's associated function returns something unexpected. - """ - def __init__(self, command_class, expected, result): - LmiError.__init__(self, - 'Got unexpected result from associated function of' - ' "%s.%s", expected "%s", got: "%s".' % - (command_class.__module__, command_class.__name__, - expected, repr(result))) - -class LmiInvalidOptions(LmiError): - """ - Raised in :py:meth:`~.command.endpoint.LmiEndPointCommand.verify_options` - method if the options given are not valid. - """ - pass - -class LmiCommandNotFound(LmiError): - """ Raised, when user requests not registered command. """ - def __init__(self, cmd_name): - LmiError.__init__(self, 'No such command "%s".' % cmd_name) - -class LmiNoConnections(LmiError): - """ Raised, when no connection to remote hosts could be made. """ - pass - -class LmiCommandImportError(LmiError): - """ Exception raised when command can not be imported. """ - def __init__(self, cmd_name, cmd_path, reason): - LmiError.__init__(self, 'Failed to import command "%s" (%s): %s' % ( - cmd_name, cmd_path, reason)) - -class LmiCommandError(LmiError): - """ Generic exception related to command declaration. """ - def __init__(self, module_name, class_name, msg): - LmiError.__init__(self, 'Wrong declaration of command "%s": %s' - % ( ".".join([module_name, class_name]) - if module_name else class_name - , msg)) - -class LmiCommandInvalidName(LmiCommandError): - """ Raised, when command gets invalid name. """ - def __init__(self, module_name, class_name, cmd_name): - LmiCommandError.__init__(self, module_name, class_name, - 'Invalid command name "%s".' % cmd_name) - -class LmiCommandMissingCallable(LmiCommandError): - """ Raised, when command declaration is missing callable object. """ - def __init__(self, module_name, class_name): - LmiCommandError.__init__(self, module_name, class_name, - 'Missing CALLABLE property.') - -class LmiCommandInvalidProperty(LmiCommandError): - """ Raised, when any command property contains unexpected value. """ - pass - -class LmiImportCallableFailed(LmiCommandInvalidProperty): - """ Raised, when callable object of command could not be imported. """ - def __init__(self, module_name, class_name, callable_prop): - LmiCommandInvalidProperty.__init__(self, module_name, class_name, - 'Failed to import callable "%s".' % callable_prop) - -class LmiCommandInvalidCallable(LmiCommandInvalidProperty): - """ Raised, when given callback is not callable. """ - def __init__(self, module_name, class_name, msg): - LmiCommandInvalidProperty.__init__(self, module_name, class_name, msg) - -class LmiBadSelectExpression(LmiCommandError): - """ - Raised, when expression of :py:class:`~.command.select.LmiSelectCommand` - could not be evaluated. - """ - def __init__(self, module_name, class_name, expr): - LmiCommandError.__init__(self, module_name, class_name, - "Bad select expression: %s" % expr) - -class LmiTerminate(Exception): - """ - Raised to cleanly terminate interavtive shell. - """ - def __init__(self, exit_code=0): - Exception.__init__(self, exit_code) diff --git a/lmi/scripts/common/formatter/__init__.py b/lmi/scripts/common/formatter/__init__.py deleted file mode 100644 index db7b7e3..0000000 --- a/lmi/scripts/common/formatter/__init__.py +++ /dev/null @@ -1,484 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Subpackage with formatter classes used to render and output results. - -Each formatter has a :py:meth:`Formatter.produce_output` method taking one -argument which gets rendered and printed to output stream. Each formatter -expects different argument, please refer to doc string of particular class. -""" - -import itertools -import locale -import os -import pywbem - -from lmi.scripts.common import errors -from lmi.scripts.common.formatter import command as fcmd - -def get_terminal_width(): - """ - Get the number of columns of current terminal if attached to it. It - defaults to 79 characters. - - :returns: Number of columns of attached terminal. - :rtype: integer - """ - try: - term_cols = int(os.popen('stty size', 'r').read().split()[1]) - except (IOError, OSError, ValueError): - term_cols = 79 # fallback - return term_cols - -class Formatter(object): - """ - Base formatter class. - - It produces string representation of given argument and prints it. - - This formatter supports following commands: - - * :py:class:`~.command.NewHostCommand`. - - :param file stream: Output stream. - :param integer padding: Number of leading spaces to print at each line. - :param boolean no_headings: If table headings should be omitted. - """ - - def __init__(self, stream, padding=0, no_headings=False): - if not isinstance(padding, (int, long)): - raise TypeError("padding must be an integer") - if padding < 0: - padding = 0 - self.out = stream - self.padding = padding - self.no_headings = no_headings - #: counter of hosts printed - self.host_counter = 0 - #: counter of tables produced for current host - self.table_counter = 0 - #: counter of lines producted for current table - self.line_counter = 0 - - @property - def encoding(self): - """ - Try to determine encoding for output terminal. - - :returns: Encoding used to encode unicode strings. - :rtype: string - """ - enc = getattr(self.out, 'encoding') - if not enc: - enc = locale.getpreferredencoding() - return enc - - def render_value(self, val): - """ - Rendering function for single value. - - :param val: Any value to render. - :returns: Value converted to its string representation. - :rtype: str - """ - if isinstance(val, unicode): - return val.encode(self.encoding) - if not isinstance(val, str): - val = str(val) - return val - - def print_line(self, line, *args, **kwargs): - """ - Prints single line. Output message is prefixed with ``padding`` spaces, - formated and printed to output stream. - - :param string line: Message to print, it can contain markers for - other arguments to include according to ``format_spec`` language. - Please refer to ``Format Specification Mini-Language`` in python - documentation. - :param list args: Positional arguments to ``format()`` function of - ``line`` argument. - :param dictionary kwargs: Keyword arguments to ``format()`` function. - """ - line = ' ' * self.padding + line.format(*args, **kwargs) - self.out.write(line.encode(self.encoding)) - self.out.write("\n") - self.line_counter += 1 - - def print_host(self, hostname): - """ - Prints header for new host. - - :param string hostname: Hostname to print. - """ - if ( self.host_counter > 0 - or self.table_counter > 0 - or self.line_counter > 0): - self.out.write("\n") - term_width = get_terminal_width() - self.out.write("="*term_width + "\n") - self.out.write("Host: %s\n" % hostname) - self.out.write("="*term_width + "\n") - self.host_counter += 1 - self.table_counter = 0 - self.line_counter = 0 - - def produce_output(self, data): - """ - Render and print given data. - - Data can be also instance of - :py:class:`~.command.FormatterCommand`, see - documentation of this class for list of - allowed commands. - - This shall be overridden by subclasses. - - :param data: Any value to print. Subclasses may specify their - requirements for this argument. It can be also am instance of - :py:class:`~.command.FormatterCommand`. - """ - self.print_line(str(data)) - self.line_counter += 1 - -class ListFormatter(Formatter): - """ - Base formatter taking care of list of items. It renders input data in a - form of table with mandatory column names at the beginning followed by - items, one occupying single line (row). - - This formatter supports following commands: - * :py:class:`~.command.NewHostCommand` - * :py:class:`~.command.NewTableCommand` - * :py:class:`~.command.NewTableHeaderCommand` - - The command must be provided as content of one row. This row is then not - printed and the command is executed. - - This class should be subclassed to provide nice output. - """ - def __init__(self, stream, padding=0, no_headings=False): - super(ListFormatter, self).__init__(stream, padding, no_headings) - self.column_names = None - - def print_text_row(self, row): - """ - Print data row without any header. - - :param tuple row: Data to print. - """ - self.out.write(" "*self.padding + self.render_value(row) + "\n") - self.line_counter += 1 - - def print_row(self, data): - """ - Print data row. Optionaly print header, if requested. - - :param tuple data: Data to print. - """ - if self.line_counter == 0 and not self.no_headings: - self.print_header() - self.print_text_row(data) - - def print_table_title(self, title): - """ - Prints title of next tale. - - :param string title: Title to print. - """ - if self.table_counter > 0 or self.line_counter > 0: - self.out.write('\n') - self.out.write("%s:\n" % title) - self.table_counter += 1 - self.line_counter = 0 - - def print_header(self): - """ Print table header. """ - if self.no_headings: - return - if self.column_names: - self.print_text_row(self.column_names) - - def produce_output(self, rows): - """ - Prints list of rows. - - There can be a :py:class:`~.command.FormatterCommand` instance instead - of a row. See documentation of this class for list of allowed commands. - - :param rows: List of rows to print. - :type rows: list, generator or :py:class:`.command.FormatterCommand` - """ - for row in rows: - if isinstance(row, fcmd.NewHostCommand): - self.print_host(row.hostname) - elif isinstance(row, fcmd.NewTableCommand): - self.print_table_title(row.title) - elif isinstance(row, fcmd.NewTableHeaderCommand): - self.column_names = row.columns - else: - self.print_row(row) - -class TableFormatter(ListFormatter): - """ - Print nice human-readable table to terminal. - - To print the table nicely aligned, the whole table must be populated first. - Therefore this formatter stores all rows locally and does not print - them until the table is complete. Column sizes are computed afterwards - and the table is printed at once. - - This formatter supports following commands: - * :py:class:`~.command.NewHostCommand` - * :py:class:`~.command.NewTableCommand` - * :py:class:`~.command.NewTableHeaderCommand` - - The command must be provided as content of one row. This row is then not - printed and the command is executed. - """ - def __init__(self, stream, padding=0, no_headings=False): - super(TableFormatter, self).__init__(stream, padding, no_headings) - self.stash = [] - - def print_text_row(self, row, column_sizes): - for i in xrange(len(row)): - size = column_sizes[i] - # Convert to unicode to compute correct length of utf-8 strings - # (e.g. with fancy trees with utf-8 graphics). - item = ( unicode(row[i]) if not isinstance(row[i], str) - else row[i].decode(self.encoding)) - if i < len(row) - 1: - item = item.ljust(size) - self.out.write(self.render_value(item)) - if i < len(row) - 1: - self.out.write(" ") - self.out.write("\n") - self.line_counter += 1 - - def print_stash(self): - # Compute column sizes - column_sizes = [] - rows = iter(self.stash) - try: - if self.column_names is None: - row = rows.next() - else: - row = self.column_names - for i in xrange(len(row)): - column_sizes.append(len(row[i])) - except StopIteration: - pass # empty stash - - for row in rows: - for i in xrange(len(row)): - row_length = len( - unicode(row[i]) if not isinstance(row[i], str) - else row[i].decode(self.encoding)) - if column_sizes[i] < row_length: - column_sizes[i] = row_length - - # print headers - if not self.no_headings and self.column_names: - self.print_text_row(self.column_names, column_sizes) - # print stashed rows - for row in self.stash: - self.print_text_row(row, column_sizes) - self.stash = [] - - def print_row(self, data): - """ - Print data row. - - :param tuple data: Data to print. - """ - self.stash.append(data) - - def print_host(self, hostname): - """ - Prints header for new host. - - :param string hostname: Hostname to print. - """ - if len(self.stash): - # without a check, this would print headers for empty stash - self.print_stash() - super(TableFormatter, self).print_host(hostname) - - def print_table_title(self, title): - """ - Prints title of next tale. - - :param string title: Title to print. - """ - if len(self.stash): - # without a check, this would print headers for empty stash - self.print_stash() - if self.table_counter > 0 or self.line_counter > 0: - self.out.write('\n') - self.out.write("%s:\n" % title) - self.table_counter += 1 - self.line_counter = 0 - - def produce_output(self, rows): - """ - Prints list of rows. - - There can be :py:class:`~.command.FormatterCommand` instance instead of - a row. See documentation of this class for list of allowed commands. - - :param rows: List of rows to print. - :type rows: list or generator - """ - super(TableFormatter, self).produce_output(rows) - self.print_stash() - -class CsvFormatter(ListFormatter): - """ - Renders data in a csv (Comma-separated values) format. - - This formatter supports following commands: - * :py:class:`~.command.NewHostCommand` - * :py:class:`~.command.NewTableCommand` - * :py:class:`~.command.NewTableHeaderCommand` - """ - - def render_value(self, val): - if isinstance(val, basestring): - if isinstance(val, unicode): - val.encode(self.encoding) - val = '"%s"' % val.replace('"', '""') - elif val is None: - val = '' - else: - val = str(val) - return val - - def print_text_row(self, row): - self.print_line(",".join(self.render_value(v) for v in row)) - self.line_counter += 1 - -class SingleFormatter(Formatter): - """ - Meant to render and print attributes of single object. Attributes are - rendered as a list of assignments of values to variables (attribute names). - - This formatter supports following commands: - * :py:class:`~.command.NewHostCommand` - """ - - def produce_output(self, data): - """ - Render and print attributes of single item. - - There can be a :py:class:`~.command.FormatterCommand` instance instead - of a data. See documentation of this class for list of allowed - commands. - - :param data: Is either a pair of property names with list of values or - a dictionary with property names as keys. Using the pair allows to - order the data the way it should be printing. In the latter case - the properties will be sorted by the property names. - :type data: tuple or dict - """ - if isinstance(data, fcmd.NewHostCommand): - self.print_host(data.hostname) - return - - if not isinstance(data, (tuple, dict)): - raise ValueError("data must be tuple or dict") - - if isinstance(data, tuple): - if not len(data) == 2: - raise ValueError( - "data must contain: (list of columns, list of rows)") - dataiter = itertools.izip(data[0], data[1]) - else: - dataiter = itertools.imap( - lambda k: (k, self.render_value(data[k])), - sorted(data.keys())) - for name, value in dataiter: - self.print_line("{0}={1}", name, value) - self.line_counter += 1 - -class ShellFormatter(SingleFormatter): - """ - Specialization of :py:class:`SingleFormatter` having its output executable - as a shell script. - - This formatter supports following commands: - * :py:class:`~.command.NewHostCommand` - """ - - def render_value(self, val): - if isinstance(val, basestring): - if isinstance(val, unicode): - val.encode(self.encoding) - val = "'%s'" % val.replace("'", "\\'") - elif val is None: - val = '' - else: - val = str(val) - return val - -class ErrorFormatter(ListFormatter): - """ - Render error strings for particular host. Supported commands: - * :py:class:`~.command.NewHostCommand` - """ - def __init__(self, stream, padding=4): - super(ErrorFormatter, self).__init__(stream, padding) - - def print_row(self, data): - if isinstance(data, Exception): - if isinstance(data, pywbem.CIMError): - self.print_text_row("%s: %s" % (data.args[1], data.message)) - elif not isinstance(data, errors.LmiFailed): - self.print_text_row("(%s) %s" % (data.__class__.__name__, - str(data))) - else: - self.print_text_row(data) - else: - self.print_text_row(data) - - def print_host(self, hostname): - self.out.write("host %s\n" % hostname) - self.host_counter += 1 - self.table_counter = 0 - self.line_counter = 0 - - def produce_output(self, rows): - for row in rows: - if isinstance(row, fcmd.NewHostCommand): - self.print_host(row.hostname) - elif isinstance(row, fcmd.NewTableCommand): - self.print_table_title(row.title) - else: - self.print_row(row) - diff --git a/lmi/scripts/common/formatter/command.py b/lmi/scripts/common/formatter/command.py deleted file mode 100644 index 78830c6..0000000 --- a/lmi/scripts/common/formatter/command.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Contains command classes used to control formatters from inside of command -execution functions. -""" - -class FormatterCommand(object): - """ - Base class for formatter commands. - """ - pass - -class NewHostCommand(FormatterCommand): - """ - Command for formatter to finish current table (if any), print header for - new host and (if there are any data) print table header. - - :param string hostname: Name of host appearing at the front of new table. - """ - def __init__(self, hostname): - self.hostname = hostname - -class NewTableCommand(FormatterCommand): - """ - Command for formatter to finish current table (if any), print the **title** - and (if there are any data) print table header. - - :param string title: Optional title for new table. - """ - def __init__(self, title=None): - self.title = title - -class NewTableHeaderCommand(FormatterCommand): - """ - Command for formatter to finish current table (if any), store new table - header and (if there are any data) print the table header. - The table header will be printed in all subsequent tables, until - new instance of this class arrives. - - :param tuple columns: Array of column names. - """ - def __init__(self, columns=None): - self.columns = columns diff --git a/lmi/scripts/common/lmi_logging.py b/lmi/scripts/common/lmi_logging.py deleted file mode 100644 index a6ff0e1..0000000 --- a/lmi/scripts/common/lmi_logging.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Utilities for logging framework. -""" -import logging -import sys - -from lmi.scripts.common import configuration -from lmi.scripts.common import errors - -TERM_COLOR_NAMES = ( - 'black', - 'red', - 'green', - 'yellow', - 'blue', - 'magenta', - 'cyan', - 'white', - ) - -# Following are terminal color codes of normal mode. -CN_BLACK = 0 -CN_RED = 1 -CN_GREEN = 2 -CN_YELLOW = 3 -CN_BLUE = 4 -CN_MAGENTA = 5 -CN_CYAN = 6 -CN_WHITE = 7 -# Following are terminal color codes of bright mode. -CB_BLACK = 8 -CB_RED = 9 -CB_GREEN = 10 -CB_YELLOW = 11 -CB_BLUE = 12 -CB_MAGENTA = 13 -CB_CYAN = 14 -CB_WHITE = 15 - -WARNING_COLOR = CB_YELLOW -ERROR_COLOR = CB_RED -CRITICAL_COLOR = CB_MAGENTA - -#: Dictionary assigning color code to logging level. -LOG_LEVEL_2_COLOR = { - logging.WARNING : WARNING_COLOR, - logging.ERROR : ERROR_COLOR, - logging.CRITICAL: CRITICAL_COLOR -} - -class LogRecord(logging.LogRecord): - """ - Overrides :py:class:`logging.LogRecord`. It adds new attributes: - - * `levelname_` - Name of level in lowercase. - * `cseq` - Escape sequence for terminal used to set color - assigned to particular log level. - * `creset` - Escape sequence for terminal used to reset foreground - color. - - These can be used in format strings initializing logging formatters. - - Accepts the same arguments as base class. - """ - - def __init__(self, name, level, *args, **kwargs): - use_colors = kwargs.pop('use_colors', True) - logging.LogRecord.__init__(self, name, level, *args, **kwargs) - self.levelname_ = self.levelname.lower() - if use_colors and level >= logging.WARNING: - if level >= logging.CRITICAL: - color = LOG_LEVEL_2_COLOR[logging.CRITICAL] - elif level >= logging.ERROR: - color = LOG_LEVEL_2_COLOR[logging.ERROR] - else: - color = LOG_LEVEL_2_COLOR[logging.WARNING] - self.cseq = get_color_sequence(color) - self.creset = "\x1b[39m" - else: - self.cseq = self.creset = '' - -class ScriptsLogger(logging.getLoggerClass()): - - #: Boolean saying whether the color sequences for log messages shall be - #: generated. - USE_COLORS = True - - def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, - extra=None): - """ - Overriden method that just changes the *LogRecord* class to our - predefined and ensures that exception's traceback is printed once at - most. - """ - if exc_info: - err = exc_info[1] - if getattr(err, '_traceback_logged', False): - # do not print traceback second time - exc_info = None - elif self.isEnabledFor(level): - try: - err._traceback_logged = True - except AttributeError: - pass - rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func, - use_colors=self.USE_COLORS) - if extra is not None: - for key in extra: - if (key in ["message", "asctime"]) or (key in rv.__dict__): - raise KeyError("Attempt to overwrite %r in LogRecord" % key) - rv.__dict__[key] = extra[key] - return rv - - def exception(self, msg, *args, **kwargs): - lmi_config = configuration.Configuration.get_instance() - exc_info = sys.exc_info() - err = exc_info[1] - if ( lmi_config.trace - and ( not isinstance(err, errors.LmiError) - or ( not getattr(err, '_traceback_logged', False) - and lmi_config.verbosity >= lmi_config.OUTPUT_DEBUG))): - kwargs['exc_info'] = exc_info - else: - kwargs.pop('exc_info', None) - self.error(msg, *args, **kwargs) - -class LevelDispatchingFormatter(object): - """ - Formatter class for logging module. It allows to predefine different format - string used for some level ranges. - - :param dict formatters: Mapping of module names to *format*. - It is a dictionary of following format: :: - - { max_level1 : format1 - , max_level2 : format2 - , ... } - - *format* in parameters description can be either ``string`` or another - formatter object. - - For example if you want to have format3 used for *ERROR* and *CRITICAL* - levels, *format2* for *INFO* and *format1* for anything else, your - dictionary will look like this: :: - - { logging.INFO - 1 : format1 - , logging.INFO : format2 } - - And the *default* value should have *format3* assigned. - - :param default: Default *format* to use. This format is used for all levels - higher than the maximum of *formatters*' keys. - """ - def __init__(self, formatters, default=configuration.DEFAULT_FORMAT_STRING, - datefmt=None): - for k, formatter in formatters.items(): - if isinstance(formatter, basestring): - formatters[k] = logging.Formatter(formatter, datefmt=datefmt) - #: This a a tuple of pairs sorted by the first item in descending - #: order (highest priority ordered first). - self._formatters = tuple(sorted(formatters.items(), - key=lambda t: -t[0])) - if isinstance(default, basestring): - default = logging.Formatter(default, datefmt=datefmt) - self._default_formatter = default - - def format(self, record): - """ - Interface for logging module. - """ - record.levelname_ = record.levelname.lower() - formatter = self._default_formatter - for level, fmt in self._formatters: - if level < record.levelno: - break - formatter = fmt - try: - return formatter.format(record) - except KeyError: - # in some modules or libraries it may happen that logger is - # initialized before our logger class is set as default - record.cseq = record.creset = '' - return formatter.format(record) - -def get_logger(module_name): - """ - Convenience function for getting callable returning logger for particular - module name. It's supposed to be used at module's level to assign its - result to global variable like this: :: - - from lmi.scripts import common - - LOG = common.get_logger(__name__) - - This can be used in module's functions and classes like this: :: - - def module_function(param): - LOG().debug("This is debug statement logging param: %s", param) - - Thanks to ``LOG`` being a callable, it always returns valid logger object - with current configuration, which may change overtime. - - :param string module_name: Absolute dotted module path. - :rtype: :py:class:`logging.Logger` - """ - def _logger(): - """ Callable used to obtain current logger object. """ - return logging.getLogger(module_name) - return _logger - -def get_color_sequence(color_code): - """ - Computer color sequence for particular color code. - - :returns: Escape sequence for terminal used to set foreground color. - :rtype: str - """ - if color_code <= 7: - return "\x1b[%dm" % (30 + color_code) - return "\x1b[38;5;%dm" % color_code - -def setup_logger(use_colors=True): - """ - This needs to be called before any logging takes place. - """ - ScriptsLogger.USE_COLORS = use_colors - logging.setLoggerClass(ScriptsLogger) diff --git a/lmi/scripts/common/session.py b/lmi/scripts/common/session.py deleted file mode 100644 index b063d0a..0000000 --- a/lmi/scripts/common/session.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Module for session object representing all connection to remote hosts. -""" - -from collections import defaultdict - -from lmi.scripts.common import errors -from lmi.scripts.common import get_logger -from lmi.scripts.common.util import FilteredDict -from lmi.shell.LMIConnection import connect - -LOG = get_logger(__name__) - -class Session(object): - """ - Session object keeps connection objects to remote hosts. Their are - associated with particular hostnames. It also caches credentials for them. - Connections are made as they are needed. When credentials are missing - for connection to be made, the user is asked to supply them from - standard input. - - :param app: Instance of main application. - :param list hosts: List of hostname strings. - :param dictionary credentials: Mapping assigning a pair - ``(user, password)`` to each hostname. - :param boolean same_credentials: Use the same credentials for all - hosts in session. The first credentials given will be used. - """ - - def __init__(self, app, hosts, credentials=None, same_credentials=False): - self._app = app - self._connections = {h: None for h in hosts} - # { hostname : (username, password, verified), ... } - # where verified is a flag saying, whether these credentials - # were successfuly used for logging in - self._credentials = defaultdict(lambda: ('', '', False)) - self._same_credentials = same_credentials - if credentials is not None: - if not isinstance(credentials, dict): - raise TypeError("credentials must be a dictionary") - for hostname, creds in credentials.items(): - credentials[hostname] = (creds[0], creds[1], False) - self._credentials.update(credentials) - - def __getitem__(self, hostname): - """ - :rtype: (``LMIConnection``) Connection object to remote host. - ``None`` if connection can not be made. - """ - if self._connections[hostname] is None: - try: - self._connections[hostname] = self._connect( - hostname, interactive=True) - except Exception as exc: - LOG().error('Failed to make a connection to "%s": %s', - hostname, exc) - return self._connections[hostname] - - def __len__(self): - """ Get the number of hostnames in session. """ - return len(self._connections) - - def __contains__(self, uri): - return uri in self._connections - - def __iter__(self): - """ Yields connection objects. """ - successful_connections = 0 - for hostname in self._connections: - connection = self[hostname] - if connection is not None: - yield connection - successful_connections += 1 - if successful_connections == 0: - raise errors.LmiNoConnections('No successful connection made.') - - def _connect(self, hostname, interactive=False): - """ - Makes the connection to host. - - :param string hostname: Name of host. - :param boolean interactive: Whether we can interact with user - and expect a reply from him. - :returns: Connection to remote host or ``None``. - :rtype: :py:class:`lmi.shell.LMIConnection` or ``None`` - """ - username, password = self.get_credentials(hostname) - kwargs = { - 'verify_server_cert' : self._app.config.verify_server_cert, - 'interactive' : interactive - } - if len(self._connections) > 1: - kwargs['prompt_prefix'] = '[%s] ' % hostname - connection = connect(hostname, username, password, **kwargs) - if connection is not None: - tp = connection._client._cliconn.creds - if tp is None: - tp = ('', '') - self._credentials[hostname] = (tp[0], tp[1], True) - else: - LOG().error('Failed to connect to host "%s".', hostname) - return connection - - @property - def hostnames(self): - """ - List of hostnames in session. - - :rtype: list - """ - return self._connections.keys() - - def get_credentials(self, hostname): - """ - :param string hostname: Name of host to get credentials for. - :returns: Pair of ``(username, password)`` for given hostname. If no - credentials were given for this host, ``('', '')`` is returned. - :rtype: tuple - """ - username, password, verified = self._credentials[hostname] - if ( not verified - and (not username or not password) - and self._same_credentials): - for tp in self._credentials.values(): - if tp[2]: - username, password = tp[0], tp[1] - break - return username, password - - def get_unconnected(self): - """ - :returns: List of hostnames, which do not have associated connection - yet. - :rtype: list - """ - return [h for h, c in self._connections.items() if c is None] - -class SessionProxy(Session): - """ - Behaves like a session. But it just encapsulates other session object and - provides access to a subset of its items. - - :param session: Session object or even another session proxy. - :param list uris: Subset of uris in encapsulated session object. - """ - - def __init__(self, session, uris): - uris = set(uris) - if not all(isinstance(uri, basestring) for uri in uris): - raise ValueError("uris must be iterable of uris") - for uri in uris: - if not uri in session: - raise ValueError('uri "%s" needs to belong to given session' - % uri) - Session.__init__(self, session._app, uris, session._credentials, - session._same_credentials) - self._origin = session - self._connections = FilteredDict(uris, session._connections) - # let the credentials propagage to original session - self._credentials = session._credentials - diff --git a/lmi/scripts/common/util.py b/lmi/scripts/common/util.py deleted file mode 100644 index 4ee2161..0000000 --- a/lmi/scripts/common/util.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Various utilities for LMI Scripts. -""" - -class FilteredDict(dict): - """ - Dictionary-like collection that wraps some other dictionary and provides - limited access to its keys and values. It permits to get, delete and set - items specified in advance. - - .. note:: - Please use only the methods overriden. This class does not guarantee - 100% API compliance. Not overriden methods won't work properly. - - :param list key_filter: Set of keys that can be get, set or deleted. - For other keys, :py:class:`KeyError` will be raised. - :param dictionary original: Original dictionary containing not only - keys in *key_filter* but others as well. All modifying operations - operate also on this dictionary. But only those keys in *key_filter* - can be affected by them. - """ - - def __init__(self, key_filter, original=None): - dict.__init__(self) - if original is not None and not isinstance(original, dict): - raise TypeError("original needs to be a dictionary") - if original is None: - original = dict() - self._original = original - self._keys = frozenset(key_filter) - - def __contains__(self, key): - return key in self._keys and key in self._original - - def __delitem__(self, key): - if not key in self._keys: - raise KeyError(repr(key)) - del self._original[key] - - def clear(self): - for key in self._keys: - self._original.pop(key, None) - - def copy(self): - return FilteredDict(self._keys, self._original) - - def iterkeys(self): - for key in self._keys: - yield key - - def __getitem__(self, key): - if not key in self._keys: - raise KeyError(repr(key)) - return self._original[key] - - def __iter__(self): - for key in self._keys: - if key in self._original: - yield key - - def __eq__(self, other): - return ( isinstance(other, FilteredDict) - and self._original == other._original - and self._keys == other._keys) - - def __lt__(self, other): - if isinstance(other, dict): - return { k: v for k, v in self._original.items() - if k in self._keys} < other - if not isinstance(other, FilteredDict): - raise TypeError("Can not compare FilteredDict to objects" - " of other types!") - return self._original <= other._original and self._keys <= other._keys - - def __len__(self): - return len(self.keys()) - - def __setitem__(self, key, value): - if not key in self._keys: - raise KeyError(repr(key)) - self._original[key] = value - - def keys(self): - return [k for k in self._keys if k in self._original] - - def values(self): - return [self._original[k] for k in self.keys() if k in self._original] - - def items(self): - return [(k, self._original[k]) for k in self.keys()] - - def iteritems(self): - return iter(self.items()) - - def pop(self, key, *args): - ret = self[key] - del self[key] - return ret - - def popitem(self): - for key in self._keys: - if key in self._original: - return self.pop(key) - raise KeyError("FilterDict is empty!") - - def update(self, *args, **kwargs): - if len(args) > 1: - raise TypeError('Expected just one positional argument!') - if args and callable(getattr(args[0], 'keys', None)): - for key in args[0].keys(): - self[key] = args[0][key] - elif args: - for key, value in args[0]: - self[key] = value - for key, value in kwargs.items(): - self[key] = value - diff --git a/lmi/scripts/common/versioncheck/__init__.py b/lmi/scripts/common/versioncheck/__init__.py deleted file mode 100644 index c7e595f..0000000 --- a/lmi/scripts/common/versioncheck/__init__.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Package with utilities for checking availability of profiles or CIM classes. -Version requirements can also be specified. -""" - -import functools -from pyparsing import ParseException - -from lmi.scripts.common import Configuration -from lmi.scripts.common import errors -from lmi.scripts.common.versioncheck import parser - -def cmp_profiles(fst, snd): - """ - Compare two profiles by their version. - - :returns: - * -1 if the *fst* profile has lower version than *snd* - * 0 if their versions are equal - * 1 otherwise - :rtype: int - """ - fstver = fst.RegisteredVersion - sndver = snd.RegisteredVersion - if fstver == sndver: - return 0 - return -1 if parser.cmp_version(fstver, sndver) else 1 - -def get_profile_version(conn, name, cache=None): - """ - Get version of registered profile on particular broker. Queries - ``CIM_RegisteredProfile`` and ``CIM_RegisteredSubProfile``. The latter - comes in question only when ``CIM_RegisteredProfile`` does not yield any - matching result. - - :param conn: Connection object. - :param string name: Name of the profile which must match value of *RegisteredName* - property. - :param dictionary cache: Optional cache where the result will be stored for - later use. This greatly speeds up evaluation of several expressions refering - to same profiles or classes. - :returns: Version of matching profile found. If there were more of them, - the highest version will be returned. ``None`` will be returned when no matching - profile or subprofile is found. - :rtype: string - """ - if cache and name in cache: - return cache[(conn.uri, name)] - insts = conn.root.interop.wql('SELECT * FROM CIM_RegisteredProfile' - ' WHERE RegisteredName=\"%s\"' % name) - regular = set(i for i in insts if i.classname.endswith('RegisteredProfile')) - if regular: # select instances of PG_RegisteredProfile if available - insts = regular - else: # otherwise fallback to PG_RegisteredSubProfile instances - insts = set(i for i in insts if i not in regular) - if not insts: - ret = None - else: - ret = sorted(insts, cmp=cmp_profiles)[-1].RegisteredVersion - if cache is not None: - cache[(conn.uri, name)] = ret - return ret - -def get_class_version(conn, name, namespace=None, cache=None): - """ - Query broker for version of particular CIM class. Version is stored in - ``Version`` qualifier of particular CIM class. - - :param conn: Connection object. - :param string name: Name of class to query. - :param string namespace: Optional CIM namespace. Defaults to configured namespace. - :param dictionary cache: Optional cache used to speed up expression prrocessing. - :returns: Version of CIM matching class. Empty string if class is registered but - is missing ``Version`` qualifier and ``None`` if it is not registered. - :rtype: string - """ - if namespace is None: - namespace = Configuration.get_instance().namespace - if cache and (namespace, name) in cache: - return cache[(conn.uri, namespace, name)] - ns = conn.get_namespace(namespace) - cls = getattr(ns, name, None) - if not cls: - ret = None - else: - quals = cls.wrapped_object.qualifiers - if 'Version' not in quals: - ret = '' - else: - ret = quals['Version'].value - if cache is not None: - cache[(conn.uri, namespace, name)] = ret - return ret - -def eval_respl(expr, conn, namespace=None, cache=None): - """ - Evaluate LMIReSpL expression on particular broker. - - :param string expr: Expression to evaluate. - :param conn: Connection object. - :param string namespace: Optional CIM namespace where CIM classes will be - searched. - :param dictionary cache: Optional cache speeding up evaluation. - :returns: ``True`` if requirements in expression are satisfied. - :rtype: boolean - """ - if namespace is None: - namespace = Configuration.get_instance().namespace - stack = [] - pvget = functools.partial(get_profile_version, conn, cache=cache) - cvget = functools.partial(get_class_version, conn, - namespace=namespace, cache=cache) - pr = parser.bnf_parser(stack, pvget, cvget) - pr.parseString(expr, parseAll=True) - # Now evaluate starting non-terminal created on stack. - return stack[0]() - diff --git a/lmi/scripts/common/versioncheck/parser.py b/lmi/scripts/common/versioncheck/parser.py deleted file mode 100644 index b5f9116..0000000 --- a/lmi/scripts/common/versioncheck/parser.py +++ /dev/null @@ -1,521 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Parser for mini-language specifying profile and class requirements. We call -the language LMIReSpL (openLMI Requirement Specification Language). - -The only thing designed for use outside this module is :py:func:`bnf_parser`. - -Language is generated by BNF grammer which served as a model for parser. - -Formal representation of BNF grammer is following: :: - - expr ::= term [ op expr ]* - term ::= '!'? req - req ::= profile_cond | clsreq_cond | '(' expr ')' - profile_cond ::= 'profile'? [ profile | profile_quot ] cond? - clsreq_cond ::= 'class' [ clsname | clsname_quot] cond? - profile_quot ::= '"' /\w+[ +.a-zA-Z0-9_-]*/ '"' - profile ::= /\w+[+.a-zA-Z_-]*/ - clsname_quot ::= '"' clsname '"' - clsname ::= /[a-zA-Z]+_[a-zA-Z][a-zA-Z0-9_]*/ - cond ::= cmpop version - cmpop ::= /(<|=|>|!)=|<|>/ - version ::= /[0-9]+(\.[0-9]+)*/ - op ::= '&' | '|' - -String surrounded by quotes is a literal. String enclosed with slashes is a -regular expression. Square brackets encloses a group of words and limit -the scope of some operation (like iteration). -""" -import abc -import operator -from pyparsing import Literal, Combine, Optional, ZeroOrMore, \ - Forward, Regex, Keyword, FollowedBy, LineEnd, ParseException - -#: Dictionary mapping supported comparison operators to a pair. First item is a -#: function making the comparison and the second can be of two values (``all`` -#: or ``any``). Former sayes that each part of first version string must be in -#: relation to corresponding part of second version string in order to satisfy -#: the condition. The latter causes the comparison to end on first satisfied -#: part. -OP_MAP = { - '==' : (operator.eq, all), - '<=' : (operator.le, all), - '>=' : (operator.ge, all), - '!=' : (operator.ne, any), - '>' : (operator.gt, any), - '<' : (operator.lt, any) -} - -def cmp_version(fst, snd, opsign='<'): - """ - Compare two version specifications. Each version string shall contain - digits delimited with dots. Empty string is also valid version. It will be - replaced with -1. - - :param str fst: First version string. - :param str snd: Second version string. - :param str opsign: Sign denoting operation to be used. Supported signs - are present in :py:attr:`OP_MAP`. - :returns: ``True`` if the relation denoted by particular operation exists - between two operands. - :rtype: boolean - """ - def splitver(ver): - """ Converts version string to a tuple of integers. """ - return tuple(int(p) if p else -1 for p in ver.split('.')) - aparts = splitver(fst) - bparts = splitver(snd) - op, which = OP_MAP[opsign] - if which is all: - for ap, bp in zip(aparts, bparts): - if not op(ap, bp): - return False - else: - for ap, bp in zip(aparts, bparts): - if op(ap, bp): - return True - if ap != bp: - return False - return op(len(aparts), len(bparts)) - -class SemanticGroup(object): - """ - Base class for non-terminals. Just a minimal set of non-terminals is - represented by objects the rest is represented by strings. - - All subclasses need to define their own :py:meth:`evaluate` method. The - parser builds a tree of these non-terminals with single non-terminal being - a root node. This node's *evaluate* method returns a boolean saying whether - the condition is satisfied. Root node is always an object of - :py:class:`Expr`. - """ - - __metaclass__ = abc.ABCMeta - - def __call__(self): - return self.evaluate() - - @abc.abstractmethod - def evaluate(self): - """ - :returns: ``True`` if the sub-condition represented by this non-terminal - is satisfied. - :rtype: boolean - """ - pass - -class Expr(SemanticGroup): - """ - Initial non-terminal. Object of this class (or one of its subclasses) is a - result of parsing. - - :param term: An object of :py:class:`Term` non-terminal. - """ - - def __init__(self, term): - assert isinstance(term, Term) - self.fst = term - - def evaluate(self): - return self.fst() - - def __str__(self): - return str(self.fst) - -class And(Expr): - """ - Represents logical *AND* of two expressions. Short-circuit evaluation is - being exploited here. - - :param fst: An object of :py:class:`Term` non-terminal. - :param snd: An object of :py:class:`Term` non-terminal. - """ - - def __init__(self, fst, snd): - assert isinstance(snd, (Term, Expr)) - Expr.__init__(self, fst) - self.snd = snd - - def evaluate(self): - if self.fst(): - return self.snd() - return False - - def __str__(self): - return "%s & %s" % (self.fst, self.snd) - -class Or(Expr): - """ - Represents logical *OR* of two expressions. Short-circuit evaluation is being - exploited here. - - :param fst: An object of :py:class:`Term` non-terminal. - :param snd: An object of :py:class:`Term` non-terminal. - """ - - def __init__(self, fst, snd): - assert isinstance(snd, (Term, Expr)) - Expr.__init__(self, fst) - self.snd = snd - - def evaluate(self): - if self.fst(): - return True - return self.snd() - - def __str__(self): - return "%s | %s" % (self.fst, self.snd) - -class Term(SemanticGroup): - """ - Represents possible negation of expression. - - :param req: An object of :py:class:`Req`. - :param boolean negate: Whether the result of children shall be negated. - """ - - def __init__(self, req, negate): - assert isinstance(req, Req) - self.req = req - self.negate = negate - - def evaluate(self): - res = self.req() - return not res if self.negate else res - - def __str__(self): - if self.negate: - return '!' + str(self.req) - return str(self.req) - -class Req(SemanticGroup): - """ - Represents one of following subexpressions: - - * single requirement on particular profile - * single requirement on particular class - * a subexpression - """ - pass - -class ReqCond(Req): - """ - Represents single requirement on particular class or profile. - - :param str kind: Name identifying kind of thing this belongs. For example - ``'class'`` or ``'profile'``. - :param callable version_getter: Is a function called to get version of - either profile or CIM class. It must return corresponding version string - if the profile or class is registered and ``None`` otherwise. - Version string is read from ``RegisteredVersion`` property of - ``CIM_RegisteredProfile``. If a class is being queried, version - shall be taken from ``Version`` qualifier of given class. - :param str name: Name of profile or CIM class to check for. In case - of a profile, it is compared to ``RegisteredName`` property of - ``CIM_RegisteredProfile``. If any instance of this class has matching - name, it's version will be checked. If no matching instance is found, - instances of ``CIM_RegisteredSubProfile`` are queried the same way. - Failing to find it results in ``False``. - :param str cond: Is a version requirement. Check the grammer above for - ``cond`` non-terminal. - """ - - def __init__(self, kind, version_getter, name, cond=None): - assert isinstance(kind, basestring) - assert callable(version_getter) - assert isinstance(name, basestring) - assert cond is None or (isinstance(cond, tuple) and len(cond) == 2) - self.kind = kind - self.version_getter = version_getter - self.name = name - self.cond = cond - - def evaluate(self): - version = self.version_getter(self.name) - return version and (not self.cond or self._check_version(version)) - - def _check_version(self, version): - """ - Checks whether the version of profile or class satisfies the - requirement. Version strings are first split into a list of integers - (that were delimited with a dot) and then they are compared in - descending order from the most signigicant down. - - :param str version: Version of profile or class to check. - """ - opsign, cmpver = self.cond - return cmp_version(version, cmpver, opsign) - - def __str__(self): - return '{%s "%s"%s}' % ( - self.kind, self.name, ' %s %s' % self.cond if self.cond else '') - -class Subexpr(Req): - """ - Represents a subexpression originally enclosed in brackets. - """ - - def __init__(self, expr): - assert isinstance(expr, Expr) - self.expr = expr - - def evaluate(self): - return self.expr() - - def __str__(self): - return "(%s)" % self.expr - -class TreeBuilder(object): - """ - A stack interface for parser. It defines methods modifying the stack with - additional checks. - """ - - def __init__(self, stack, profile_version_getter, class_version_getter): - if not isinstance(stack, list): - raise TypeError("stack needs to be empty!") - if stack: - stack[:] = [] - self.stack = stack - self.profile_version_getter = profile_version_getter - self.class_version_getter = class_version_getter - - def expr(self, strg, loc, toks): - """ - Operates upon a stack. It takes either one or two *terms* there - and makes an expression object out of them. Terms need to be delimited - with logical operator. - """ - assert len(self.stack) > 0 - if not isinstance(self.stack[-1], (Term, Expr)): - raise ParseException("Invalid expression (stopped at char %d)." - % loc) - if len(self.stack) >= 3 and self.stack[-2] in ('&', '|'): - assert isinstance(self.stack[-3], Term) - if self.stack[-2] == '&': - expr = And(self.stack[-3], self.stack[-1]) - else: - expr = Or(self.stack[-3], self.stack[-1]) - self.stack.pop() - self.stack.pop() - elif not isinstance(self.stack[-1], Expr): - expr = Expr(self.stack[-1]) - else: - expr = self.stack[-1] - self.stack[-1] = expr - - def term(self, strg, loc, toks): - """ - Creates a ``term`` out of requirement (``req`` non-terminal). - """ - assert len(self.stack) > 0 - assert isinstance(self.stack[-1], Req) - self.stack[-1] = Term(self.stack[-1], toks[0] == '!') - - def subexpr(self, strg, loc, toks): - """ - Operates upon a stack. It creates an instance of :py:class:`Subexpr` - out of :py:class:`Expr` which is enclosed in brackets. - """ - assert len(self.stack) > 1 - assert self.stack[-2] == '(' - assert isinstance(self.stack[-1], Expr) - assert len(toks) > 0 and toks[-1] == ')' - self.stack[-2] = Subexpr(self.stack[-1]) - self.stack.pop() - - def push_class(self, strg, loc, toks): - """ - Handles ``clsreq_cond`` non-terminal in one go. It extracts - corresponding tokens and pushes an object of :py:class:`ReqCond` to a - stack. - """ - assert toks[0] == 'class' - assert len(toks) >= 2 - name = toks[1] - condition = None - if len(toks) > 2 and toks[2] in OP_MAP: - assert len(toks) >= 4 - condition = toks[2], toks[3] - self.stack.append(ReqCond('class', self.class_version_getter, - name, condition)) - - def push_profile(self, strg, loc, toks): - """ - Handles ``profile_cond`` non-terminal in one go. It behaves in the same - way as :py:meth:`push_profile`. - """ - index = 0 - if toks[0] == 'profile': - index = 1 - assert len(toks) > index - name = toks[index] - index += 1 - condition = None - if len(toks) > index and toks[index] in OP_MAP: - assert len(toks) >= index + 2 - condition = toks[index], toks[index + 1] - self.stack.append(ReqCond('profile', self.profile_version_getter, - name, condition)) - - def push_literal(self, strg, loc, toks): - """ - Pushes operators to a stack. - """ - assert toks[0] in ('&', '|', '(') - if toks[0] == '(': - assert not self.stack or self.stack[-1] in ('&', '|') - else: - assert len(self.stack) > 0 - assert isinstance(self.stack[-1], Term) - self.stack.append(toks[0]) - -def bnf_parser(stack, profile_version_getter, class_version_getter): - """ - Builds a parser operating on provided stack. - - :param list stack: Stack to operate on. It will contain the resulting - :py:class:`Expr` object when the parsing is successfully over - - it will be the only item in the list. It needs to be initially empty. - :param callable profile_version_getter: Function returning version - of registered profile or ``None`` if not present. - :param callable class_version_getter: Fucntion returning version - of registered class or ``None`` if not present. - :returns: Parser object. - :rtype: :py:class:`pyparsing,ParserElement` - """ - if not isinstance(stack, list): - raise TypeError("stack must be a list!") - builder = TreeBuilder(stack, profile_version_getter, class_version_getter) - - ntop = ((Literal('&') | Literal('|')) + FollowedBy(Regex('["a-zA-Z\(!]'))) \ - .setName('op').setParseAction(builder.push_literal) - ntversion = Regex(r'[0-9]+(\.[0-9]+)*').setName('version') - ntcmpop = Regex(r'(<|=|>|!)=|<|>(?=\s*\d)').setName('cmpop') - ntcond = (ntcmpop + ntversion).setName('cond') - ntclsname = Regex(r'[a-zA-Z]+_[a-zA-Z][a-zA-Z0-9_]*').setName('clsname') - ntclsname_quot = Combine( - Literal('"').suppress() - + ntclsname - + Literal('"').suppress()).setName('clsname_quot') - ntprofile_quot = Combine( - Literal('"').suppress() - + Regex(r'\w+[ +.a-zA-Z0-9_-]*') - + Literal('"').suppress()).setName('profile_quot') - ntprofile = Regex(r'\w+[+.a-zA-Z0-9_-]*').setName('profile') - ntclsreq_cond = ( - Keyword('class') - + (ntclsname_quot | ntclsname) - + Optional(ntcond)).setName('clsreq_cond').setParseAction( - builder.push_class) - ntprofile_cond = ( - Optional(Keyword('profile')) - + (ntprofile_quot | ntprofile) - + Optional(ntcond)).setName('profile_cond').setParseAction( - builder.push_profile) - ntexpr = Forward().setName('expr') - bracedexpr = ( - Literal('(').setParseAction(builder.push_literal) - + ntexpr - + Literal(')')).setParseAction(builder.subexpr) - ntreq = (bracedexpr | ntclsreq_cond | ntprofile_cond).setName('req') - ntterm = (Optional(Literal("!")) + ntreq + FollowedBy(Regex('[\)&\|]') | LineEnd()))\ - .setParseAction(builder.term) - ntexpr << ntterm + ZeroOrMore(ntop + ntexpr).setParseAction(builder.expr) - - return ntexpr - -if __name__ == '__main__': - def get_class_version(class_name): - try: - version = { 'lmi_logicalfile' : '0.1.2' - , 'lmi_softwareidentity' : '3.2.1' - , 'pg_computersystem' : '1.1.1' - }[class_name.lower()] - except KeyError: - version = None - return version - - def get_profile_version(profile_name): - try: - version = { 'openlmi software' : '0.1.2' - , 'openlmi-software' : '1.3.4' - , 'openlmi hardware' : '1.1.1' - , 'openlmi-hardware' : '0.2.3' - }[profile_name.lower()] - except KeyError: - version = None - return version - - def test(s, expected): - stack = [] - parser = bnf_parser(stack, get_profile_version, get_class_version) - results = parser.parseString(s, parseAll=True) - if len(stack) == 1: - evalresult = stack[0]() - if expected == evalresult: - print "%s\t=>\tOK" % s - else: - print "%s\t=>\tFAILED" % s - else: - print "%s\t=>\tFAILED" % s - print " stack: [%s]" % ', '.join(str(i) for i in stack) - - test( 'class LMI_SoftwareIdentity == 0.2.0', False) - test( '"OpenLMI-Software" == 0.1.2 & "OpenLMI-Hardware" < 0.1.3', False) - test( 'OpenLMI-Software<1|OpenLMI-Hardware!=1.2.4', True) - test( '"OpenLMI Software" & profile "OpenLMI Hardware"' - ' | ! class LMI_LogicalFile', True) - test( 'profile OpenLMI-Software > 0.1.2 & !(class "PG_ComputerSystem"' - ' == 2.3.4 | "OpenLMI Hardware")', False) - test( 'OpenLMI-Software > 1.3 & OpenLMI-Software >= 1.3.4' - ' & OpenLMI-Software < 1.3.4.1 & OpenLMI-Software <= 1.3.4' - ' & OpenLMI-Software == 1.3.4', True) - test( 'OpenLMI-Software < 1.3.4 | OpenLMI-Software > 1.3.4' - ' | OpenLMI-Software != 1.3.4', False) - test( '(! OpenLMI-Software == 1.3.4 | OpenLMI-Software <= 1.3.4.1)' - ' & !(openlmi-software > 1.3.4 | Openlmi-software != 1.3.4)', True) - for badexpr in ( - 'OpenLMI-Software > & OpenLMI-Hardware', - 'classs LMI_SoftwareIdentity', - 'OpenLMI-Software > 1.2.3 | == 5.4.3', - '', - '"OpenLMI-Software', - 'OpenLMI-Software < > OpenLMI-Hardware', - 'OpenlmiSoftware & (openLMI-Hardware ', - 'OpenLMISoftware & ) OpenLMI-Hardare (', - 'OpenLMISoftware | OpenlmiSoftware > "1.2.3"' - ): - try: - test(badexpr, None) - except ParseException: - print "%s\t=>\tOK" % badexpr diff --git a/man/Makefile b/man/Makefile deleted file mode 100644 index 47a3f18..0000000 --- a/man/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -BUILDDIR ?= . -MANPAGE := lmi.1 -TARGET := $(BUILDDIR)/$(notdir $(MANPAGE)).gz - -.PHONY: clean - -$(TARGET): $(MANPAGE) - cat $? | gzip > $@ - -$(MANPAGE): generate.sh ../config/lmi.conf ../lmi/scripts/_metacommand/toplevel.py - ./generate.sh $@ - -clean: - rm -f $(TARGET) diff --git a/man/generate.sh b/man/generate.sh deleted file mode 100755 index a8a8e8d..0000000 --- a/man/generate.sh +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/bash -# This script needs to be run from openlmi-scripts/man directory. It creates a -# man page in current directory for lmi meta-command. It's generated from lmi's -# help message and from the configuration file. -# Requirements: -# * lmi meta-command needs to be installed and on PATH. - -dest=${1:-lmi.1} -usage_file=`mktemp` -options_file=`mktemp` -configuration_file=`mktemp` - -# states: -# desc - parse description -# wusg - wait for usage -# pusg - parse usage -# wopt - wait for options -# popt - parse options -state="desc" -re_option="^([[:blank:]]+)((-[-[:alpha:]_]+)" -re_option+="(\ [][:alpha:]<>_()[|-]+)*)(\ \ +(.*))?" -DESCRIPTION= - -if ! which "lmi" >/dev/null 2>&1; then - echo "Please install lmi meta-command first!" >&2 - exit 1 -fi - -while IFS= read line; do - case "$state" in - "desc") - if [[ "$line" =~ ^[[:blank:]]*$ ]] && [ -n "$DESCRIPTION" ]; then - state="wusg" - else - [ -n "$DESCRIPTION" ] && DESCRIPTION+=" " - DESCRIPTION+="$line" - fi - ;; - - "wusg") - if [[ "$line" =~ ^[Uu]sage: ]]; then - state="pusg0" - printf ".nf\n" >$usage_file - fi - ;; - - pusg*) - if [[ "$line" =~ ^[[:blank:]]*$ ]]; then - state="wopt" - printf ".fi\n" >>$usage_file - else - usg_count=`echo $state | sed 's/^pusg\([[:digit:]]\+\)/\1/'` - if [ "$usg_count" = 0 ]; then - spaces=`echo "$line" | sed 's/^\(\s\+\).*/\1/' | wc -c` - elif [ $spaces -lt `echo "$line" | sed 's/^\(\s\+\).*/\1/' | wc -c` ]; - then - indented=true - printf ".RS\n" >>$usage_file - elif $indented; then - indented=false - printf ".RE\n" >>$usage_file - fi - printf ".B " >>$usage_file - echo "$line" >>$usage_file - #echo "$line" | sed -e 's/^\s\+//' -e 's/\s\+$//' >>$usage_file - state="pusg$((usg_count+1))" - fi - ;; - - "wopt") - if [[ "$line" =~ ^[Oo]ption ]]; then - state="popt0" - fi - ;; - - popt*) - if [[ "$line" =~ ^[[:blank:]]*$ ]]; then - printf "\n" - break - elif [[ "$line" =~ $re_option ]]; then - opt_count=`echo $state | sed 's/^popt\([[:digit:]]\+\)/\1/'` - [ "$opt_count" -gt 0 ] && printf '\n' - printf ".TP\n.I ${BASH_REMATCH[2]}\n" - printf -- "${BASH_REMATCH[5]}" | sed -e 's/^\s\+//' -e 's/\s\+$//' - state="popt$((opt_count+1))" - else - printf " " - printf "$line" | sed -e 's/^\s\+//' -e 's/\s\+$//' - fi - ;; - - *) break - ;; - - esac -done < <( lmi --help ) >$options_file; - -# states: -# wsct - wait for the first section -# psct - parsing the section -# pdsc - parsing description of section -state="wsct" -re_sct_start="^\[([[:alpha:]]+)\]" -re_opt_desc="^#(\s+(.*)|$)" -re_opt_value="^#?([[:alpha:]]+)\s*=\s*(.*)" -while IFS= read line; do - case $state in - "wsct") - if [[ "$line" =~ $re_sct_start ]]; then - state="psct" - printf ".TP\n.B ${BASH_REMATCH[1]} Section\n" - fi - ;; - - "psct") - if [[ "$line" =~ $re_opt_desc ]]; then - state="pdsc" - DESCRIPTION="${BASH_REMATCH[2]}" - elif [[ "$line" =~ $re_sct_start ]]; then - printf ".TP\n.B ${BASH_REMATCH[1]} Section\n" - fi - ;; - - "pdsc") - if [[ "$line" =~ $re_opt_value ]]; then - state="psct" - #DESCRIPTION="${DESCRIPTION} ${BASH_REMATCH[2]}" - printf ".RS 4\n.TP 4\n.B ${BASH_REMATCH[1]}\n" - if [ -n "${BASH_REMATCH[2]}" ]; then - echo "Default:" - echo ".B ${BASH_REMATCH[2]}" - printf ".IP\n" - fi - echo "${DESCRIPTION}" - printf ".RE\n" - elif [[ "$line" =~ $re_opt_desc ]]; then - DESCRIPTION="${DESCRIPTION} ${BASH_REMATCH[2]}" - elif [[ $line =~ ^$ ]]; then - state="psct" - fi - ;; - - esac -done < ../config/lmi.conf >$configuration_file - -DATE=`LC_ALL="en_US.utf8" date '+%B %Y'` -VERSION=`lmi --version` - -cat >$dest <<EOF -.TH lmi 1 "${DATE}" "openlmi-scripts ${VERSION}" "User Commands" - -.SH NAME -lmi \- meta-command for managing systems with OpenLMI providers -.SH SYNOPSIS -EOF - -cat $usage_file >>$dest -rm $usage_file - -cat >>$dest << EOF -.SH DESCRIPTION -${DESCRIPTION} -.SH OPTIONS -EOF - -cat $options_file >>$dest -rm $options_file - -cat >>$dest <<EOF -.SH CONFIGURATION -There is a system-wide configuration file located at -\`/etc/openlmi/lmi.conf\` and user configuration file searched at \`~/.lmirc\`. -File path of the later can also be specified with -.B --config-file -option. Options in user configuration overrides system-wide. - -The configuration consists of sections, led by a -.B [section] -header and followed by -.B name: value -entries. Comments are prefixed with -.B # -or -.B ; -.PP -Please refer to -.B RFC 822 -for more details on this format. Follows a list of possible configuration -options with leading section names. -EOF - -cat $configuration_file >>$dest -rm $configuration_file - -cat >>$dest <<EOF -.SH AUTHORS -.LP -Michal Minar <miminar@redhat.com> -EOF diff --git a/man/lmi.1 b/man/lmi.1 deleted file mode 100644 index d402bf5..0000000 --- a/man/lmi.1 +++ /dev/null @@ -1,201 +0,0 @@ -.TH lmi 1 "October 2013" "openlmi-scripts 0.2.2" "User Commands" - -.SH NAME -lmi \- meta-command for managing systems with OpenLMI providers -.SH SYNOPSIS -.nf -.B lmi [options] [(--trace | --notrace)] [-v]... [-h <host>]... -.RS -.B <command> [<args> ...] -.RE -.B lmi [options] [(--trace | --notrace)] [-v]... [-h <host>]... -.B lmi (--help | --version) -.fi -.SH DESCRIPTION -Allows to set a path to file, where messages will be logged. No log file is written at default. -.SH OPTIONS -.TP -.I -c --config-file <config> -Path to a user configuration file. Options specified here override any settings of global configuration file. -.TP -.I -h --host <host> -Hostname of target system. -.TP -.I --hosts-file <hosts> -Path to a file containing target hostnames. Each hostname must be listed on a single line. -.TP -.I --user <user> -Username used in connection to any target host. -.TP -.I --same-credentials -Use the first credentials given for all hosts. -.TP -.I -n --noverify -Do not verify cimom's ssl certificate. -.TP -.I -v -Increase verbosity of output. -.TP -.I --trace -Show tracebacks on errors. -.TP -.I --notrace -Suppress tracebacks for exceptions. -.TP -.I -q --quiet -Supress output except for errors. -.TP -.I --log-file <log_file> -Output file for logging messages. -.TP -.I --namespace <namespace> -Default CIM namespace to use. -.TP -.I -N --no-headings -Don't print table headings. -.TP -.I -H --human-friendly -Print large values in human friendly units (i.e. MB, GB, TB etc.) -.TP -.I -L --lister-format (table | csv) - Print output of lister commands in CSV or table format. CSV format is more suitable for machine processing. Defaults to table. -.TP -.I --help -Show this text and quit. -.TP -.I --version -Print version of 'lmi' in use and quit. -.SH CONFIGURATION -There is a system-wide configuration file located at -`/etc/openlmi/lmi.conf` and user configuration file searched at `~/.lmirc`. -File path of the later can also be specified with -.B --config-file -option. Options in user configuration overrides system-wide. - -The configuration consists of sections, led by a -.B [section] -header and followed by -.B name: value -entries. Comments are prefixed with -.B # -or -.B ; -.PP -Please refer to -.B RFC 822 -for more details on this format. Follows a list of possible configuration -options with leading section names. -.TP -.B Main Section -.RS 4 -.TP 4 -.B CommandNamespace -Default: -.B lmi.scripts.cmd -.IP -Python namespace, where command entry points will be searched for. -.RE -.RS 4 -.TP 4 -.B Trace -Default: -.B False -.IP -Whether the exceptions should be logged with tracebacks. -.RE -.RS 4 -.TP 4 -.B Verbosity -Default: -.B 0 -.IP -An integer from -1 to 2 saying, how verbosive the output to console shall be. This differs from [Log] Level, which controls the logging messages being logged to file. If logging to console is enabled it sets the minimum severity level. -1 Supresses all messages except for critical ones. 0 shows warnings, 1 info messages and 2 enables debug messages. This option also affects the verbosity of commands, making them print more information to stdout. -.RE -.TP -.B CIM Section -.RS 4 -.TP 4 -.B namespace -Default: -.B root/cimv2 -.IP -To override default CIM namespace, uncomment the line below. -.RE -.TP -.B SSL Section -.RS 4 -.TP 4 -.B VerifyServerCertificate -Default: -.B True -.IP -Whether to verify server-side certificate, when making secured connection over ssl. -.RE -.TP -.B Format Section -.RS 4 -.TP 4 -.B HumanFriendly -Default: -.B False -.IP -Whether to print values in human readable forms (e.g. with units). -.RE -.RS 4 -.TP 4 -.B ListerFormat -Default: -.B table -.IP -What format to use, when listing tabular data. Possible values are: {csv, table}. The former allows for easy machine parsing, the latter is more human friendly. -.RE -.RS 4 -.TP 4 -.B NoHeadings -Default: -.B False -.IP -Whether to suppress headings (column names) when printing tables. -.RE -.TP -.B Log Section -.RS 4 -.TP 4 -.B Level -Default: -.B ERROR -.IP -Level can be set to following values: DEBUG, INFO, WARNING, ERROR, CRITICAL Affects only logging to a file. -.RE -.RS 4 -.TP 4 -.B ConsoleFormat -Default: -.B %(levelname)s: %(message)s -.IP -Format string used, when logging to a console. For details visit: http://docs.python.org/2/library/logging.html#logrecord-attributes -.RE -.RS 4 -.TP 4 -.B LogToConsole -Default: -.B True -.IP -Whether the logging to console is enabled. -.RE -.RS 4 -.TP 4 -.B FileFormat -Default: -.B %(asctime)s:%(levelname)-8s:%(name)s:%(lineno)d - %(message)s -.IP -Format string used, when logging to a file. -.RE -.RS 4 -.TP 4 -.B OutputFile -Allows to set a path to file, where messages will be logged. No log file is written at default. -.RE -.SH AUTHORS -.LP -Michal Minar <miminar@redhat.com> diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 2ddce25..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[upload_docs] -upload-dir = doc/_build/html diff --git a/setup.py.skel b/setup.py.skel deleted file mode 100644 index ff31f29..0000000 --- a/setup.py.skel +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python - -import os -import subprocess -import sys -from setuptools import setup, find_packages - -long_description = '' -try: - try: - script_dir = os.path.dirname(sys.argv[0]) - cmd = ['/usr/bin/make', 'readme'] - readme_file = 'README.txt' - if script_dir not in (',', ''): - cmd[1:1] = ['-C', script_dir] - readme_file = os.path.join(script_dir, readme_file) - with open('/dev/null', 'w') as null: - ret = subprocess.call(cmd, stdout=null, stderr=null) - if not ret: - long_description = open(readme_file, 'rt').read() - except Exception as err: - sys.stderr.write('ERROR while reading README.txt: %s\n' % str(err)) - if not long_description: - long_description = open('README.md', 'rt').read() -except IOError: - pass - -setup( - name='openlmi-scripts', - version='@@VERSION@@', - description='Client-side library and command-line client', - long_description=long_description, - author='Michal Minar', - author_email='miminar@redhat.com', - url='https://github.com/openlmi/openlmi-scripts', - download_url='https://github.com/openlmi/openlmi-scripts/tarball/master', - platforms=['Any'], - license="BSD", - classifiers=[ - 'License :: OSI Approved :: BSD License', - 'Operating System :: POSIX :: Linux', - 'Topic :: System :: Systems Administration', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - namespace_packages=['lmi', 'lmi.scripts'], - packages=[ - 'lmi', - 'lmi.scripts', - 'lmi.scripts.common', - 'lmi.scripts.common.command', - 'lmi.scripts.common.formatter', - 'lmi.scripts.common.versioncheck', - 'lmi.scripts._metacommand'], - install_requires=['openlmi-tools >= 0.8', 'docopt >= 0.6', 'pyparsing'], - include_package_data=True, - #data_files=[('/etc/openlmi/scripts', ['config/lmi.conf'])], - zip_safe=False, - entry_points={ - 'console_scripts': [ - 'lmi = lmi.scripts._metacommand:main' - ], - 'lmi.scripts.cmd': [], - }, - ) diff --git a/test/README.md b/test/README.md deleted file mode 100644 index cfcbbf7..0000000 --- a/test/README.md +++ /dev/null @@ -1,32 +0,0 @@ -Tests Description -================= -Here are tests for lmi meta-command. -Interactive and non-interactive modes are tested. - -Dependencies ------------- - * bash - * beakerlib - * expect - * openlmi-tools - -Openlmi scripts need not be installed. - -Remote host shall have `openlmi-software` and `openlmi-hardware` installed. If -not, `LMI_SOFTWARE_PROVIDER_VERSION` and `LMI_HARDWARE_PROVIDER_VERSION` needs -to be set to `none`. - -Run ---- -Install and run any CIM broker on this or any remote host. Ensure that lmishell -can connect to it. Export these variables: - - * LMI_CIMOM_URL - * LMI_CIMOM_USERNAME - * LMI_CIMOM_PASSWORD - * LMI_SOFTWARE_PROVIDER_VERSION - version of software provider registered - with CIMOM - * LMI_HARDWARE_PROVIDER_VERSION - -Execute: - $ ./run.sh diff --git a/test/base.sh b/test/base.sh deleted file mode 100644 index acec1be..0000000 --- a/test/base.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Jan Safranek <jsafrane@redhat.com> -# Authors: Michal Minar <miminar@redhat.com> - -# Basic framework for all 'lmi' tests. -# It is based on Beakerlib, https://fedorahosted.org/beakerlib/ - -. /usr/share/beakerlib/beakerlib.sh - -LMI_CIMOM_URL=${LMI_CIMOM_URL:-kvm-rhel7} -LMI_CIMOM_USERNAME=${LMI_CIMOM_USERNAME:-root} -LMI_CIMOM_PASSWORD=${LMI_CIMOM_PASSWORD:-redhat} - -SCHEMA=$(echo $LMI_CIMOM_URL | sed -n 's!^\([[:alpha:]]\+://\).*!\1!p') -HOSTNAME=$(echo $LMI_CIMOM_URL | sed -e 's!^.*//!!' -e 's!/.*!!') -LMI="lmi" -[ -n "$HOSTNAME" ] && \ - LMI+=" -n -h $SCHEMA$LMI_CIMOM_USERNAME:$LMI_CIMOM_PASSWORD@$HOSTNAME" -export LMI - -# for some tests no other options are welcomed -export LMI_=`echo $LMI | cut -d \ -f 1` - diff --git a/test/cmd/help_foo.out b/test/cmd/help_foo.out deleted file mode 100644 index 1584a91..0000000 --- a/test/cmd/help_foo.out +++ /dev/null @@ -1,6 +0,0 @@ -error : No such command "foo". -Commands: - help - Print the list of supported commands with short description. - -For more informations about particular command type: - help <command> diff --git a/test/cmd/help_help.out b/test/cmd/help_help.out deleted file mode 100644 index fd6fecb..0000000 --- a/test/cmd/help_help.out +++ /dev/null @@ -1,4 +0,0 @@ -Print the list of supported commands with short description. -If a subcommand is given, its detailed help will be printed. - -Usage: lmi help [<subcommand>...] diff --git a/test/cmd/help_test.out b/test/cmd/help_test.out deleted file mode 100644 index 8ebe3a4..0000000 --- a/test/cmd/help_test.out +++ /dev/null @@ -1,7 +0,0 @@ -Test command for lmi-metacommand. - -Usage: - lmi test list <cmd> [<args> ...] - lmi test show pkg <name> - lmi test show repo <name> - lmi test log <cmd> [<args> ...] diff --git a/test/cmd/help_test_list.out b/test/cmd/help_test_list.out deleted file mode 100644 index 3063607..0000000 --- a/test/cmd/help_test_list.out +++ /dev/null @@ -1,5 +0,0 @@ -List various types of software data. - -Usage: - lmi test list pkgs [(--installed | --available)] - lmi test list repos [(--enabled | --disabled)] diff --git a/test/cmd/help_with_test.out b/test/cmd/help_with_test.out deleted file mode 100644 index 8b93267..0000000 --- a/test/cmd/help_with_test.out +++ /dev/null @@ -1,6 +0,0 @@ -Commands: - help - Print the list of supported commands with short description. - test - Test command for lmi-metacommand. - -For more informations about particular command type: - help <command> diff --git a/test/cmd/help_without_test.out b/test/cmd/help_without_test.out deleted file mode 100644 index 6640727..0000000 --- a/test/cmd/help_without_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Commands: - help - Print the list of supported commands with short description. - -For more informations about particular command type: - help <command> diff --git a/test/cmdver/README.md b/test/cmdver/README.md deleted file mode 100644 index 14c401e..0000000 --- a/test/cmdver/README.md +++ /dev/null @@ -1 +0,0 @@ -Subcommand just for testing purposes. diff --git a/test/cmdver/lmi/__init__.py b/test/cmdver/lmi/__init__.py deleted file mode 100644 index b1a2ff0..0000000 --- a/test/cmdver/lmi/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -__import__('pkg_resources').declare_namespace(__name__) diff --git a/test/cmdver/lmi/scripts/__init__.py b/test/cmdver/lmi/scripts/__init__.py deleted file mode 100644 index b1a2ff0..0000000 --- a/test/cmdver/lmi/scripts/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -__import__('pkg_resources').declare_namespace(__name__) diff --git a/test/cmdver/lmi/scripts/cmdver/__init__.py b/test/cmdver/lmi/scripts/cmdver/__init__.py deleted file mode 100644 index f9e0c8b..0000000 --- a/test/cmdver/lmi/scripts/cmdver/__init__.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. - -import pywbem -from lmi.scripts.common import command -from lmi.scripts.common import get_computer_system - -# 1st entry point -class CmdverSw(command.LmiSelectCommand): - """ - This is a short description for CmdverSw. - """ - SELECT = ( - ('OpenLMI-Software < 0.4.2', 'lmi.scripts.cmdver.pre042.Cmd'), - ('OpenLMI-Software == 0.4.2', 'lmi.scripts.cmdver.ver042.Cmd'), - ('OpenLMI-Software > 0.4.2', 'lmi.scripts.cmdver.devel.Cmd'), - ) - -def get_hw_profile_version(ns): - try: - return ns.connection.root.interop.wql('SELECT * FROM PG_RegisteredProfile' - ' WHERE RegisteredName="OpenLMI-Hardware"')[0].RegisteredVersion - except pywbem.CIMError, IndexError: - return None - -class SystemInfo(command.LmiLister): - COLUMNS = [] - PRE042 = False - - def execute(self, ns): - cls = ns.LMI_Chassis - inst = cls.first_instance() - verstr = get_hw_profile_version(ns) - if self.PRE042: - verstr += ' (PRE 0.4.2)' - return [('Prov version', verstr), - ('Chassis Type', cls.ChassisPackageTypeValues.value_name( - inst.ChassisPackageType))] - -class HostnameInfo(command.LmiLister): - COLUMNS = [] - PRE042 = False - - def execute(self, ns): - verstr = get_hw_profile_version(ns) - if self.PRE042: - verstr += ' (PRE 0.4.2)' - return [('Prov version', verstr), - ('Hostname', get_computer_system(ns).Name)] - -class PreSystemInfo(SystemInfo): - PRE042 = True - -class PreHostnameInfo(HostnameInfo): - PRE042 = True - -class HwCmd(command.LmiCommandMultiplexer): - """ - Hardware testing command. - - Usage: - %(cmd)s system - %(cmd)s hostname - """ - COMMANDS = { - 'system' : SystemInfo, - 'hostname' : HostnameInfo - } - OWN_USAGE = True - -class PreHwCmd(HwCmd): - COMMANDS = { - 'system' : PreSystemInfo, - 'hostname' : PreHostnameInfo - } - -class NoHwRegistered(command.LmiLister): - """ - Hardware testing command. - - Usage: %(cmd)s <cmd> - """ - COLUMNS = [] - OWN_USAGE = True - def execute(self, ns, cmd): - return [('Given command', cmd), ('Prov version', 'N/A')] - -# 2nd entry point -class CmdverHw(command.LmiSelectCommand): - """ - This is a short description for CmdverHw. - """ - SELECT = ( - ('OpenLMI-Hardware < 0.4.2', PreHwCmd), - ('OpenLMI-Hardware >= 0.4.2 & class LMI_Chassis == 0.3.0', HwCmd) - ) - DEFAULT = NoHwRegistered - -# 3rd entry point -class Cmdver(command.LmiCommandMultiplexer): - """ - Command for testing version dependencies. - - Usage: - %(cmd)s (sw|hw) [<args>...] - """ - COMMANDS = { - 'sw' : CmdverSw, - 'hw' : CmdverHw - } - OWN_USAGE = True diff --git a/test/cmdver/lmi/scripts/cmdver/devel.py b/test/cmdver/lmi/scripts/cmdver/devel.py deleted file mode 100644 index 624428e..0000000 --- a/test/cmdver/lmi/scripts/cmdver/devel.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. - -from lmi.scripts.common import command -from lmi.scripts.cmdver import swbase - -class Cmd(swbase.SwCmdBase): - ADDITIONAL_VERSION_INFO = ' (DEVEL)' diff --git a/test/cmdver/lmi/scripts/cmdver/pre042.py b/test/cmdver/lmi/scripts/cmdver/pre042.py deleted file mode 100644 index e7565ae..0000000 --- a/test/cmdver/lmi/scripts/cmdver/pre042.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. - -from lmi.scripts.common import command -from lmi.scripts.cmdver import swbase - -class Cmd(swbase.SwCmdBase): - ADDITIONAL_VERSION_INFO = ' (PRE 0.4.2)' diff --git a/test/cmdver/lmi/scripts/cmdver/swbase.py b/test/cmdver/lmi/scripts/cmdver/swbase.py deleted file mode 100644 index eed9dc2..0000000 --- a/test/cmdver/lmi/scripts/cmdver/swbase.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -""" -Software testing command. - -Usage: %(cmd)s -""" - -import pywbem -from lmi.scripts.common import command - -def get_sw_profile_version(ns): - try: - return ns.connection.root.interop.wql('SELECT * FROM PG_RegisteredProfile' - ' WHERE RegisteredName="OpenLMI-Software"')[0].RegisteredVersion - except pywbem.CIMError, IndexError: - return None - -class SwCmdBase(command.LmiLister): - OWN_USAGE = __doc__ - COLUMNS = [] - ADDITIONAL_VERSION_INFO = '' - - def execute(self, ns): - return [('Prov version', - get_sw_profile_version(ns) + self.ADDITIONAL_VERSION_INFO)] - diff --git a/test/cmdver/lmi/scripts/cmdver/ver042.py b/test/cmdver/lmi/scripts/cmdver/ver042.py deleted file mode 100644 index 433b539..0000000 --- a/test/cmdver/lmi/scripts/cmdver/ver042.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. - -from lmi.scripts.common import command -from lmi.scripts.cmdver import swbase - -class Cmd(swbase.SwCmdBase): - ADDITIONAL_VERSION_INFO = ' (VER 0.4.2)' diff --git a/test/cmdver/setup.py b/test/cmdver/setup.py deleted file mode 100644 index 58ea463..0000000 --- a/test/cmdver/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -from setuptools import setup - -try: - long_description = open('README.md', 'rt').read() -except IOError: - long_description = '' - -setup( - name='openlmi-scripts-cmdver', - version='0.1.2', - description='Test command for versioning.', - long_description=long_description, - author=u'Michal Minar', - author_email='miminar@redhat.com', - url='https://github.com/openlmi/openlmi-cmdver', - download_url='https://github.com/openlmi/openlmi-cmdver/tarball/master', - platforms=['Any'], - license="BSD", - classifiers=[ - 'License :: OSI Approved :: BSD License', - 'Operating System :: POSIX :: Linux', - 'Topic :: System :: Systems Administration', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - install_requires=['openlmi-scripts'], - - namespace_packages=['lmi', 'lmi.scripts'], - packages=['lmi', 'lmi.scripts', 'lmi.scripts.cmdver'], - include_package_data=True, - - entry_points={ - 'lmi.scripts.cmd': [ - # All subcommands of lmi command should go here. - # See http://pythonhosted.org/openlmi-scripts/script-development.html#writing-setup-py - 'ver-sw = lmi.scripts.cmdver:CmdverSw', - 'ver-hw = lmi.scripts.cmdver:CmdverHw', - 'ver = lmi.scripts.cmdver:Cmdver', - ], - }, - ) diff --git a/test/imode/builtin_help.out b/test/imode/builtin_help.out deleted file mode 100644 index 016468d..0000000 --- a/test/imode/builtin_help.out +++ /dev/null @@ -1,15 +0,0 @@ -Built-ins. - -Usage: - : cd [<path>] - : .. - : pwd - : (help | -h | --help) - -Description: - cd Nest to a subcommand. Accepts a sequence of subcommands separated - with "/". If "/" is given, top-level command becomes the active one. - Other supported special symbols are ".." and ".". - .. Make parent command the active one. Same as issuing ":cd ..". - pwd Print current command path. - help Show this help. diff --git a/test/imode/cd_test.exp b/test/imode/cd_test.exp deleted file mode 100644 index c241c89..0000000 --- a/test/imode/cd_test.exp +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/expect -f -# Test :cd and :pwd built-in commands. -# -# Environment variables: -# LMI - path to lmi meta-command executable with optional argumenta. -# -# Last change dir is unsuccessful. -# Exits with the exit code os spawned lmi shell. - -set timeout 1 - -expect_after { - timeout { exit -1 } -} - -proc send1l {in out} { - send "$in\r" - expect "$out" -} - -proc send2l {in out1 out2} { - send "$in\r" - expect "$out1" - expect "$out2" -} - -set LMI lmi -if {[info exists env(LMI)]} { - set LMI $env(LMI) -} -spawn {*}$LMI -expect "lmi> " -send2l ": pwd" "/lmi" "lmi> " -send1l ": cd ." "lmi> " -send2l " : pwd " "/lmi" "lmi> " -send1l ":cd .." "lmi> " -send1l ":cd /" "lmi> " -send1l " : cd // " "lmi> " -send1l ": cd test" ">test> " -send2l ": pwd" "/lmi/test" ">test> " -send1l ":cd ." ">test> " -send1l ": .." "lmi> " -send2l ":pwd" "/lmi" "lmi> " -send1l ":cd test" ">test> " -send1l ":cd / " "lmi> " -send1l ":cd test/" ">test> " -send1l ":cd ../test" ">test> " -send1l ":cd .././test/.././test/../" "lmi> " -send1l ":cd test/./" ">test> " -send1l ":cd list" ">>list> " -send2l ":pwd" "/lmi/test/list" ">>list> " -send1l ":cd .." ">test> " -send1l ":cd /lmi/test/list" "list> " -send1l ":cd /lmi" "lmi> " -send1l ":cd /test/list" "list> " -send1l ":cd" "list> " -send1l ":cd /./test/../test/./list" ">>list> " -send2l ":cd /foo" "No such subcommand \"foo\"." ">>list> " -send2l ":cd pkgs" "Can not nest to subcommand \"pkgs\" which is not a multiplexer." ">>list> " -send2l ":cd ../show" "Can not nest to subcommand \"../show\" which lacks any help message." ">>list> " -send2l ":cd foo" "No such subcommand \"foo\"." ">>list> " -send "" -expect ">test> " -send "" -expect "lmi> " -send "" -expect "" -catch wait result -exit [lindex $result 3] diff --git a/test/imode/complete_builtin.exp b/test/imode/complete_builtin.exp deleted file mode 100644 index 9f9ef88..0000000 --- a/test/imode/complete_builtin.exp +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/expect -f - -set timeout 1 - -expect_after { - timeout { exit -1 } -} - -set LMI lmi -if {[info exists env(LMI)]} { - set LMI $env(LMI) -} -spawn {*}$LMI -expect "lmi> " -send ": " -expect ".. cd help pwd" -expect "lmi> :" -send ". " -expect "." -send "" -send "p " -expect "wd" -send "c " -expect "d" -send " test\r" -expect ">test> " -send " : " -expect ".. cd help pwd" -send "h " -expect "elp" -send "" -expect ">test> " -send "EOF\r" -expect "lmi> " -send "EOF\r" -catch wait result -exit [lindex $result 3] diff --git a/test/imode/completion.exp b/test/imode/completion.exp deleted file mode 100644 index d9ea477..0000000 --- a/test/imode/completion.exp +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/expect -f - -set timeout 1 - -expect_after { - timeout { exit -1 } -} - -set LMI lmi -if {[info exists env(LMI)]} { - set LMI $env(LMI) -} - -spawn {*}$LMI -expect "lmi> " -send " " -expect "EOF exit help test" -send "E " -expect "EOF" -send "" -expect "lmi> " -send "e " -expect "xit" -send " " -expect "exit" -expect "lmi> exit" -send "\r" -expect "" -catch wait result -if {[lindex $result 3] != 0} { - exit [expr 10 + [lindex $result 3]] -} else { - puts "\n" -} - -spawn {*}$LMI -expect "lmi> " -send ":cd test\r" -expect ">test> " -send " " -expect "EOF exit help list log show" -send "l " -expect "list log" -expect ">test> l" -send "i " -expect "st" -send "" -expect ">test> " -send "ex " -expect "it" -send " 20\r" -expect "" -catch wait result -exit [lindex $result 3] diff --git a/test/imode/empty_lines.exp b/test/imode/empty_lines.exp deleted file mode 100644 index c5fdb77..0000000 --- a/test/imode/empty_lines.exp +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/expect -f - -set timeout 1 - -expect_after { - timeout { exit -1 } -} - -set LMI lmi -if {[info exists env(LMI)]} { - set LMI $env(LMI) -} -spawn {*}$LMI -expect "lmi> " -send "\r" -expect "lmi> " -send "\r" -expect "lmi> " -send " \r" -expect "lmi> " -send " : pwd\r" -expect "/lmi" -expect "lmi> " -send "exit\r" -catch wait result -exit [lindex $result 3] diff --git a/test/imode/empty_lines.out b/test/imode/empty_lines.out deleted file mode 100644 index e03a6dc..0000000 --- a/test/imode/empty_lines.out +++ /dev/null @@ -1 +0,0 @@ -/lmi diff --git a/test/imode/empty_lines.txt b/test/imode/empty_lines.txt deleted file mode 100644 index 006be60..0000000 --- a/test/imode/empty_lines.txt +++ /dev/null @@ -1,6 +0,0 @@ - - -:cd . -:pwd - - diff --git a/test/imode/eof.exp b/test/imode/eof.exp deleted file mode 100644 index 56187ec..0000000 --- a/test/imode/eof.exp +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/expect -f - -set timeout 1 - -expect_after { - timeout { exit -1 } -} - -spawn lmi -expect "lmi> " -if {[llength $argv] > 0} { - send "[lindex $argv 0]\r" - expect { - timeout { exit -2 } - "[lindex $argv 1]" - } - expect "lmi> " -} -close -catch wait result -exit [lindex $result 3] diff --git a/test/imode/exit.exp b/test/imode/exit.exp deleted file mode 100644 index 55cf207..0000000 --- a/test/imode/exit.exp +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/expect -f - -set timeout 1 - -expect_after { - timeout { exit -1 } -} - -if {[llength $argv] > 0} { - set exit_arg " [lindex $argv 0]" -} else { - set exit_arg "" -} - -spawn lmi -expect "lmi> " -send "exit$exit_arg\r" -expect eof -catch wait result -exit [lindex $result 3] diff --git a/test/imode/help.out b/test/imode/help.out deleted file mode 100644 index 4a89747..0000000 --- a/test/imode/help.out +++ /dev/null @@ -1,13 +0,0 @@ - -Static commands -=============== -EOF exit help - -Application commands (type help <topic>): -========================================= -test - -Built-in commands (type :help): -=============================== -:.. :cd :pwd :help - diff --git a/test/imode/help_exit.out b/test/imode/help_exit.out deleted file mode 100644 index c6ab1d1..0000000 --- a/test/imode/help_exit.out +++ /dev/null @@ -1,3 +0,0 @@ -Terminate the shell. - -Usage: exit [<exit_code>] diff --git a/test/imode/help_foo.out b/test/imode/help_foo.out deleted file mode 100644 index 134c3f8..0000000 --- a/test/imode/help_foo.out +++ /dev/null @@ -1,14 +0,0 @@ -error : No such command "foo". - -Static commands -=============== -EOF exit help - -Application commands (type help <topic>): -========================================= -test - -Built-in commands (type :help): -=============================== -:.. :cd :pwd :help - diff --git a/test/imode/help_help.out b/test/imode/help_help.out deleted file mode 100644 index bddf2cd..0000000 --- a/test/imode/help_help.out +++ /dev/null @@ -1,4 +0,0 @@ -Print the list of supported commands with short description. -If a subcommand is given, its detailed help will be printed. - -Usage: help [<subcommand>...] diff --git a/test/imode/help_test.out b/test/imode/help_test.out deleted file mode 100644 index 4d69313..0000000 --- a/test/imode/help_test.out +++ /dev/null @@ -1,7 +0,0 @@ -Test command for lmi-metacommand. - -Usage: - test list <cmd> [<args> ...] - test show pkg <name> - test show repo <name> - test log <cmd> [<args> ...] diff --git a/test/imode/lmi_test_help.out b/test/imode/lmi_test_help.out deleted file mode 100644 index 8b93267..0000000 --- a/test/imode/lmi_test_help.out +++ /dev/null @@ -1,6 +0,0 @@ -Commands: - help - Print the list of supported commands with short description. - test - Test command for lmi-metacommand. - -For more informations about particular command type: - help <command> diff --git a/test/imode/run_test_cmd.exp b/test/imode/run_test_cmd.exp deleted file mode 100644 index f61b7a0..0000000 --- a/test/imode/run_test_cmd.exp +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/expect -f -# Environment variables: -# LMI - path to lmi meta-command executable with optional argumenta. -# -# Arguments: -# OUTFILE - Where to write the output of COMMAND. -# PATH - Command path where we change before execution. -# PROMPT - Prompt that is expected to appear after changing to PATH. -# COMMAND - Command to execute on given PATH. -# -# Exits with the exit code of spawned lmi shell. - -set timeout 2 - -expect_after { - timeout { exit -1 } -} - -set OUTFILE [open [lindex $argv 0] w] -set PROMPT [lindex $argv 2] -set LMI lmi -if {[info exists env(LMI)]} { - set LMI $env(LMI) -} -spawn -noecho sh -c "$LMI 2> /dev/null" -expect "lmi> " -send ":cd [lindex $argv 1]\r" -expect $PROMPT -send "[lindex $argv 3]\r" -expect -re ".*" {} -set REG {([^\n]+)\n(.*)} -expect -re "$REG$PROMPT\s*" { - puts -nonewline $OUTFILE [string map {"\r" ""} "$expect_out(2,string)"] -} -send "exit\r" -expect eof - -catch wait result -exit [lindex $result 3] diff --git a/test/imode/test_builtin_help.txt b/test/imode/test_builtin_help.txt deleted file mode 100644 index 99cdb2b..0000000 --- a/test/imode/test_builtin_help.txt +++ /dev/null @@ -1,2 +0,0 @@ -: cd test -: help diff --git a/test/imode/test_help.out b/test/imode/test_help.out deleted file mode 100644 index 39176aa..0000000 --- a/test/imode/test_help.out +++ /dev/null @@ -1,10 +0,0 @@ -Test command for lmi-metacommand. - -Usage: - test list <cmd> [<args> ...] - test show pkg <name> - test show repo <name> - test log <cmd> [<args> ...] - -To get help for built-in commands, type: - :help diff --git a/test/imode/test_help.txt b/test/imode/test_help.txt deleted file mode 100644 index e17ac65..0000000 --- a/test/imode/test_help.txt +++ /dev/null @@ -1,2 +0,0 @@ -:cd test -help diff --git a/test/imode/test_help_exit.txt b/test/imode/test_help_exit.txt deleted file mode 100644 index 5470ee3..0000000 --- a/test/imode/test_help_exit.txt +++ /dev/null @@ -1,2 +0,0 @@ -:cd test -help exit diff --git a/test/imode/test_help_list.out b/test/imode/test_help_list.out deleted file mode 100644 index 3f81637..0000000 --- a/test/imode/test_help_list.out +++ /dev/null @@ -1,5 +0,0 @@ -List various types of software data. - -Usage: - list pkgs [(--installed | --available)] - list repos [(--enabled | --disabled)] diff --git a/test/imode/test_help_list.txt b/test/imode/test_help_list.txt deleted file mode 100644 index 6ccd253..0000000 --- a/test/imode/test_help_list.txt +++ /dev/null @@ -1,2 +0,0 @@ -:cd test -help list diff --git a/test/imode/test_help_show.out b/test/imode/test_help_show.out deleted file mode 100644 index 4d69313..0000000 --- a/test/imode/test_help_show.out +++ /dev/null @@ -1,7 +0,0 @@ -Test command for lmi-metacommand. - -Usage: - test list <cmd> [<args> ...] - test show pkg <name> - test show repo <name> - test log <cmd> [<args> ...] diff --git a/test/imode/test_help_show.txt b/test/imode/test_help_show.txt deleted file mode 100644 index bd8ae57..0000000 --- a/test/imode/test_help_show.txt +++ /dev/null @@ -1,2 +0,0 @@ -:cd test -help show diff --git a/test/imode/test_list_opt_help.out b/test/imode/test_list_opt_help.out deleted file mode 100644 index 92f76ee..0000000 --- a/test/imode/test_list_opt_help.out +++ /dev/null @@ -1,5 +0,0 @@ -List various types of software data. - -Usage: - test list pkgs [(--installed | --available)] - test list repos [(--enabled | --disabled)] diff --git a/test/imode/test_opt_help.out b/test/imode/test_opt_help.out deleted file mode 100644 index 4d69313..0000000 --- a/test/imode/test_opt_help.out +++ /dev/null @@ -1,7 +0,0 @@ -Test command for lmi-metacommand. - -Usage: - test list <cmd> [<args> ...] - test show pkg <name> - test show repo <name> - test log <cmd> [<args> ...] diff --git a/test/logging/defaults.conf b/test/logging/defaults.conf deleted file mode 100644 index 047572b..0000000 --- a/test/logging/defaults.conf +++ /dev/null @@ -1 +0,0 @@ -[Log] diff --git a/test/logging/log_all_debug.file b/test/logging/log_all_debug.file deleted file mode 100644 index 69326b5..0000000 --- a/test/logging/log_all_debug.file +++ /dev/null @@ -1,9 +0,0 @@ -DEBUG: Running command "test". -DEBUG: Found registered command "test". -WARNING: Command "lmi.scripts.cmdtest.Show" is missing usage string. It will be inherited from parent command. -INFO: Connected to kvm-rhel7 -DEBUG: One Ring to rule them all. -INFO: One Ring to find them. -WARNING: One Ring to bring them all -ERROR: and in the darkness bind them. -CRITICAL: In the land of Mordor where the Shadows lie. diff --git a/test/logging/log_all_debug.stderr b/test/logging/log_all_debug.stderr deleted file mode 100644 index 31573aa..0000000 --- a/test/logging/log_all_debug.stderr +++ /dev/null @@ -1,9 +0,0 @@ -Running command "test". -Found registered command "test". -warning : Command "lmi.scripts.cmdtest.Show" is missing usage string. It will be inherited from parent command. -Connected to kvm-rhel7 -One Ring to rule them all. -One Ring to find them. -warning : One Ring to bring them all -error : and in the darkness bind them. -critical: In the land of Mordor where the Shadows lie. diff --git a/test/logging/log_all_debug.stdout b/test/logging/log_all_debug.stdout deleted file mode 100644 index 98d7a80..0000000 --- a/test/logging/log_all_debug.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Additional information. -Enjoy tracebacks. -I can not be more verbose than this! diff --git a/test/logging/log_all_debug_notrace.stdout b/test/logging/log_all_debug_notrace.stdout deleted file mode 100644 index dfd2b72..0000000 --- a/test/logging/log_all_debug_notrace.stdout +++ /dev/null @@ -1,2 +0,0 @@ -Additional information. -I can not be more verbose than this! diff --git a/test/logging/log_all_default.stdout b/test/logging/log_all_default.stdout deleted file mode 100644 index e69de29..0000000 --- a/test/logging/log_all_default.stdout +++ /dev/null diff --git a/test/logging/log_all_error.stderr b/test/logging/log_all_error.stderr deleted file mode 100644 index f8f1c07..0000000 --- a/test/logging/log_all_error.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error : and in the darkness bind them. -critical: In the land of Mordor where the Shadows lie. diff --git a/test/logging/log_all_info.stderr b/test/logging/log_all_info.stderr deleted file mode 100644 index 2abec7e..0000000 --- a/test/logging/log_all_info.stderr +++ /dev/null @@ -1,5 +0,0 @@ -warning : Command "lmi.scripts.cmdtest.Show" is missing usage string. It will be inherited from parent command. -One Ring to find them. -warning : One Ring to bring them all -error : and in the darkness bind them. -critical: In the land of Mordor where the Shadows lie. diff --git a/test/logging/log_all_info.stdout b/test/logging/log_all_info.stdout deleted file mode 100644 index 17af42d..0000000 --- a/test/logging/log_all_info.stdout +++ /dev/null @@ -1,2 +0,0 @@ -Additional information. -Enjoy tracebacks. diff --git a/test/logging/log_all_silent.stdout b/test/logging/log_all_silent.stdout deleted file mode 100644 index 73f7ea6..0000000 --- a/test/logging/log_all_silent.stdout +++ /dev/null @@ -1,2 +0,0 @@ -Let's be silent. -Enjoy tracebacks. diff --git a/test/logging/log_all_warn.file b/test/logging/log_all_warn.file deleted file mode 100644 index 4eaadb9..0000000 --- a/test/logging/log_all_warn.file +++ /dev/null @@ -1,4 +0,0 @@ -WARNING: Command "lmi.scripts.cmdtest.Show" is missing usage string. It will be inherited from parent command. -WARNING: One Ring to bring them all -ERROR: and in the darkness bind them. -CRITICAL: In the land of Mordor where the Shadows lie. diff --git a/test/logging/log_all_warn.stderr b/test/logging/log_all_warn.stderr deleted file mode 100644 index eab784d..0000000 --- a/test/logging/log_all_warn.stderr +++ /dev/null @@ -1,4 +0,0 @@ -warning : Command "lmi.scripts.cmdtest.Show" is missing usage string. It will be inherited from parent command. -warning : One Ring to bring them all -error : and in the darkness bind them. -critical: In the land of Mordor where the Shadows lie. diff --git a/test/run.sh b/test/run.sh deleted file mode 100755 index 03c87ae..0000000 --- a/test/run.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com - -retval=0 -for test in test_*.sh; do - bash ./$test - [ "$retval" = 0 ] && retval=$? -done - -exit $retval diff --git a/test/subcmd/lmi/__init__.py b/test/subcmd/lmi/__init__.py deleted file mode 100644 index f752629..0000000 --- a/test/subcmd/lmi/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -__import__('pkg_resources').declare_namespace(__name__) diff --git a/test/subcmd/lmi/scripts/__init__.py b/test/subcmd/lmi/scripts/__init__.py deleted file mode 100644 index f752629..0000000 --- a/test/subcmd/lmi/scripts/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -__import__('pkg_resources').declare_namespace(__name__) diff --git a/test/subcmd/lmi/scripts/cmdtest/__init__.py b/test/subcmd/lmi/scripts/cmdtest/__init__.py deleted file mode 100644 index e15fd97..0000000 --- a/test/subcmd/lmi/scripts/cmdtest/__init__.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> -# -""" -Test command for lmi-metacommand. - -Usage: - %(cmd)s list <cmd> [<args> ...] - %(cmd)s show pkg <name> - %(cmd)s show repo <name> - %(cmd)s log <cmd> [<args> ...] -""" - -from lmi.scripts.common import get_logger -from lmi.scripts.common import errors -from lmi.scripts.common import command -from lmi.scripts.common import configuration - -LOG = get_logger(__name__) - -class ListPackages(command.LmiLister): - OPT_NO_UNDERSCORES = True - COLUMNS = ('Name', 'Arch') - - def execute(self, ns, installed, available): - fltr = lambda a: True - if installed: - fltr = lambda a: a[2] - elif available: - fltr = lambda a: not a[2] - for pkg in PACKAGES: - if fltr(pkg): - yield (pkg[0], pkg[1]) - -class ListRepos(command.LmiLister): - OPT_NO_UNDERSCORES = True - - def execute(self, ns, enabled, disabled): - fltr = lambda a: True - if enabled: - fltr = lambda a: a[1] - elif disabled: - fltr = lambda a: not a[1] - if self.app.config.verbose: - props = ('Name', 'Enabled') - else: - props = ('Name', ) - yield props - for repo in REPOSITORIES: - if not fltr(repo): - continue - yield tuple(v for _, v in zip(props, repo)) - -class Lister(command.LmiCommandMultiplexer): - """ - List various types of software data. - - Usage: - %(cmd)s pkgs [(--installed | --available)] - %(cmd)s repos [(--enabled | --disabled)] - """ - COMMANDS = { - 'pkgs' : ListPackages, - 'repos' : ListRepos - } - OWN_USAGE = True - -class ShowPackage(command.LmiLister): - COLUMNS = ("Prop", "Value") - - def execute(self, ns, name): - pkgd = { p[0] : p for p in PACKAGES } - if not name in pkgd: - raise errors.LmiFailed('No such package "%s".' % name) - for n, v in zip(('Name', 'Architecture', 'Installed'), pkgd[name]): - yield n, v - -class ShowRepository(command.LmiLister): - - def execute(self, ns, name): - repod = { r[0] : r for r in REPOSITORIES } - if not name in repod: - raise errors.LmiFailed('No such repository "%s".' % name) - if self.app.config.verbose: - props = ('Name', 'Enabled', 'Packages') - else: - props = ('Name', 'Enabled') - value_map = {n: v for n, v in zip(props, repod[name])} - - return (props, tuple(value_map[p] for p in props)) - -class Show(command.LmiCommandMultiplexer): - COMMANDS = { - 'pkg' : ShowPackage, - 'repo' : ShowRepository - } - -class LogLevel(command.LmiCheckResult): - EXPECT = None - - def execute(self, ns, _debug, _info, _warn, _error, _critical, - message=None, args=None): - for level in ('debug', 'info', 'warn', 'error', 'critical'): - if locals()['_' + level]: - break - if not message: - message = "This is %s message." % level - if not args: - args = [] - getattr(LOG(), level)(message, *args) - -class LogAll(command.LmiCheckResult): - EXPECT = None - POEM = ( - 'One Ring to rule them all.', - 'One Ring to find them.', - 'One Ring to bring them all', - 'and in the darkness bind them.', - 'In the land of Mordor where the Shadows lie.') - - def execute(self, ns, _with_traceback): - for verse, level in zip(LogAll.POEM, - ('debug', 'info', 'warn', 'error', 'critical')): - if _with_traceback: - try: - raise RuntimeError('S**t happens!') - except RuntimeError as err: - getattr(LOG(), level)(verse, exc_info=err) - else: - getattr(LOG(), level)(verse) - if self.app.config.verbose: - self.app.stdout.write('Additional information.\n') - if self.app.config.silent: - self.app.stdout.write("Let's be silent.\n") - if self.app.config.trace: - self.app.stdout.write('Enjoy tracebacks.\n') - if self.app.config.verbosity >= self.app.config.OUTPUT_DEBUG: - self.app.stdout.write('I can not be more verbose than this!\n') - -class LogRaise(command.LmiCheckResult): - EXPECT = None - - def execute(self, ns, _lmi_failed=False): - if _lmi_failed: - raise errors.LmiFailed("You asked for it!") - raise RuntimeError("This shall make a nice traceback.") - -class Logger(command.LmiCommandMultiplexer): - """ - Command for logging testing. - - Usage: - %(cmd)s level (--debug | --info | --warn | --error | --critical) - [<message> <args>...] - %(cmd)s all [--with-traceback] - %(cmd)s raise [--lmi-failed] - """ - COMMANDS = { - 'level' : LogLevel, - 'all' : LogAll, - 'raise' : LogRaise - } - OWN_USAGE = True - -Test = command.register_subcommands( - 'Test', __doc__, - { 'list' : Lister, - 'show' : Show, - 'log' : Logger, - }, - ) - -PACKAGES = ( - # Name, Arch, Installed - ('pywbem', 'noarch', True), - ('hwdata', 'noarch', True), - ('tog-pegasus', 'x86_64', False), - ('python-docopt', 'noarch', False)) - -REPOSITORIES = ( - # Name, Enabled, Packages - ('fedora', True, 1000), - ('updates', False, 500)) - diff --git a/test/subcmd/setup.py b/test/subcmd/setup.py deleted file mode 100644 index 20a50a1..0000000 --- a/test/subcmd/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python - -from setuptools import setup, find_packages - -setup( - name="openlmi-scripts-cmdtest", - version="0.0.1", - description='Test command for lmi', - author='Michal Minar', - author_email='miminar@redhat.com', - url='http://openlmi.org', - platforms=['Any'], - license="BSD", - classifiers=[ - 'License :: OSI Approved :: BSD License', - 'Operating System :: POSIX :: Linux', - 'Topic :: System :: Systems Administration', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - install_requires=['openlmi-scripts >= 0.2.6'], - - namespace_packages=['lmi', 'lmi.scripts'], - packages=['lmi', 'lmi.scripts', 'lmi.scripts.cmdtest'], - include_package_data=True, - - entry_points={ - 'lmi.scripts.cmd': [ - 'test = lmi.scripts.cmdtest:Test', - ], - }, - ) diff --git a/test/test_cmd.sh b/test/test_cmd.sh deleted file mode 100644 index e846699..0000000 --- a/test/test_cmd.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> - -. ./base.sh - -# Set the full test name -TEST="openlmi-scripts/test/test_cmd.sh" - -# Package being tested -PACKAGE="openlmi-scripts" - -rlJournalStart - -rlPhaseStartSetup - rlLogInfo "Creating temporary python sandbox" - sandbox=`mktemp -d` - export PYTHONPATH="$sandbox" - pushd .. - rlLogInfo "Installing lmi meta-command" - rlRun "python setup.py develop --install-dir=$sandbox" - popd - export "$sandbox:$PATH" -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test help messages without subommands" - - rlRun -s "$LMI_ --help" - rlAssertGrep "^Usage:$" $rlRun_LOG - rlAssertGrep "^Options:$" $rlRun_LOG - rlAssertGrep "^Commands:$" $rlRun_LOG - rlAssertGrep "^ help$" $rlRun_LOG - rlRun "head -n 1 $rlRun_LOG | grep -q '[[:alpha:]]\+'" 0 \ - "Test whether first line is not blank" - rm $rlRun_LOG - - rlRun -s "$LMI help" - rlAssertNotDiffer "cmd/help_without_test.out" $rlRun_LOG - rm $rlRun_LOG - - # suppress warning messages - rlRun -s "$LMI -q help foo" - rlAssertNotDiffer "cmd/help_foo.out" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help help" - rlAssertNotDiffer "cmd/help_help.out" $rlRun_LOG - rm $rlRun_LOG - -rlPhaseEnd - -rlPhaseStartSetup - rlLogInfo "Installing testing command" - pushd subcmd - rlRun "python setup.py develop --install-dir=$sandbox" - popd -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test help messages with test subcommand" - - rlRun -s "$LMI_ --help" - rlAssertGrep "^Usage:$" $rlRun_LOG - rlAssertGrep "^Options:$" $rlRun_LOG - rlAssertGrep "^Commands:$" $rlRun_LOG - rlAssertGrep "^ help test$" $rlRun_LOG - rlRun "head -n 1 $rlRun_LOG | grep -q '[[:alpha:]]\+'" 0 \ - "Test whether first line is not blank" - rm $rlRun_LOG - - rlRun -s "$LMI help 2>/dev/null" - rlAssertNotDiffer "cmd/help_with_test.out" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help test 2>/dev/null" - rlAssertNotDiffer "cmd/help_test.out" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI test --help 2>/dev/null" - rlAssertNotDiffer "cmd/help_test.out" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI test list --help 2>/dev/null" - rlAssertNotDiffer "cmd/help_test_list.out" $rlRun_LOG - rm $rlRun_LOG - -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test the *no headings* option" - with_headings=`mktemp with_headingsXXXX` - without_headings=`mktemp without_headingsXXXX` - expected_colonized=`mktemp expectedXXXX` - - rlRun -s "$LMI test show pkg hwdata >$with_headings" - whl=`cat $with_headings | wc -l` - rlRun -s "$LMI -N test show pkg hwdata >$without_headings" - nhl=`cat $without_headings | wc -l` - rlAssertEquals "Output without headings needs to be one line shorter" \ - $((whl - 1)) $nhl - rlAssertGrep "Prop\s\+Value" $with_headings - rlAssertNotGrep "Prop\s\+Value" $without_headings - cat >$expected_colonized <<EOF -Name:hwdata -Architecture:noarch -Installed:True -EOF - rlRun "sed -n -e '/^warning :/ d' -e '1 !s/\s\+/:/gp' $with_headings | cmp $expected_colonized -" 0 \ - "Compare the output to expected with reduced spaces" - - rlRun -s "$LMI test show repo fedora >$with_headings" - whl=`cat $with_headings | wc -l` - rlRun -s "$LMI -N test show repo fedora >$without_headings" - nhl=`cat $without_headings | wc -l` - rlAssertEquals "Output without headings needs to be one line shorter" \ - $((whl - 1)) $nhl - rlAssertGrep "^Name\s\+Enabled" $with_headings - rlAssertNotGrep "^Name\s\+Enabled" $without_headings - echo "fedora:True" >$expected_colonized - rlRun "sed -n -e '/^warning :/ d' -e '1 !s/\s\+/:/gp' $with_headings | cmp $expected_colonized -" 0 \ - "Compare the output to expected with reduced spaces" - - rlRun -s "$LMI -v test show repo fedora >$with_headings" - whl=`cat $with_headings | wc -l` - rlRun -s "$LMI -v -N test show repo fedora >$without_headings" - nhl=`cat $without_headings | wc -l` - rlAssertEquals "Output without headings needs to be one line shorter" \ - $((whl - 1)) $nhl - rlAssertGrep "^Name\s\+Enabled\s\+Packages" $with_headings - rlAssertNotGrep "^Name\s\+Enabled\s\+Packages" $without_headings - sed -i '1 s/$/:1000/' $expected_colonized - rlRun "sed -n -e '/^warning :/ d' -e '1 !s/\s\+/:/gp' $with_headings | cmp $expected_colonized -" 0 \ - "Compare the output to expected with reduced spaces" - - rm $with_headings $without_headings $expected_colonized -rlPhaseEnd - -rlPhaseStartCleanup - rlLogInfo "Removing temporary python sandbox" - rm -rf "$sandbox" -rlPhaseEnd - -rlJournalPrintText -rlJournalEnd diff --git a/test/test_imode.sh b/test/test_imode.sh deleted file mode 100644 index 6cd2ab2..0000000 --- a/test/test_imode.sh +++ /dev/null @@ -1,270 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> - -. ./base.sh - -# Set the full test name -TEST="openlmi-scripts/test/test_imode.sh" - -# Package being tested -PACKAGE="openlmi-scripts" - -function run_and_compare_output() { - # Run lmi command and compare its output to a contents of given file. - # - # Args: - # command - command to run which is executed in subshell - # expected - file name with expected output stored in imode without - # extension - rlRun -s "$1" 0 - rlRun -l "cmp imode/$2.out $rlRun_LOG" 0 - rm $rlRun_LOG -} - -function run_test_command() { - # Run command in interactive mode with expect script - # `imode/run_test_cmd.exp`. Command is run iteratively from different - # command namespaces. stderr is redirected to /dev/null when running - # the `command`. - # - # Args: - # command - command path (without `lmi` prefix) - # expected - path to a file with expected output - # dir... - command namespaces to nest to before running command - # - # Common prefix of `command` and current namespace is removed from command - # in each iteration before running it. - cmd="$1" - expected_output="$2" - shift 2 - while [ $# -gt 0 ]; do - dir="$1" - cmd_="${cmd#$dir }" - out_file=`mktemp out_fileXXXX` - prompt='lmi> ' - path="$(echo $dir | tr ' ' '/')" - [ "$dir" = '.' ] || prompt=">$(echo $dir | sed 's/\([^ ]\+ \)/>/g')> " - rlRun "expect imode/run_test_cmd.exp $out_file \"$path\" \"$prompt\" \"$cmd_\"" 0 - rlAssertNotDiffer $expected_output $out_file - rm $out_file - shift - done -} - -function make_error_message() { - # Args: - # level - one of debug, info, warn, error, critical - # message - level=$1 - message="$2" - [ "$level" = 'warn' ] && level=warning - case "$level" in - warning) color='\x1b[38;5;11m'; ;; - error) color='\x1b[38;5;9m'; ;; - critical) color='\x1b[38;5;13m'; ;; - *) unset color; ;; - esac - if [[ -n "$color" ]]; then - printf "$color%-8s:\x1b[39m %s\n" $level "$message" - else - echo "$message" - fi -} - -rlJournalStart - -rlPhaseStartSetup - rlLogInfo "Creating temporary python sandbox" - sandbox=`mktemp -d` - export PYTHONPATH="$sandbox" - pushd .. - rlLogInfo "Installing lmi meta-command" - rlRun "python setup.py develop --install-dir=$sandbox" 0 - popd - export "$sandbox:$PATH" -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Try to import lmi meta-command" - rlRun 'python -c "import lmi.scripts._metacommand"' 0 -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test exit command" - rlRun "expect imode/exit.exp" 0 - rlRun "expect imode/exit.exp 0" 0 - rlRun "expect imode/exit.exp 1" 1 - rlRun "expect imode/exit.exp 2" 2 - rlRun "expect imode/exit.exp 10" 10 -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test end of file" - rlRun "expect imode/eof.exp" 0 - rlRun "expect imode/eof.exp ':pwd' '/lmi'" 0 - errmsg=`make_error_message error 'No such subcommand \"foo\".'` - rlRun "expect imode/eof.exp ':cd foo' \"$errmsg\"" 0 - rlRun "expect imode/empty_lines.exp" 0 - run_and_compare_output "$LMI < imode/empty_lines.txt" empty_lines -rlPhaseEnd - -rlPhaseStartSetup - rlLogInfo "Installing testing command" - pushd subcmd - rlRun "python setup.py develop --install-dir=$sandbox" - popd -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Check whether test command is installed" - run_and_compare_output "$LMI help 2>/dev/null" lmi_test_help -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test nesting to subcommands" - rlRun "expect imode/cd_test.exp" 1 -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test help messages in interactive mode" - run_and_compare_output "echo help | $LMI" "help" - run_and_compare_output "echo help help | $LMI" "help_help" - run_and_compare_output "echo \": help\" | $LMI" "builtin_help" - run_and_compare_output "echo help exit | $LMI 2>/dev/null" "help_exit" - run_and_compare_output "echo help test | $LMI 2>/dev/null" "help_test" - run_and_compare_output "$LMI 2>/dev/null < imode/test_help_exit.txt" \ - "help_exit" - run_and_compare_output "$LMI 2>/dev/null < imode/test_help.txt" \ - "test_help" - run_and_compare_output "$LMI 2>/dev/null < imode/test_builtin_help.txt" \ - "builtin_help" - run_and_compare_output "$LMI 2>/dev/null < imode/test_help_list.txt" \ - "test_help_list" - run_and_compare_output "$LMI 2>/dev/null < imode/test_help_show.txt" \ - "test_help_show" - run_and_compare_output "echo help foo | $LMI" "help_foo" - - rlLogInfo "Test --help" - outfile=`mktemp outfileXXXX` - expected=`mktemp expectedXXXX` - rlRun "expect imode/run_test_cmd.exp $outfile . 'lmi> ' 'test --help'" 0 - rlAssertNotDiffer imode/test_opt_help.out $outfile - - rlRun "expect imode/run_test_cmd.exp $outfile . 'lmi> ' 'test list --help'" 0 - rlAssertNotDiffer imode/test_list_opt_help.out $outfile - - cmd="test list pkgs --help" - rlRun "expect imode/run_test_cmd.exp $outfile . 'lmi> ' '$cmd'" 0 - rlAssertNotDiffer imode/test_list_opt_help.out $outfile - - cmd="test show --help" - rlRun "expect imode/run_test_cmd.exp $outfile . 'lmi> ' '$cmd'" 0 - rlAssertNotDiffer imode/test_opt_help.out $outfile - - cmd="test foo --help" - rlRun "expect imode/run_test_cmd.exp $outfile . 'lmi> ' '$cmd'" 0 - rlAssertNotDiffer imode/test_opt_help.out $outfile - - cmd="list --help" - sed 's/^ test / /' imode/test_list_opt_help.out >$expected - rlRun "expect imode/run_test_cmd.exp $outfile test '>test> ' '$cmd'" 0 - rlAssertNotDiffer $expected $outfile - - cmd="list pkgs --help" - rlRun "expect imode/run_test_cmd.exp $outfile test '>test> ' '$cmd'" 0 - rlAssertNotDiffer $expected $outfile - - cmd="show --help" - rlRun "expect imode/run_test_cmd.exp $outfile test '>test> ' '$cmd'" 0 - rlAssertNotDiffer imode/test_opt_help.out $outfile - - rm $expected $outfile - -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test completion in interactive mode" - # this also tests exit command nested in subcommand (with 20 as argument) - rlRun "expect imode/completion.exp" 20 - rlRun "expect imode/complete_builtin.exp" 0 -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Check behaviour of test subcommand" - - rlLogInfo \ - "First create files in non-interactive mode we will then compare to" - rlRun -s "$LMI test list pkgs 2>/dev/null" 0 - rlAssertEquals "List of all packages is sane" \ - `grep -E '^(tog-pegasus|pywbem|hwdata|python-docopt)' $rlRun_LOG | wc -l` 4 - all_packages=`mktemp all_packagesXXXX` - mv $rlRun_LOG $all_packages - - rlRun -s "$LMI test list pkgs --installed 2>/dev/null" 0 - rlAssertEquals "List of installed packages is sane"\ - `grep -E '^(pywbem|hwdata)' $rlRun_LOG | wc -l` 2 - installed_packages=`mktemp installed_packagesXXXX` - mv $rlRun_LOG $installed_packages - - rlRun -s "$LMI test list repos 2>/dev/null" 0 - rlAssertEquals "List of repositories is sane" \ - `grep -E '^(fedora|updates)' $rlRun_LOG | wc -l` 2 - all_repos=`mktemp all_reposXXXX` - mv $rlRun_LOG $all_repos - - rlRun -s "$LMI test show pkg hwdata 2>/dev/null" 0 - rlAssertEquals "Package description is sane" \ - `grep -E '^(Name|Arch|Installed)' $rlRun_LOG | wc -l` 3 - one_package=`mktemp one_packageXXXX` - mv $rlRun_LOG $one_package - - rlLogInfo "Now compare outputs to interactive mode" - - run_test_command 'test list pkgs' $all_packages \ - '.' 'test' 'test list' - run_test_command 'test list pkgs --installed' $installed_packages \ - '.' 'test' 'test list' - run_test_command 'test list repos' $all_repos \ - '.' 'test' 'test list' - run_test_command 'test show pkg hwdata' $one_package \ - '.' 'test' - - rm $all_packages $installed_packages $all_repos $one_package - -rlPhaseEnd - -rlPhaseStartCleanup - rlLogInfo "Removing temporary python sandbox" - rm -rf "$sandbox" -rlPhaseEnd - -rlJournalPrintText -rlJournalEnd diff --git a/test/test_logging.sh b/test/test_logging.sh deleted file mode 100644 index ef6060f..0000000 --- a/test/test_logging.sh +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> - -. ./base.sh - -# Set the full test name -TEST="openlmi-scripts/test/test_logging.sh" - -# Package being tested -PACKAGE="openlmi-scripts" - -# array of supported log levels -LEVELS=( critical error warn info debug ) - -DEBUG_CONFIG_TEMPLATE=" -[Main] -Trace = True -Verbosity = 2 - -[Log] -Level = DEBUG -LogToConsole = True -FileFormat = %%(levelname)s: %%(message)s -OutputFile = %s -" - -rlJournalStart - -rlPhaseStartSetup - rlLogInfo "Creating temporary python sandbox" - sandbox=`mktemp -d` - export PYTHONPATH="$sandbox" - pushd .. - rlLogInfo "Installing lmi meta-command" - rlRun "python setup.py develop --install-dir=$sandbox" 0 - popd - pushd subcmd - rlRun "python setup.py develop --install-dir=$sandbox" 0 - popd - export PATH="$sandbox:$PATH" -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test logging with default settings" - - LMICMD="$LMI -c logging/defaults.conf" - - stdout=`mktemp stdoutXXXX` - stderr=`mktemp stderrXXXX` - rlRun "$LMICMD test log all >$stdout 2>$stderr" 0 - rlAssertNotDiffer logging/log_all_default.stdout $stdout - rlAssertNotDiffer logging/log_all_warn.stderr $stderr - - rlRun "$LMICMD test log all --with-traceback >$stdout 2>$stderr" 0 - rlAssertNotDiffer logging/log_all_default.stdout $stdout - tmpout=`mktemp tmpoutXXXX` - grep '^\(warning\|error\|critical\)' $stderr > $tmpout - rlAssertNotDiffer logging/log_all_warn.stderr $tmpout - rlAssertEquals "Correct number of exceptions were raised" 3 \ - `grep '^RuntimeError: S\*\*t happens!' $stderr | wc -l` - rm $tmpout - - expected_out=`mktemp expected_stdoutXXXX` - expected_err=`mktemp expected_stderrXXXX` - for verbosity in 0 1 2; do - if [[ $verbosity -gt 0 ]]; then - option=`printf -- "-v %.0s" $(seq $verbosity)` - else - option='' - fi - CMD="$LMICMD $option test log level" - for level in `seq 0 $((${#LEVELS[@]} - 1))`; do - rlRun "$CMD --${LEVELS[$level]} >$stdout 2>$stderr" 0 - level_name=${LEVELS[$level]} - log_level_name=$level_name - [ $level_name = warn ] && log_level_name=warning - ( \ - if [ $verbosity = 2 ]; then \ - echo 'Running command "test".'; \ - echo 'Found registered command "test".'; \ - fi; \ - echo -n 'warning : Command "lmi.scripts.cmdtest.Show"'; \ - echo -n " is missing usage string. It will be"; \ - echo " inherited from parent command."; \ - if [ $verbosity = 2 ]; then \ - for opt_name in '_with_traceback' '_lmi_failed'; do \ - echo -n "Option \"$opt_name\" not handled in function"; \ - echo ' "level", ignoring.'; \ - done; \ - echo "Connected to $HOSTNAME"; \ - fi; \ - if [ $(($verbosity + 2)) -ge $level ]; then \ - if [ $level -lt 3 ]; then \ - printf "%-8s: " $log_level_name; \ - fi; \ - echo "This is $level_name message."; \ - fi \ - ) >$expected_err - rlAssertNotDiffer $expected_err $stderr - rlAssertEquals "Nothing is written to stdout" 0 `cat $stdout | wc -c` - done - done - - rm $stdout $stderr $expected_out $expected_err - -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test logging with debug settings" - - config_file=`mktemp configXXXX` - log_file=`mktemp log_fileXXXX` - printf "$DEBUG_CONFIG_TEMPLATE" $log_file >$config_file - - LMICMD="$LMI -c $config_file" - - stdout=`mktemp stdoutXXXX` - stderr=`mktemp stderrXXXX` - filter_debug=" | grep -v '^Option' > $stderr" - rlRun "$LMICMD test log all 2>&1 >$stdout $filter_debug" 0 - rlAssertNotDiffer logging/log_all_debug.stdout $stdout - rlAssertNotDiffer logging/log_all_debug.stderr $stderr - cat $log_file | grep -v '^DEBUG: Option' >${log_file}.tmp - mv ${log_file}{.tmp,} - rlAssertNotDiffer logging/log_all_debug.file $log_file - rm $log_file - - rlRun "$LMICMD test log all --with-traceback 2>&1 >$stdout $filter_debug" 0 - rlAssertNotDiffer logging/log_all_debug.stdout $stdout - tmpout=`mktemp tmpoutXXXX` - grep '^\(warning\|error\|critical\)' $stderr > $tmpout - rlAssertNotDiffer logging/log_all_warn.stderr $tmpout - rlAssertEquals "Correct number of exceptions were raised" 5 \ - `grep '^RuntimeError: S\*\*t happens!' $stderr | wc -l` - rlAssertEquals "Correct number of exceptions were raised" 5 \ - `grep '^RuntimeError: S\*\*t happens!' $log_file | wc -l` - rm $tmpout $log_file - - rlLogInfo "Try override log-file" - new_log_file=`mktemp new_log_fileXXXX` - rlRun "$LMICMD --log-file $new_log_file test log all 2>&1 >$stdout $filter_debug" 0 - rlAssertNotDiffer logging/log_all_debug.stdout $stdout - rlAssertNotDiffer logging/log_all_debug.stderr $stderr - cat $new_log_file | grep -v '^DEBUG: Option' >${new_log_file}.tmp - mv ${new_log_file}{.tmp,} - rlAssertNotDiffer logging/log_all_debug.file $new_log_file - rlRun "[ -e $log_file ]" 1 "Log file from config file shall not be written" - - [ -e $new_log_file ] && rm $new_log_file - [ -e $log_file ] && rm $log_file - - rlLogInfo "Try to disable logging to a file" - rlRun "$LMICMD --log-file '' test log all 2>&1 >$stdout $filter_debug" 0 - rlAssertNotDiffer logging/log_all_debug.stdout $stdout - rlAssertNotDiffer logging/log_all_debug.stderr $stderr - rlRun "[ -e $log_file ]" 1 "Log file from config file shall not be written" - [ -e $log_file ] && rm $log_file - - rlLogInfo "Try override verbosity" - rlRun "$LMICMD -q test log all 2>&1 >$stdout $filter_debug" 0 - rlAssertNotDiffer logging/log_all_silent.stdout $stdout - rlAssertNotDiffer logging/log_all_error.stderr $stderr - cat $log_file | grep -v '^DEBUG: Option' >${log_file}.tmp - mv ${log_file}{.tmp,} - rlAssertNotDiffer logging/log_all_debug.file $log_file - rm $log_file - - rlRun "$LMICMD -v test log all >$stdout 2>$stderr" 0 - rlAssertNotDiffer logging/log_all_info.stdout $stdout - rlAssertNotDiffer logging/log_all_info.stderr $stderr - cat $log_file | grep -v '^DEBUG: Option' >${log_file}.tmp - mv ${log_file}{.tmp,} - rlAssertNotDiffer logging/log_all_debug.file $log_file - rm $log_file - - rlLogInfo "Change file log level" - sed -i 's/DEBUG/WARNING/' $config_file - rlAssertGrep 'Level = WARNING' $config_file - rlRun "$LMICMD test log all 2>&1 >$stdout $filter_debug" 0 - rlAssertNotDiffer logging/log_all_debug.stdout $stdout - rlAssertNotDiffer logging/log_all_debug.stderr $stderr - rlAssertNotDiffer logging/log_all_warn.file $log_file - [ -e $log_file ] && rm $log_file - - rlLogInfo "Disable logging to console" - sed -i 's/\(LogToConsole.*\)True/\1False/' $config_file - rlAssertGrep 'LogToConsole = False' $config_file - rlRun "$LMICMD test log all >$stdout 2>$stderr" 0 - rlAssertNotDiffer logging/log_all_debug.stdout $stdout - rlAssertEquals "Nothing is written to stderr" `cat $stderr | wc -c` 0 - rlAssertNotDiffer logging/log_all_warn.file $log_file - [ -e $log_file ] && rm $log_file - - rlLogInfo "Override tracing" - rlRun "$LMICMD --notrace test log all >$stdout 2>$stderr" 0 - rlAssertNotDiffer logging/log_all_debug_notrace.stdout $stdout - rlAssertEquals "Nothing is written to stderr" `cat $stderr | wc -c` 0 - rlAssertNotDiffer logging/log_all_warn.file $log_file - - for f in $log_file $new_log_file $config_file $stdout $stderr; do - [ -e $f ] && rm $f - done - -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test logging with settings" - - stdout=`mktemp stdoutXXXX` - stderr=`mktemp stderrXXXX` - rlRun "$LMI -c logging/defaults.conf test log all >$stdout 2>$stderr" 0 - rlAssertNotDiffer $stdout logging/log_all_default.stdout - rlAssertNotDiffer $stderr logging/log_all_warn.stderr - rm $stdout $stderr -rlPhaseEnd - - -rlPhaseStartCleanup - rlLogInfo "Removing temporary python sandbox" - rm -rf "$sandbox" -rlPhaseEnd diff --git a/test/test_unit.sh b/test/test_unit.sh deleted file mode 100644 index 04fe17b..0000000 --- a/test/test_unit.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> - -. ./base.sh - -# Set the full test name -TEST="openlmi-scripts/test/test_cmd.sh" - -PACKAGE="openlmi-scripts" - -rlJournalStart - -rlPhaseStartSetup - rlLogInfo "Creating temporary python sandbox" - sandbox=`mktemp -d` - export PYTHONPATH="$sandbox" - pushd .. - rlLogInfo "Installing lmi meta-command" - rlRun "python setup.py develop --install-dir=$sandbox" - popd - export "$sandbox:$PATH" -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Running unittests" - - pushd unit - for i in test_*.py; do - rlRun "python $i" - done - popd # unit - -rlPhaseEnd - -rlPhaseStartCleanup - rlLogInfo "Removing temporary python sandbox" - rm -rf "$sandbox" -rlPhaseEnd - -rlJournalPrintText -rlJournalEnd diff --git a/test/test_versioning.sh b/test/test_versioning.sh deleted file mode 100644 index 0f7f5b6..0000000 --- a/test/test_versioning.sh +++ /dev/null @@ -1,236 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2014, Red Hat, Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the FreeBSD Project. -# -# Authors: Michal Minar <miminar@redhat.com> - -. ./base.sh - -EXIT_CODE_UNSATISFIED=5 -DEFAULT_VERSION='0.4.2' - -# Set the full test name -TEST="openlmi-scripts/test/test_versioning" - -# Package being tested -PACKAGE="openlmi-scripts" - -function cmp2int() { - digits=( `echo $1 | tr '.' ' '` ) - result=0 - for i in `seq 0 $((${#digits[@]} - 1))`; do - result=$((result*100)) - result=$((result + ${digits[$i]})) - done - echo $result -} - -rlJournalStart - -if [[ -z "${LMI_SOFTWARE_PROVIDER_VERSION}" ]]; then - msg="No version specified for OpenLMI-Software. Defaulting to " - msg+="{$DEFAULT_VERSION}." - rlLogInfo "$msg" - LMI_SOFTWARE_PROVIDER_VERSION="${DEFAULT_VERSION}" -elif [[ "$LMI_SOFTWARE_PROVIDER_VERSION" == none ]]; then - LMI_SOFTWARE_PROVIDER_VERSION='' -fi -if [[ -z "${LMI_HARDWARE_PROVIDER_VERSION}" ]]; then - msg="No version specified for OpenLMI-Hardware Defaulting to " - msg+="{$DEFAULT_VERSION}." - rlLogInfo "$msg" - LMI_HARDWARE_PROVIDER_VERSION="${DEFAULT_VERSION}" -elif [[ "$LMI_SOFTWARE_PROVIDER_VERSION" == none ]]; then - LMI_HARDWARE_PROVIDER_VERSION='' -fi - -rlPhaseStartSetup - rlLogInfo "Creating temporary python sandbox" - sandbox=`mktemp -d` - export PYTHONPATH="$sandbox" - pushd .. - rlLogInfo "Installing lmi meta-command" - rlRun "python setup.py develop --install-dir=$sandbox" - popd - rlLogInfo "Installing testing command" - pushd cmdver - rlRun "python setup.py develop --install-dir=$sandbox" - popd - export "$sandbox:$PATH" -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test help on select command" - - rlRun -s "$LMI_ help" - rlAssertEquals "Check the number of subcommands available." \ - `grep '^\s\+[[:alnum:]-]\+\s\+-\s\+' $rlRun_LOG | wc -l` 4 - rlAssertGrep '\<ver\>\s\+-\s\+Command for testing version dependencies\.$' \ - $rlRun_LOG - rlAssertGrep '\<ver-hw\s\+-\s\+This is a short description for CmdverHw\.$' \ - $rlRun_LOG - rlAssertGrep '\<ver-sw\s\+-\s\+This is a short description for CmdverSw\.$' \ - $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help ver" - rlAssertGrep "^Command for testing version dependencies.$" $rlRun_LOG - rlAssertGrep "^Usage:$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver (sw\\|hw) \[<args>\.\.\.\]$'" $rlRun_LOG - rm $rlRun_LOG - - if [[ -z "$LMI_SOFTWARE_PROVIDER_VERSION" ]]; then - rlRun -s "$LMI help ver-sw" $EXIT_CODE_UNSATISFIED - rlAssertGrep "error\s*:\s\+Profile and class dependencies were not satisfied for" \ - $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help ver sw" $EXIT_CODE_UNSATISFIED - rlAssertGrep "error\s*:\s\+Profile and class dependencies were not satisfied for" \ - $rlRun_LOG - rm $rlRun_LOG - - else - rlRun -s "$LMI help ver-sw" - rlAssertGrep "^Software testing command.$" $rlRun_LOG - rlAssertGrep "Usage: lmi ver-sw" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help ver sw" 0 - rlAssertGrep "^Software testing command.$" $rlRun_LOG - rlAssertGrep "Usage: lmi ver sw" $rlRun_LOG - rm $rlRun_LOG - fi - - if [[ -z "$LMI_HARDWARE_PROVIDER_VERSION" ]]; then - rlRun -s "$LMI help ver-hw" - rlAssertGrep "^Hardware testing command\.$" $rlRun_LOG - rlAssertGrep "^Usage: lmi ver-hw <cmd>$" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help ver hw" - rlAssertGrep "^Hardware testing command\.$" $rlRun_LOG - rlAssertGrep "^Usage: lmi ver hw <cmd>$" $rlRun_LOG - rm $rlRun_LOG - - else - rlRun -s "$LMI help ver-hw" - rlAssertGrep "^Hardware testing command\.$" $rlRun_LOG - rlAssertGrep "^Usage:$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver-hw system$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver-hw hostname$" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help ver hw" 0 - rlAssertGrep "^Hardware testing command.$" $rlRun_LOG - rlAssertGrep "^Usage:$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver hw system$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver hw hostname$" $rlRun_LOG - rm $rlRun_LOG - - rlRun -s "$LMI help ver hw system" 0 - rlAssertGrep "^Hardware testing command.$" $rlRun_LOG - rlAssertGrep "^Usage:$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver hw system$" $rlRun_LOG - rlAssertGrep "^\s\+lmi ver hw hostname$" $rlRun_LOG - rm $rlRun_LOG - fi - -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test software testing command" - - if [[ -z "$LMI_SOFTWARE_PROVIDER_VERSION" ]]; then - rlRun -s "$LMI ver-sw" $EXIT_CODE_UNSATISFIED - rlAssertGrep "Profile and class dependencies were not satisfied" \ - $rlRun_LOG - rm $rlRun_LOG - - elif [[ `cmp2int $LMI_SOFTWARE_PROVIDER_VERSION` -lt `cmp2int 0.4.2` ]]; then - - rlRun -s "$LMI ver-sw" - rlAssertGrep "Prov version.*${LMI_SOFTWARE_PROVIDER_VERSION} (PRE 0.4.2)" $rlRun_LOG - rm $rlRun_LOG - - elif [[ `cmp2int $LMI_SOFTWARE_PROVIDER_VERSION` == `cmp2int 0.4.2` ]]; then - rlRun -s "$LMI ver-sw" - rlAssertGrep "Prov version.*${LMI_SOFTWARE_PROVIDER_VERSION} (VER 0.4.2)" $rlRun_LOG - rm $rlRun_LOG - - else - rlRun -s "$LMI ver-sw" - rlAssertGrep "Prov version.*${LMI_SOFTWARE_PROVIDER_VERSION} (DEVEL)" $rlRun_LOG - rm $rlRun_LOG - fi - -rlPhaseEnd - -rlPhaseStartTest - rlLogInfo "Test hardware testing command" - - if [[ -z "$LMI_HARDWARE_PROVIDER_VERSION" ]]; then - for cmd in "system" "hostname"; do - rlRun -s "$LMI ver-hw $cmd" - rlAssertEquals "Printed table has just 2 rows" \ - `cat $rlRun_LOG | wc -l` 2 - rlAssertGrep "^Given command\s\+$cmd$" $rlRun_LOG - rlAssertGrep "^Prov version\s\+N/A" $rlRun_LOG - rm $rlRun_LOG - done - - else - if [[ `cmp2int $LMI_HARDWARE_PROVIDER_VERSION` -lt `cmp2int 0.4.2` ]]; then - ver_suffix=' (PRE 0.4.2)' - else - ver_suffix='' - fi - for cmd in "system" "hostname"; do - rlRun -s "$LMI ver-hw $cmd" - rlAssertEquals "Printed table has just 2 rows" \ - `cat $rlRun_LOG | wc -l` 2 - rlAssertGrep "^Prov version\s\+$LMI_SOFTWARE_PROVIDER_VERSION$ver_suffix\$" \ - $rlRun_LOG - if [[ $cmd == system ]]; then - reg="^Chassis Type\s\+.*" - else - reg="^Hostname\s\+$HOSTNAME" $rlRun_LOG - fi - rm $rlRun_LOG - done - fi - -rlPhaseEnd - -rlPhaseStartCleanup - rlLogInfo "Removing temporary python sandbox" - rm -rf "$sandbox" -rlPhaseEnd - -rlJournalPrintText -rlJournalEnd diff --git a/test/unit/test_common.py b/test/unit/test_common.py deleted file mode 100644 index 955d964..0000000 --- a/test/unit/test_common.py +++ /dev/null @@ -1,79 +0,0 @@ -import unittest - -from lmi.scripts.common.util import FilteredDict - -class FilteredDictTest(unittest.TestCase): - - def test_empty(self): - d = FilteredDict(tuple(), {}) - self.assertEqual(0, len(d)) - self.assertNotIn('key', d) - self.assertEqual(0, len(d.keys())) - self.assertEqual(0, len(d.values())) - self.assertEqual(0, len(d.items())) - self.assertRaises(KeyError, d.__getitem__, 'key') - self.assertRaises(KeyError, d.__setitem__, 'key', 'value') - - def test_empty_keys(self): - d = FilteredDict(tuple(), {'a': 1}) - self.assertEqual(0, len(d)) - self.assertNotIn('a', d) - self.assertEqual(0, len(d.keys())) - self.assertEqual(0, len(d.values())) - self.assertEqual(0, len(d.items())) - self.assertRaises(KeyError, d.__getitem__, 'a') - self.assertRaises(KeyError, d.__setitem__, 'a', 2) - - def test_empty_origin(self): - d = FilteredDict(tuple('a'), {}) - self.assertEqual(0, len(d)) - self.assertNotIn('a', d) - self.assertEqual(0, len(d.keys())) - self.assertEqual(0, len(d.values())) - self.assertEqual(0, len(d.items())) - self.assertRaises(KeyError, d.__getitem__, 'a') - d['a'] = 1 - self.assertEqual(1, len(d)) - self.assertEqual(1, d['a']) - self.assertIn('a', d) - self.assertEqual(['a',], d.keys()) - self.assertEqual([1], d.values()) - self.assertEqual([('a', 1)], d.items()) - di = d.iteritems() - self.assertEqual(('a', 1), di.next()) - self.assertRaises(StopIteration, di.next) - d['a'] = 2 - self.assertEqual(2, d['a']) - del d['a'] - self.assertEqual(0, len(d)) - - def test_filled(self): - original = {'b': 2, 'c': 3} - d = FilteredDict(('a', 'b'), original) - self.assertEqual(1, len(d)) - self.assertNotIn('a', d) - self.assertIn('b', d) - self.assertNotIn('c', d) - self.assertEqual(1, len(d.keys())) - self.assertEqual(1, len(d.values())) - self.assertEqual(1, len(d.items())) - self.assertRaises(KeyError, d.__getitem__, 'a') - self.assertEqual(2, d['b']) - di = d.iteritems() - self.assertEqual(('b', 2), di.next()) - self.assertRaises(StopIteration, di.next) - self.assertEqual(2, d.pop('b')) - self.assertEqual(0, len(d)) - self.assertEqual({'c': 3}, original) - d.update({'a': 1, 'b': 4}) - self.assertEqual({'a': 1, 'b': 4, 'c': 3}, original) - self.assertEqual(2, len(d)) - self.assertEqual(set((('a', 1), ('b', 4))), set(d.items())) - d.clear() - self.assertEqual(0, len(d)) - self.assertEqual({'c': 3}, original) - self.assertRaises(KeyError, d.__setitem__, 'c', 5) - self.assertRaises(KeyError, d.update, {'b': 2, 'c': 3}) - -if __name__ == '__main__': - unittest.main() |