diff options
author | Niranjan Mallapadi <mrniranjan@redhat.com> | 2015-10-01 18:43:06 +0530 |
---|---|---|
committer | Niranjan Mallapadi <mrniranjan@redhat.com> | 2015-10-01 18:51:51 +0530 |
commit | 4f5051463ea9dc1366a2b58b9814c0e7997c1813 (patch) | |
tree | b94c6a9f3524d0073d157fd61228cbe52d6755b4 /tests | |
parent | fd8bc4fe1acceba7f88b71a44e43d2993056cfd3 (diff) | |
download | pki-4f5051463ea9dc1366a2b58b9814c0e7997c1813.tar.gz pki-4f5051463ea9dc1366a2b58b9814c0e7997c1813.tar.xz pki-4f5051463ea9dc1366a2b58b9814c0e7997c1813.zip |
shared functions/classes created to setup DS
created a library called pkilib containing
functions to setup directory services.
python/docs contain the pkilib api documentation
Signed-off-by: Niranjan Mallapadi <mrniranjan@redhat.com>
Diffstat (limited to 'tests')
29 files changed, 1828 insertions, 0 deletions
diff --git a/tests/dogtag/shared/python/MANIFEST.in b/tests/dogtag/shared/python/MANIFEST.in new file mode 100644 index 000000000..96b79c9d3 --- /dev/null +++ b/tests/dogtag/shared/python/MANIFEST.in @@ -0,0 +1,3 @@ +include README.rst +recursive-include pkilib/etc * +recursive-include docs * diff --git a/tests/dogtag/shared/python/README.rst b/tests/dogtag/shared/python/README.rst new file mode 100644 index 000000000..5d8299b2f --- /dev/null +++ b/tests/dogtag/shared/python/README.rst @@ -0,0 +1,5 @@ +pki_tests +========= + +`pkilib`_ is a library containing shared functions to automtate `Dogtag PKI & Red Hat Ceritificate System __` using pytest framework. + diff --git a/tests/dogtag/shared/python/docs/Install.rst b/tests/dogtag/shared/python/docs/Install.rst new file mode 100644 index 000000000..ca136f42c --- /dev/null +++ b/tests/dogtag/shared/python/docs/Install.rst @@ -0,0 +1,33 @@ +Install +======= + +* pkilib is a python library which contains shared functions to be used with py.test to automate CLI, Web UI of Red Hat Certificate Services and + Dogtag PKI. + +Dependencies +------------ + pkilib requires following packages: + + 1. python-paramiko + 2. python-pytest-multihost + 3. PyYAML + 4. python-ldap + 5. pytest + 6. ipa-python(freeipa-ipapython) + 7. python-dns + 8. python-krbV +RHEL7.2 +------- +* pkilib can be downloaded from `this link <https://mrniranjan.fedorapeople.org/pkilib-0.1-1.el7.noarch.rpm>`_. +To install above dependencies on RHEL7.2 get the `idmqe-extras-repo <http://cosmos.lab.eng.pnq.redhat.com/idmqe-extras>`_.file:: + + wget -O /etc/yum.repos.d/idmqe-extras-rhel.repo \ + http://cosmos.lab.eng.pnq.redhat.com/idmqe-extras/idmqe-extras-rhel.repo + + +Fedora 21 +--------- +* On fedora 21, all the dependencies are provided on Base Fedora repository. Download the pkilib rpm from `here <https://mrniranjan.fedorapeople.org/pkilib-0.1-1.fc21.noarch.rpm>`_.:: + + wget https://mrniranjan.fedorapeople.org/pkilib-0.1-1.fc21.noarch.rpm/pki_tests-0.1.noarch.f21.rpm + yum localinstall pki_tests-0.1.noarch.f21.rpm diff --git a/tests/dogtag/shared/python/docs/Makefile b/tests/dogtag/shared/python/docs/Makefile new file mode 100644 index 000000000..419d203a4 --- /dev/null +++ b/tests/dogtag/shared/python/docs/Makefile @@ -0,0 +1,189 @@ +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# 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 coverage gettext + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Nexus.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Nexus.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Nexus" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Nexus" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + diff --git a/tests/dogtag/shared/python/docs/MultihostPlugin.rst b/tests/dogtag/shared/python/docs/MultihostPlugin.rst new file mode 100644 index 000000000..76f0955f5 --- /dev/null +++ b/tests/dogtag/shared/python/docs/MultihostPlugin.rst @@ -0,0 +1,23 @@ +pytest multihost plugin doc +=========================== + +pytest_multihost.config +----------------------- +.. automodule:: pytest_multihost.config + :members: + +pytest_multihost.plugin +----------------------- +.. automodule:: pytest_multihost.plugin + :members: + +pytest_multihost.transport +-------------------------- +.. automodule:: pytest_multihost.transport + :members: + +pytest_multihost.util +--------------------- +.. automodule:: pytest_multihost.util + :members: +~ diff --git a/tests/dogtag/shared/python/docs/conf.py b/tests/dogtag/shared/python/docs/conf.py new file mode 100644 index 000000000..7086f4120 --- /dev/null +++ b/tests/dogtag/shared/python/docs/conf.py @@ -0,0 +1,301 @@ +# -*- coding: utf-8 -*- +# +# pki tests documentation build configuration file, created by +# sphinx-quickstart on Thu Apr 2 23:33:29 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# 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. +def skip(app, what, name, obj, skip, options): + if name == "__init__": + return False + return skip + +def setup(app): + app.connect("autodoc-skip-member", skip) + +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', + 'sphinx.ext.autodoc', +] +autoclass_content = 'both' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pki-tests' +copyright = u'2015, Red Hat' +author = u'Niranjan MR' + +# 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.1' +# The full version, including alpha/beta/rc tags. +release = '1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# 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 = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- 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 = 'flask' +#html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pki-testsdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'pki-tests.tex', u'pki-tests Documentation', + u'Niranjan', '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 = [ + (master_doc, 'nexus', u'pki-tests Documentation', + [author], 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 = [ + (master_doc, 'pki-tests', u'pki-tests Documentation', + author, 'pki-tests', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + + diff --git a/tests/dogtag/shared/python/docs/index.rst b/tests/dogtag/shared/python/docs/index.rst new file mode 100644 index 000000000..0886db6a7 --- /dev/null +++ b/tests/dogtag/shared/python/docs/index.rst @@ -0,0 +1,34 @@ +.. CS(pki) QE Test documentation master file, created by + sphinx-quickstart on Wed Sep 02 18:52:16 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +IDM QE CS pytest framework Documentation +=========================================== + +IDM QE PKI PyTest provides a framework `pkilib` which contains shared functions and libraries to be used to write tests in pytest framework for +Red Hat Certificate Services/Dogtag PKI. + + +Contents: + +.. toctree:: + :maxdepth: 2 + + Install + running + layout + pkilib + MultihostPlugin + + +Additional Information +====================== +.. [#] `Python Pytest Multihost plugin <https://fedorahosted.org/python-pytest-multihost/>`_. + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/tests/dogtag/shared/python/docs/layout.rst b/tests/dogtag/shared/python/docs/layout.rst new file mode 100644 index 000000000..476ff9477 --- /dev/null +++ b/tests/dogtag/shared/python/docs/layout.rst @@ -0,0 +1,22 @@ +layout +====== +* This doc provides layout of Pki pytest framework and test suites directory. + + +module +------- + +* pkilib + * This is the main top directory under which there are subdirectories containing various shared functions required to write tests using pytest + +* pkilib/api + + * This directory contains shared functions required to write tests for Certificate Services/Dogtag PKI API + +* pkilib/cli + + * This directory contains shared functions to required to write tests for automating Certificate Services/Dogtag PKI cli. + +* pkilib/common + + * This directory contains shared functions which are common irrespective of cli/ui diff --git a/tests/dogtag/shared/python/docs/pkilib.rst b/tests/dogtag/shared/python/docs/pkilib.rst new file mode 100644 index 000000000..d0f138663 --- /dev/null +++ b/tests/dogtag/shared/python/docs/pkilib.rst @@ -0,0 +1,18 @@ +pkilib +========= +* This doc documents all the shared modules and functions used for automating cli + +pkilib.common.mh_wrapper: +------------------------- +.. automodule:: pkilib.common.mh_wrapper + :members: + +pkilib.common.mh_libdirsrv +-------------------------- +.. automodule:: pkilib.common.mh_libdirsrv + :members: + +pkilib.common.Qe_class +---------------------- +.. automodule:: pkilib.common.Qe_class + :members: diff --git a/tests/dogtag/shared/python/docs/running.rst b/tests/dogtag/shared/python/docs/running.rst new file mode 100644 index 000000000..21787fee2 --- /dev/null +++ b/tests/dogtag/shared/python/docs/running.rst @@ -0,0 +1,89 @@ +running +======= + +* Running Tests + + +Prerequisites +------------- +* Functional Tests mostly written for Certificate services require multiple hosts. General naming used in this regard are: + + * master(m): Node on which we have all the subsystems installed like CA (Root), kra, ocsp, tks, tps + * clone(r): Which has clone of all the subsystems installed on Master or subca + * client(c): System from which we run pki commands + * mrc: topology with master, clone, client + * mrr: topology with master, clone, clone + * m : topology with only master + * mc: topology with only master and client + + +config +------- + + * To run multihosts tests, pickup a multihost template to use. Template files can be found in /etc/pkilib directory, They are named based on topology they represent. Naming scheme is mh_cfg_<topology>.yaml + + Example config file:: + + root_password: 'redhat' + domains: + - name: testrelm.test + type: cs + hosts: + - name: hostname1 + ip: 192.168.122.1 + role: master + - name: hostname2 + ip: 192.168.122.2 + role: clone + + Edit the config file and replace **hostname1** and **hostname2** with actual hostname. Hostname should be Fully qualified domain name. + + Set the root password of the systems under parameter **root_password** + +Executing Tests +--------------- +* To execute existing tests clone pki-tests repo and run py.test against any specific test suite directory. + + * On RHEL7.2:: + + $ git clone git://git.app.eng.bos.redhat.com/pki-tests.git + $ cd pki-tests/dogtag/pytest + $ py.test --multihost-config=<multihost-template> <test-suite-directory> + + * On Fedora 22:: + + $ git clone git://git.fedorahosted.org/pki.git + $ cd tests/dogtag/pytest + $ py.test --multihost-config=<multihost-template> <test-suite-directory> + +* Before executing any tests, it's required to create a config file as specified in `config` section. + + * Executing test suite:: + + $ cd pki-tests/dogtag/pytest/ + $ py.test --junit-xml=/tmp/junit.xml \ + --multihost-config=mh_cfg.yaml \ + -v <test_suite_dir> + + * Executing Individual Test sub-suite (module):: + + $ cd pki-tests/dogtag/pytest/ + $ py.test --junit-xml=/tmp/junit.xml \ + --multihost-config=mh_cfg.yaml \ + -v <test_suite_dir/test_module.py> + + * Executing individual Test cases.:: + + $ cd pki-tests/dogtag/pytest/ + + $ py.test --junit.xml=/tmp/junit.xml \ + --multihosts-config=mh_cfg.yaml \ + -v <test_suite_dir>/<test_module>.py::<TestClass>::<test_case> + + * Example 1: Running Installation test suite:: + + $ cd pki-tests/dogtag/pytest/installation + + $ py.test --junit.xml=/tmp/junit.xml \ + --multihosts-config=mh_cfg.yaml \ + -v installation diff --git a/tests/dogtag/shared/python/pkilib.spec-f22 b/tests/dogtag/shared/python/pkilib.spec-f22 new file mode 100644 index 000000000..537ab246c --- /dev/null +++ b/tests/dogtag/shared/python/pkilib.spec-f22 @@ -0,0 +1,56 @@ +%define name pkilib +%define owner mrniranjan +%define project pkilib +%define version 0.1 +%define release 1 +%define modulename pkilib + +Name: %{name} +Version: %{version} +Release: %{release}%{?dist} +Summary: Red Hat Certificate Services/Dogtag PKI PyTest Framework + +License: GPLv3+ +URL: http://www.dotagpki.org +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch +BuildRequires: python +Requires: python-paramiko +Requires: python-pytest-multihost +Requires: PyYAML +Requires: python-ldap +Requires: pytest +Requires: freeipa-python +Requires: python-dns +Requires: python-krbV + + +%description +An python framework for Red Hat Certificate Services/Dogtag-PKI Pytest Test suite. + + +%prep +%setup -qn %{project} + +%build +%{__python} setup.py build + + +%install +%{__python} setup.py install --skip-build --root %{buildroot} +%{__python} -m compileall %{buildroot}%{python_sitelib}/%{srcname} +mkdir -p %{buildroot}%{_sysconfdir}/%{modulename} +cp -p %{buildroot}%{python_sitelib}/%{modulename}/etc/* %{buildroot}%{_sysconfdir}/%{modulename} + +%files +%defattr(-,root,root) +%doc docs/* +%{python_sitelib}/%{name}-%{version}-py2.?.egg-info +%{python_sitelib}/%{modulename}/ +%config(noreplace) %{_sysconfdir}/%{modulename} + +%changelog +* Mon Aug 31 2015 Niranjan MR <mrniranjan@fedoraproject.org> - 0.1-1 +- initial version- + diff --git a/tests/dogtag/shared/python/pkilib.spec-rhel7 b/tests/dogtag/shared/python/pkilib.spec-rhel7 new file mode 100644 index 000000000..c75aef79f --- /dev/null +++ b/tests/dogtag/shared/python/pkilib.spec-rhel7 @@ -0,0 +1,56 @@ +%define name pkilib +%define owner mrniranjan +%define project pkilib +%define version 0.1 +%define release 1 +%define modulename pkilib + +Name: %{name} +Version: %{version} +Release: %{release}%{?dist} +Summary: Red Hat Certificate Services/Dogtag PKI PyTest Framework + +License: GPLv3+ +URL: http://www.dotagpki.org +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch +BuildRequires: python +Requires: python-paramiko +Requires: python-pytest-multihost +Requires: PyYAML +Requires: python-ldap +Requires: pytest +Requires: ipa-python +Requires: python-dns +Requires: python-krbV + + +%description +An python framework for Red Hat Certificate Services/Dogtag-PKI Pytest Test suite. + + +%prep +%setup -qn %{project} + +%build +%{__python} setup.py build + + +%install +%{__python} setup.py install --skip-build --root %{buildroot} +%{__python} -m compileall %{buildroot}%{python_sitelib}/%{srcname} +mkdir -p %{buildroot}%{_sysconfdir}/%{modulename} +cp -p %{buildroot}%{python_sitelib}/%{modulename}/etc/* %{buildroot}%{_sysconfdir}/%{modulename} + +%files +%defattr(-,root,root) +%doc docs/* +%{python_sitelib}/%{name}-%{version}-py2.?.egg-info +%{python_sitelib}/%{modulename}/ +%config(noreplace) %{_sysconfdir}/%{modulename} + +%changelog +* Mon Aug 31 2015 Niranjan MR <mrniranjan@fedoraproject.org> - 0.1-1 +- initial version- + diff --git a/tests/dogtag/shared/python/pkilib/__init__.py b/tests/dogtag/shared/python/pkilib/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/__init__.py diff --git a/tests/dogtag/shared/python/pkilib/api/__init__.py b/tests/dogtag/shared/python/pkilib/api/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/api/__init__.py diff --git a/tests/dogtag/shared/python/pkilib/cli/__init__.py b/tests/dogtag/shared/python/pkilib/cli/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/cli/__init__.py diff --git a/tests/dogtag/shared/python/pkilib/cli/factory.py b/tests/dogtag/shared/python/pkilib/cli/factory.py new file mode 100755 index 000000000..d38a1c4a1 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/cli/factory.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +import rpm +import subprocess +import os +import shlex +import shutil +import ldap +import ldap.modlist as modlist +from ldap.ldapobject import SimpleLDAPObject +import ldap.sasl +import ldif +import sys + +class PkiFactory: + def run_cmd(cls,cmd,stdin=None,capture_output=True): + p_in = None + p_out = None + p_err = None + if stdin: + p_in = subprocess.PIPE + else: + p_out = subprocess.PIPE + p_err = subprocess.PIPE + + args = shlex.split(cmd) + p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err, close_fds=True) + stdout, stderr = p.communicate(stdin) + stdout, stderr = str(stdout), str(stderr) + if capture_output: + return stdout, stderr, p.returncode + else: + return stderr, p.returncode + + + + def setup_ds(cls,dsInfFile): + cmd = "setup-ds.pl --silent --file=%s" % dsInfFile + stdout, stderr, returncode = cls.run_cmd(cmd,capture_output=True) + if returncode !=0: + return stderr, returncode + else: + return stdout,returncode + + def remove_dsInstance(cls,InstanceName=None): + cmd = "remove-ds.pl -i slapd-%s -d" % (InstanceName) + print(cmd) + stdout, stderrr, returncode = cls.run_cmd(cmd,capture_output=True) + if returncode !=0: + return stderr, returncode + else: + return stdout,returncode + + def setup_PkiInstance(cls,InstanceName=None,InstanceFile=None): + cmd = "pkispawn -s %s -f %s -vv" % (InstanceName,InstanceFile) + print(cmd) + stdout,stderr,returncode = cls.run_cmd(cmd) + if returncode !=0: + return stderr, returncode + else: + return stdout,stderr,returncode + + def remove_subsystem(cls,subsystem=None,InstanceName=None): + cmd = "pkidestroy -i %s -s %s" % (InstanceName, subsystem) + stdout,stderr,returncode = cls.run_cmd(cmd,capture_output=True) + if returncode !=0: + return stderr, returncode + else: + return stdout,stderr,returncode + diff --git a/tests/dogtag/shared/python/pkilib/cli/ldap.py b/tests/dogtag/shared/python/pkilib/cli/ldap.py new file mode 100755 index 000000000..acef76e82 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/cli/ldap.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +import ldap +import ldap.modlist as modlist +import time + +def setup_ldbm(host='localhost', port=389, binddn="CN=Directory Manager", bindpw="Secret123", ldapentry=None, ldapdn=None): + l = ldap.open('localhost', 389) + try: + l.bind(binddn, bindpw) + except ldap.SERVER_DOWN, e: + print("ldap server is down") + return False + else: + print("Bind Successful") + + entry=ldapentry + dn = ldapdn + ldif = modlist.addModlist(entry) + print("ldif = ",ldif) + try: + l.add_s(dn, ldif) + except: + raise + else: + print("%s succesfully added" % (dn)) + return True + finally: + l.unbind() + del l + +DBName = "%(pki_instance_name)s" % {'pki_instance_name' : 'Example1'} +RootDC = "o=%s" % DBName +RootDCMapping = "%s,cn=mapping tree,cn=config" % RootDC + +entry1 = { + 'objectClass' : ['extensibleObject', 'nsBackendInstance'], + 'nsslapd-suffix' : [RootDC] + } +dn1 = 'cn=%s,cn=ldbm database,cn=plugins,cn=config' % DBName + +entry2 = { + 'objectClass':['top', 'extensibleObject','nsMappingTree'], + 'nsslapd-state' : 'backend', + 'nsslapd-backend' : DBName, + 'cn' : RootDC + } +dn2 = RootDCMapping +setup_ldbm(ldapentry=entry1,ldapdn=dn1) +time.sleep(30) +setup_ldbm(ldapentry=entry2,ldapdn=dn2) + +entry3 = { + 'objectClass': ['top', 'dcObject', 'organization'], + 'dc' : [DBName], + 'o' : ['Example,Inc'] + } +dn3 = RootDC +setup_ldbm(ldapentry=entry3,ldapdn=dn3) + diff --git a/tests/dogtag/shared/python/pkilib/common/Qe_class.py b/tests/dogtag/shared/python/pkilib/common/Qe_class.py new file mode 100644 index 000000000..c62cbb74e --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/Qe_class.py @@ -0,0 +1,117 @@ +import pytest_multihost.config +import pytest_multihost.host +import logging +import pytest + +""" +qe_class provides the expansion to the py.test multihost plugin for CS Testing +""" + +class QeConfig(pytest_multihost.config.Config): + """ + QeConfig subclass of multihost plugin to extend functionality + """ + extra_init_args = {} + + def __init__(self, **kwargs): + """ + Initialize pytest_multihost.config with default variables + + :param kwargs: + """ + self.log = self.get_logger('%s.%s' % (__name__, type(self).__name__)) + pytest_multihost.config.Config.__init__(self, **kwargs) + + + def get_domain_class(self): + """ + return custom domain class. This is needed to fully extend the config for + custom multihost plugin extensions. + + :param None: + + :return None: + """ + return QeDomain + + def get_logger(self, name): + """ + Override get_logger to set logging level + + :param str name: + :return obj log: + """ + log = logging.getLogger(name) + log.propagate = False + if not log.handlers: + #set log Level + log.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + handler.setLevel(logging.DEBUG) + #set formatter + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + log.addHandler(handler) + return log + +class QeDomain(pytest_multihost.config.Domain): + """ + QeDomain subclass of multihost plugin domain class. + """ + def __init__(self, config, name, domain_type): + """ + Subclass of pytest_multihost.config.Domain + + :param obj config: config config + :param str name: Name + :param str domain_type: + + :return None: + """ + + self.type = str(domain_type) + self.config = config + self.name = str(name) + self.hosts = [] + + def get_host_class(self, host_dict): + """ + return custom host class + """ + return QeHost + +class QeHost(pytest_multihost.host.Host): + """ + QeHost subclass of multihost plugin host class. This extends functionality + of the host class for IPA QE purposes. Here we add support functions that + will be very widely used across tests and must be run on any or all hosts + in the environment. + """ + def qerun(self, command, stdin_text=None, exp_returncode=0, exp_output=None): + """ + qerun :: <command> [stdin_text=<string to pass as stdin>] + [exp_returncode=<retcode>] + [<exp_output=<string to check from output>] + - function to run a command and check return code and output + + :param str command: Command + :param str stdin_text: Stdin + :param int exp_returncode: Return code (default 0) + :param str exp_output: Check the expected output + """ + cmd = self.run_command(command, stdin_text, raiseonerr=False) + if cmd.returncode != exp_returncode: + pytest.xfail("returncode mismatch.") + print("GOT: ", cmd.returncode) + print("EXPECTED: ", exp_returncode) + + if exp_output == None: + print("Not checking expected output") + + elif cmd.stdout_text.find(exp_output) == 0: + pytest.xfail("expected output not found") + print("GOT: ", cmd.stdout_text) + print("EXPECTED: ", exp_output) + + print("COMMAND SUCCEEDED!") + diff --git a/tests/dogtag/shared/python/pkilib/common/__init__.py b/tests/dogtag/shared/python/pkilib/common/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/__init__.py diff --git a/tests/dogtag/shared/python/pkilib/common/exceptions.py b/tests/dogtag/shared/python/pkilib/common/exceptions.py new file mode 100644 index 000000000..2ab43116f --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/exceptions.py @@ -0,0 +1,37 @@ +""" + Provide Exceptions for py.test framework +""" +class StandardException(Exception): + """ Overrides Exception class """ + def __init__(self, msg='', rval=1): + if msg is None: + msg = '' + self.msg = msg + self.rval = rval + def __str__(self): + return (self.msg, self.rval) + +class InvalidInput(StandardException): + """ + Override StandardException used mainly when invalid input is passed + """ + pass + +class DirSrvException(StandardException): + """ + Override StandardException, This exception s to be used for Directory Server related Errors + """ + pass + +class PkiLibException(StandardException): + """ + Override StandardException , This exception is to be used for Dogtag/CS related Errors + """ + pass + +class OSException(StandardException): + """ + Override StandardException, This exception is to be used for Operating system errors. + """ + pass + diff --git a/tests/dogtag/shared/python/pkilib/common/factory.py b/tests/dogtag/shared/python/pkilib/common/factory.py new file mode 100644 index 000000000..540bc9856 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/factory.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +This module implements generic functions for py.test framework. +""" +import subprocess +import copy +import os + +class PkiTools: + ''' + PkiTools consists of functions related to Operating system tasks + that are used regularly. + ''' + @classmethod + def Execute(self, args, stdin=None, capture_output=True, raiseonerr=False, env=None, cwd=None): + """ + Execute a command and return stdout, stderr and return code + + :param str args: List of arguments for the command + :param str stdin: Optional input + :param bool capture_output: Capture output of the command (default True) + :param bool raiseonerr: Raise exception if command fails + :param str env: Environment variables to be set before the command is run + :param str cwd: Current working Directory + + :return stdout, stderr and returncode: if command return code is 0 else raises exception if raiseonerr is True + """ + p_in = None + p_out = None + p_err = None + if env is None: + env = copy.deepcopy(os.environ) + if capture_output: + p_out = subprocess.PIPE + p_err = subprocess.PIPE + try: + proc = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err, + close_fds=True, env=env, cwd=cwd) + stdout, stderr = proc.communicate(stdin) + except KeyboardInterrupt: + proc.wait() + raise + if proc.returncode !=0 and raiseonerr: + raise subprocess.CalledProcessError(proc.returncode, args, stdout) + else: + return (stdout, stderr, proc.returncode) diff --git a/tests/dogtag/shared/python/pkilib/common/mh_libdirsrv.py b/tests/dogtag/shared/python/pkilib/common/mh_libdirsrv.py new file mode 100644 index 000000000..f651cb278 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/mh_libdirsrv.py @@ -0,0 +1,222 @@ +from pkilib.common.exceptions import DirSrvException +from pkilib.common.Qe_class import QeHost +from os.path import exists +from pkilib.common.factory import PkiTools +from ipapython import ipautil +from ipapython import certdb +import os +import ConfigParser +import os.path +import pwd +import array +import tempfile +import grp +import subprocess + + + +# Constants +DS_USER = 'nobody' +DS_GROUP = 'nobody' +DS_admin = 'admin' +DS_ROOTDN = 'CN=Directory Manager' + +class DirSrv(object): + """ + Setup/Remove Directory Server Instances used by CS subsystems. + """ + def __init__(self, InstName, InstHost, InstSuffix, RootDNPwd=None, LdapPort=None, TLSPort=None, MultiHost=None): + """ + Initalize DirSrv Object with given Instance_Name, InstHost, suffix, LdapPort, and TLSPort. + + :param str InstName: Directory Server Instance Name + :param str InstHost: Host on which Directory server should be setup + :param str InstSuffix: Suffix required for setup + :param str RootDNPwd: RootDN Password + :param str LdapPort: Ldap Port to be used (optional) + :param int TlsPort: TLSPort to be used for setup (optional) + :param obj Multihost: Object from pytest multihost plugin (optional) + + """ + self.InstName = InstName + self.DSInstHost = InstHost + self.DSInstSuffix = InstSuffix + self.DSLdapPort = LdapPort + self.DSTLSPort = TLSPort + self.DSRootDN = DS_ROOTDN + self.DSRootDNPwd = RootDNPwd + self.DSInstName = 'slapd-%s' % InstName + self.DSRootDir = '/etc/dirsrv' + self.DSInstPath = os.path.join(self.DSRootDir, self.DSInstName) + self.MultiHost = MultiHost + + def __str__(self): + return "%s.%s('%r')" % (self.__module__, self.__class__.__name__, self.__dict__) + + def ___repr__(self): + return '%s(%s, %r)' % (self.__class__.__name__, self.__dict__) + + def create_config(self): + """ + Creates the configuration file for setup-ds.pl to create Directory server Instance. + + :param: None + + :return: File path containing config file. + """ + config = ConfigParser.RawConfigParser() + config.optionxform = str + + config.add_section('General') + config.set('General', 'FullMachineName', self.DSInstHost) + config.set('General', 'SuiteSpotUserID', DS_USER) + config.set('General', 'SuiteSpotGroup', DS_GROUP) + config.set('General', 'ConfigDirectoryAdminID', DS_admin) + config.add_section('slapd') + config.set('slapd', 'ServerIdentifier', self.InstName) + config.set('slapd', 'ServerPort', self.DSLdapPort) + config.set('slapd', 'Suffix', self.DSInstSuffix) + config.set('slapd', 'RootDN', self.DSRootDN) + config.set('slapd', 'RootDNPwd', self.DSRootDNPwd) + (DScfgfile_fd, DScfg_file_path) = tempfile.mkstemp(suffix='cfg') + + os.close(DScfgfile_fd) + with open(DScfg_file_path, "wb") as f: + config.write(f) + return DScfg_file_path + + def Setup_DSInstance(self, DSCfg_file): + """ + Creates Directory server instance by running setup-ds.pl. + if MultiHost parameter is passed to DirSrv Object then InstHost parameter contains + the actual host on which setup-ds.pl is run else setup-ds.pl is run on localhost + + :param: Configuration File path + :return: True if seutp-ds.pl ran successfully else false + + Todo: Should raise an DirSrvException + """ + if isinstance(self.MultiHost, QeHost): + self.MultiHost.transport.put_file(DSCfg_file, '/tmp/test.cfg') + setup_args = ['setup-ds.pl', '--silent', '--file=/tmp/test.cfg', '--debug'] + try: + output = self.MultiHost.run_command(setup_args, log_stdout=True,raiseonerr=True) + except subprocess.CalledProcessError as E: + return False + else: + os.remove(DSCfg_file) + return True + else: + setup_args = ['setup-ds.pl', '--silent', '--file=%s' % DSCfg_file] + try: + stdin, stdout, return_code = PkiTools.execute(setup_args, raiseonerr=True) + except ipautil.CalledProcessError as e: + return False + else: + os.remove(DSCfg_file) + return True + + def Remove_DSInstance(self, InstName=None): + """ + Removes Directory server Instance + + :param str InstName: Instance Name + :return bool: Returns True is successfull else Returns False + + Todo: Should raise an DirSrvException + """ + if InstName is None: + InstName = self.DSInstName + remove_args = ['remove-ds.pl', '-i', InstName, '-d'] + if isinstance(self.MultiHost, QeHost): + try: + output = self.MultiHost.run_command(remove_args, log_stdout=True,raiseonerr=True) + except subprocess.CalledProcessError as E: + return False + else: + return True + else: + try: + stdin, stdout, return_code = ipautil.run(remove_args, raiseonerr=True) + except ipautil.CalledProcessError as e: + return False + else: + return True + + def CreateSelfSignedCerts(self): + """ + Creates NSS DB on the Directory Instance Directory and creates self signed certificates using certutil command. + + :param: None + :return bool: True if certs are created else Raises DirSrvException. + + Note: This method uses certdb function from ipapython to create NSS DB directory + """ + DSCertDBOjb = certdb.NSSDatabase(nssdir=self.DSInstPath) + DSNSSPassPhrase = 'Secret123' + (pwdfile_fd, pwd_file_path) = tempfile.mkstemp() + os.write(pwdfile_fd, DSNSSPassPhrase) + os.close(pwdfile_fd) + + #setup NSS DB with the password created + DSCertDBOjb.create_db(pwd_file_path) + #since there is no exception handling , we need to verify + #if the nssdb is created properly, we check if cert8.db, + #secmod.db and key3.db exists + nss_db_file = ['cert8.db', 'key3.db', 'secmod.db'] + for files in nss_db_file: + if not exists(os.path.join(self.DSInstPath, files)): + raise DirSrvException('Could not setup NSS DB on %s' % self.DSInstPath) + + # create Noise File + noise = array.array('B', os.urandom(128)) + (noise_fd, noise_name) = tempfile.mkstemp() + os.write(noise_fd, noise) + os.close(noise_fd) + ca_args = ["-f", pwd_file_path, + "-S", + "-n", "Example CA", + "-s", "CN=Example CA,O=Example,L=Raleigh,C=US", + "-t", "CT,,", + "-x", + "-z", noise_name] + + + stdin, stdout, return_code = DSCertDBOjb.run_certutil(ca_args) + if return_code != 0: + raise DirSrvException('Could not create Self signed CA Cert') + + server_dn = "CN=%s" % (self.DSInstHost) + + server_args = ["-f", pwd_file_path, + "-S", + "-n", "Server-Cert", + "-s", server_dn, + "-c", "Example CA", + "-t", "u,u,u", + "-v", "720", + "-m", "1001", + "-z", noise_name] + + stdin, stdout, return_code = DSCertDBOjb.run_certutil(server_args) + if return_code != 0: + raise DirSrvException('Could not create Server-Cert') + + os.remove(pwd_file_path) + os.remove(noise_name) + + ##write password to pin.txt + pin_file = os.path.join(self.DSInstPath, 'pin.txt') + pin_fd = open(pin_file, "w") + pin_fd.write('Internal (Software) Token:%s' % DSNSSPassPhrase) + pin_fd.close() + + #all these files are to be owned by #dirsrv a/c + DSUID = pwd.getpwnam(DS_USER) + DSGID = grp.getgrnam(DS_GROUP) + for files in nss_db_file: + os.chown((os.path.join(self.DSInstPath, files)), DSUID.pw_uid, DSGID.gr_gid) + os.chown(pin_file, DSUID.pw_uid, DSGID.gr_gid) + os.chmod(pin_file, 0400) + + return True diff --git a/tests/dogtag/shared/python/pkilib/common/mh_wrapper.py b/tests/dogtag/shared/python/pkilib/common/mh_wrapper.py new file mode 100644 index 000000000..544f116a0 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/mh_wrapper.py @@ -0,0 +1,221 @@ +from pkilib.common.exceptions import DirSrvException +from pkilib.common.factory import PkiTools +from pkilib.common.Qe_class import QeHost +from pkilib.common.mh_libdirsrv import DirSrv +from os.path import exists +import os.path +import subprocess +import socket + + +class W_DirSrv(object): + """ + This is a wrapper class for DirSrv object which validates all the inputs sent to DirSrv object. + Selects Ldap and SSL Ports sanely, Validates the inputs and in cases uses certain default values in + cases not all options are provided. + + Defaults: + + **DSInstHost: localhost** + + **DSRootDN:` Secret123** + + **DSInstSuffix:` 'dc=example,dc=org** + + **Ldap and TLS ports are choosen from the available list of below ports:** + + **DSPorts: [30389, 31389, 32389, 33389, 34389, 35389, 36389, 37389, 38389, 39389]** + + **TLSPorts: [30636, 31636, 32636, 33636, 34636, 35636, 36636, 37636, 38636, 39636]** + + """ + + + def __init__(self,Host=None): + """ + Create a DirSrv object for a specific Host. Specify the ports, Instance details to the Dirsrv object + + :param str Host: Host + """ + self.DSUsedPorts = {} + self.DirSrvInfo = {} + self.DirSrvInst = None + self.Host = Host + + def _set_options(self): + """ + Set default values: + Defaults: + DSInstHost: localhost + DSRootDN: Secret123 + DSInstSuffix: 'dc=example,dc=org' + Ldap and TLS ports are choosen from the available list of below ports: + DSPorts = [30389, 31389, 32389, 33389, 34389, 35389, 36389, 37389, 38389, 39389 + TLSPorts = [30636, 31636, 32636, 33636, 34636, 35636, 36636, 37636, 38636, 39636] + """ + + if self.Host is None: + self.DSInstHost = socket.gethostname() + else: + self.DSInstHost = self.Host.hostname + + if self.DSRootDNPwd is None: + self.DSRootDNPwd = 'Secret123' + + if self.DSInstSuffix is None: + self.DSInstSuffix = "dc=example,dc=org" + #Get ports + try: + self.DSLdapPort, self.DSTLSPort = self._set_ports(self.DSLdapPort, self.DSTLSPort) + except IndexError as err: + return ("No More ports available", 1) + else: + self.DSUsedPorts[self.DSIntName]=[self.DSLdapPort, self.DSTLSPort] + #validate Instance + try: + self._validate_options() + except DirSrvException as err: + return err.msg, err.rval + else: + return ("Success", 0) + + def _set_ports(self, u_port, e_port): + + """ + Idea behind this is when a directory server instance needs + to be created we need ports for ldap and ssl ports. + 1. check if LdapPort and SSLPort is given + 1.1 If given, verify if the ports are available(not used) + 1.1.1. Bind that port to ldap_port_t using semanage command + 1.1.2. Use the ports and add it to the self.UsedPorts list + 1.2 else raise exception + 2. If LdapPort and SSLPort is not given. + 2.1 Check if the ports are available(not used) + 2.1.1. Bind the port to ldap_port_t using semanage command + 2.1.2. Use the ports and add it to self.UsedPorts list + """ + DSPorts = [30389, 31389, 32389, 33389, 34389, 35389, 36389, 37389, 38389, 39389] + TLSPorts = [30636, 31636, 32636, 33636, 34636, 35636, 36636, 37636, 38636, 39636] + + if u_port is None and e_port is None: + for ldap_port, ldaps_port in zip(DSPorts, TLSPorts): + if (self._check_remote_port(ldap_port) or self._check_remote_port(ldaps_port)): + pass + else: + return ldap_port, ldaps_port + else: + a = [] + for ports in self.DSUsedPorts.values(): + a.append(ports) + + b = [] + for l_port, s_port in zip(DSPorts, TLSPorts): + b.append((l_port,s_port)) + + if (len(set(a)) > len(set(b))): + available_ports = set(a) - set(b) + else: + available_ports = set(b) - set(a) + print("available_ports =", available_ports) + sorted_available_ports = sorted(available_ports) + return sorted_available_ports[0] + + def _check_remote_port(self, port): + """ + checks if the port on the remote host is free + + :param: int port: + + :return bool: True if port is free else return False if port is unavailable + """ + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + try: + result = s.connect((self.DSInstHost, port)) + except socket.error as e: + print("Unable to connect to port %s due to error %r" % (port, e.errno)) + return False + s.close() + if result != 0: + return True + else: + return False + + def _validate_options(self): + """ + Verifies if the instance directory alread exists + + :param: None + :return: raises DirSrvException if the instance directory already exists else retuns True + """ + if isinstance(self.Host, QeHost): + check_instance = ['ls ' '/etc/dirsrv/slapd-%s' % self.DSIntName] + try: + output = self.Host.run_command(check_instance, log_stdout=True,raiseonerr=True) + except subprocess.CalledProcessError as E: + return True + else: + raise DirSrvException('%s Instance already Exists' % self.DSIntName) + else: + if exists(os.path.join('/etc/dirsrv/', 'slapd-%s' % self.DSIntName)): + raise DirSrvException('%s Instance already Exists' % self.DSIntName) + else: + return True + + def CreateInstance(self, InstName, InstHost=None, InstSuffix=None, RootDNPwd=None, LdapPort=None, TLSPort=None): + """ + Creates Directory server instances + + :param str InstName: Instance Name + :param str InstHost: Host on which instance should be created + :param str InstSuffix: Suffix to be created + :param str RootDNPwd: Root DN password + :param str LdapPort: Ldap Port to be used + :param str TLSPort: TLSPort port to be used + + :return str result, return_code: output of the command and return code + + :raises DirSrvException: if Directory server instance could not be created + """ + self.DSIntName = InstName + self.DSInstHost = InstHost + self.DSInstSuffix = None + self.DSRootDNPwd = RootDNPwd + self.DSLdapPort = LdapPort + self.DSTLSPort = TLSPort + + result, return_code = self._set_options() + if return_code == 0: + self.DirSrvInst = DirSrv(self.DSIntName, self.DSInstHost, + self.DSInstSuffix, self.DSRootDNPwd, self.DSLdapPort, + self.DSTLSPort,self.Host) + cfg_file = self.DirSrvInst.create_config() + result = self.DirSrvInst.Setup_DSInstance(cfg_file) + self.DirSrvInfo[self.DSIntName] = self.DirSrvInst.__dict__ + return result, return_code + else: + raise DirSrvException('Could not setup Directory Server Instance') + + + def RemoveInstance(self, InstName): + """ + Removes Directory server instance + + :param str InstName: + + :return bool: Returns True + + :raises DirSrvException: if Directory Server instance cannot be removed + """ + ret = self.DirSrvInfo[InstName] + if ret['InstName'] == InstName: + DSInstName = ret['DSInstName'] + result = self.DirSrvInst.Remove_DSInstance(DSInstName) + if result: + del self.DSUsedPorts[InstName] + return True + else: + raise DirSrvException('Could not remove Directory Server Instance', DSInstName) + else: + raise DirSrvException("%s Instance could not be found" %(InstName)) + diff --git a/tests/dogtag/shared/python/pkilib/common/wrapper.py b/tests/dogtag/shared/python/pkilib/common/wrapper.py new file mode 100644 index 000000000..93ffb7e98 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/common/wrapper.py @@ -0,0 +1,157 @@ +from pkilib.common.exceptions import DirSrvException +from pkilib.common.factory import PkiTools +from pkilib.common.libdirsrv2 import DirSrv +from ipapython import ipautil +from os.path import exists +import os.path +import subprocess +import socket + + +class W_DirSrv(object): + """ + This is a wrapper class for DirSrv object + Validates all the inputs sent to DirSrv object. + Chooses Ldap and SSL Ports sanely Validates the inputs + and In cases uses certain default values in + cases not all options are provided. + """ + def __init__(self): + + self.DSUsedPorts = {} + self.DirSrvInfo = {} + self.DirSrvInst = None + + def _set_options(self): + + if self.DSInstHost is None: + self.DSInstHost = socket.gethostname() + + if self.DSRootDNPwd is None: + self.DSRootDNPwd = 'Secret123' + + if self.DSInstSuffix is None: + self.DSInstSuffix = "o=%s" % self.DSIntName + + try: + self.DSLdapPort, self.DSTLSPort = self._set_ports(self.DSLdapPort, self.DSTLSPort) + except IndexError as err: + return ("No More ports available", 1) + else: + self.DSUsedPorts[self.DSIntName]=(self.DSLdapPort, self.DSTLSPort) + + output, process_ret = self._bind_to_selinux([self.DSLdapPort, self.DSTLSPort]) + if process_ret != 0: + raise DirSrvException("Unable to bind ports to ldap_port_t, Error: ",output) + else: + try: + self._validate_options() + except DirSrvException as err: + return err.msg, err.rval + else: + return ("Success", 0) + + def _set_ports(self, u_port, e_port): + + """ + Idea behind this is when a directory server instance needs + to be created we need ports for ldap and ssl ports. + 1. check if LdapPort and SSLPort is given + 1.1 If given, verify if the ports are available(not used) + 1.1.1. Bind that port to ldap_port_t using semanage command + 1.1.2. Use the ports and add it to the self.UsedPorts list + 1.2 else raise exception + 2. If LdapPort and SSLPort is not given. + 2.1 Check if the ports are available(not used) + 2.1.1. Bind the port to ldap_port_t using semanage command + 2.1.2. Use the ports and add it to self.UsedPorts list + """ + DSPorts = [30389, 31389, 32389, 33389, 34389, 35389, 36389, 37389, 38389, 39389] + TLSPorts = [30636, 31636, 32636, 33636, 34636, 35636, 36636, 37636, 38636, 39636] + + if u_port is None and e_port is None: + for ldap_port, ldaps_port in zip(DSPorts, TLSPorts): + if (self._check_port(ldap_port) or self._check_port(ldaps_port)): + pass + else: + return ldap_port, ldaps_port + else: + a = [] + for ports in self.DSUsedPorts.values(): + a.append(ports) + + b = [] + for l_port, s_port in zip(DSPorts, TLSPorts): + b.append((l_port,s_port)) + + if (len(set(a)) > len(set(b))): + available_ports = set(a) - set(b) + else: + available_ports = set(b) - set(a) + print("available_ports =", available_ports) + sorted_available_ports = sorted(available_ports) + return sorted_available_ports[0] + + def _check_port(self, port): + """ + Verify if the port given is available. + Returns True if port is already in use else returns False + """ + return ipautil.host_port_open(None, port) + + def _bind_to_selinux(self, ldap_ports): + """ Use semanage to bind ldap and ldaps ports to ldap_port_t """ + for port in ldap_ports: + semanage_args = ['semanage', 'port', '-a', '-t', 'ldap_port_t', '-p', 'tcp', str(port)] + try: + stdout, stderr, process_ret = PkiTools.Execute(semanage_args) + except subprocess.CalledProcessError: + return ("Error", 1) + else: + if process_ret == 1: + return stdout, 0 + elif process_ret != 0: + return stderr, process_ret + else: + return stdout, process_ret + + def _validate_options(self): + if exists(os.path.join('/etc/dirsrv/', 'slapd-%s' % self.DSIntName)): + raise DirSrvException('%s Instance already Exists' % self.DSIntName) + else: + return True + + def _CreateInstance(self, InstName, InstHost=None, InstSuffix=None, RootDNPwd=None, LdapPort=None, TLSPort=None): + + self.DSIntName = InstName + self.DSInstHost = InstHost + self.DSInstSuffix = None + self.DSRootDNPwd = RootDNPwd + self.DSLdapPort = LdapPort + self.DSTLSPort = TLSPort + + result, return_code = self._set_options() + if return_code == 0: + self.DirSrvInst = DirSrv(self.DSIntName, self.DSInstHost, + self.DSInstSuffix, self.DSRootDNPwd, self.DSLdapPort, + self.DSTLSPort) + cfg_file = self.DirSrvInst._create_config() + result = self.DirSrvInst._Setup_DSInstance(cfg_file) + self.DirSrvInfo[self.DSIntName] = self.DirSrvInst.__dict__ + return result + else: + return result, return_code + + def _RemoveInstance(self, InstName): + ret = self.DirSrvInfo[InstName] + if ret['InstName'] == InstName: + DSInstName = ret['DSInstName'] + result = self.DirSrvInst._Remove_DSInstance(DSInstName) + if result: + del self.DSUsedPorts[InstName] + return True + else: + return False + else: + return False + diff --git a/tests/dogtag/shared/python/pkilib/etc/mh_cfg_m.yaml b/tests/dogtag/shared/python/pkilib/etc/mh_cfg_m.yaml new file mode 100644 index 000000000..7226130ea --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/etc/mh_cfg_m.yaml @@ -0,0 +1,8 @@ +root_password: 'redhat' +domains: + - name: testrelm.test + type: cs + hosts: + - name: hostname1 + ip: 192.168.122.1 + role: master diff --git a/tests/dogtag/shared/python/pkilib/etc/mh_cfg_mc.yaml b/tests/dogtag/shared/python/pkilib/etc/mh_cfg_mc.yaml new file mode 100644 index 000000000..aa5a53d11 --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/etc/mh_cfg_mc.yaml @@ -0,0 +1,12 @@ +root_password: 'redhat' +domains: + - name: testrelm.test + type: cs + hosts: + - name: hostname1 + ip: 192.168.122.1 + role: master + - name: hostname2 + ip: 192.168.122.3 + role: client + diff --git a/tests/dogtag/shared/python/pkilib/etc/mh_cfg_mrc.yaml b/tests/dogtag/shared/python/pkilib/etc/mh_cfg_mrc.yaml new file mode 100644 index 000000000..62118ba2f --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/etc/mh_cfg_mrc.yaml @@ -0,0 +1,15 @@ +root_password: 'redhat' +domains: + - name: testrelm.test + type: cs + hosts: + - name: hostname1 + ip: 192.168.122.1 + role: master + - name: hostname2 + ip: 192.168.122.2 + role: clone + - name: hostname3 + ip: 192.168.122.3 + role: client + diff --git a/tests/dogtag/shared/python/pkilib/ui/__init__.py b/tests/dogtag/shared/python/pkilib/ui/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/dogtag/shared/python/pkilib/ui/__init__.py diff --git a/tests/dogtag/shared/python/setup.py b/tests/dogtag/shared/python/setup.py new file mode 100644 index 000000000..1d0d13d8d --- /dev/null +++ b/tests/dogtag/shared/python/setup.py @@ -0,0 +1,35 @@ +from setuptools import find_packages, setup + +REQUIRES = [ + 'python-ldap', + 'paramiko', + 'requests', + 'PyYAML', + 'pytest_multihost', + 'pytest' + ] + +with open('README.rst', 'r') as f: + README = f.read() + +setup( + name = 'pkilib', + version = '0.1', + description = u'Dogtag & Red Hat Certificate system python test suite', + long_description = README, + author = u'CS QE Team', + url = 'http://git.app.eng.bos.redhat.com/git/pki-tests.git/', + packages = find_packages(exclude=['tests*']), + package_data={'':['LICENSE']}, + include_package_data=True, + install_requires=REQUIRES, + license='GNU GPL v3.0', + classifiers=( + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + ), + ) + + + + |