This is intended to be a start. There is a lot more that needs to be documented both outside and within the code.
For now, things are not set up to auto-generate the docs at build time. --- doc/Makefile | 153 ++++++++++++++++++++++++ doc/blivet.devicelibs.rst | 75 ++++++++++++ doc/blivet.formats.rst | 91 +++++++++++++++ doc/blivet.rst | 179 ++++++++++++++++++++++++++++ doc/conf.py | 291 ++++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 23 ++++ doc/intro.rst | 197 +++++++++++++++++++++++++++++++ doc/modules.rst | 7 ++ 8 files changed, 1016 insertions(+) create mode 100644 doc/Makefile create mode 100644 doc/blivet.devicelibs.rst create mode 100644 doc/blivet.formats.rst create mode 100644 doc/blivet.rst create mode 100644 doc/conf.py create mode 100644 doc/index.rst create mode 100644 doc/intro.rst create mode 100644 doc/modules.rst
diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..68bfbdb --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# 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 + +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)" + +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/Blivet.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Blivet.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/Blivet" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Blivet" + @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/blivet.devicelibs.rst b/doc/blivet.devicelibs.rst new file mode 100644 index 0000000..bdbf2ae --- /dev/null +++ b/doc/blivet.devicelibs.rst @@ -0,0 +1,75 @@ +devicelibs Package +================== + +:mod:`btrfs` Module +------------------- + +.. automodule:: blivet.devicelibs.btrfs + :members: + :undoc-members: + :show-inheritance: + +:mod:`crypto` Module +-------------------- + +.. automodule:: blivet.devicelibs.crypto + :members: + :undoc-members: + :show-inheritance: + +:mod:`dm` Module +---------------- + +.. automodule:: blivet.devicelibs.dm + :members: + :undoc-members: + :show-inheritance: + +:mod:`edd` Module +----------------- + +.. automodule:: blivet.devicelibs.edd + :members: + :undoc-members: + :show-inheritance: + +:mod:`loop` Module +------------------ + +.. automodule:: blivet.devicelibs.loop + :members: + :undoc-members: + :show-inheritance: + +:mod:`lvm` Module +----------------- + +.. automodule:: blivet.devicelibs.lvm + :members: + :undoc-members: + :show-inheritance: + +:mod:`mdraid` Module +-------------------- + +.. automodule:: blivet.devicelibs.mdraid + :members: + :undoc-members: + :show-inheritance: + +:mod:`mpath` Module +------------------- + +.. automodule:: blivet.devicelibs.mpath + :members: + :undoc-members: + :show-inheritance: + +:mod:`swap` Module +------------------ + +.. automodule:: blivet.devicelibs.swap + :members: + :undoc-members: + :show-inheritance: + diff --git a/doc/blivet.formats.rst b/doc/blivet.formats.rst new file mode 100644 index 0000000..c8b6c03 --- /dev/null +++ b/doc/blivet.formats.rst @@ -0,0 +1,91 @@ +formats Package +=============== + +:mod:`formats` Package +---------------------- + +.. automodule:: blivet.formats + :members: + :undoc-members: + :show-inheritance: + +:mod:`biosboot` Module +---------------------- + +.. automodule:: blivet.formats.biosboot + :members: + :undoc-members: + :show-inheritance: + +:mod:`disklabel` Module +----------------------- + +.. automodule:: blivet.formats.disklabel + :members: + :undoc-members: + :show-inheritance: + +:mod:`dmraid` Module +-------------------- + +.. automodule:: blivet.formats.dmraid + :members: + :undoc-members: + :show-inheritance: + +:mod:`fs` Module +---------------- + +.. automodule:: blivet.formats.fs + :members: + :undoc-members: + :show-inheritance: + +:mod:`luks` Module +------------------ + +.. automodule:: blivet.formats.luks + :members: + :undoc-members: + :show-inheritance: + +:mod:`lvmpv` Module +------------------- + +.. automodule:: blivet.formats.lvmpv + :members: + :undoc-members: + :show-inheritance: + +:mod:`mdraid` Module +-------------------- + +.. automodule:: blivet.formats.mdraid + :members: + :undoc-members: + :show-inheritance: + +:mod:`multipath` Module +----------------------- + +.. automodule:: blivet.formats.multipath + :members: + :undoc-members: + :show-inheritance: + +:mod:`prepboot` Module +---------------------- + +.. automodule:: blivet.formats.prepboot + :members: + :undoc-members: + :show-inheritance: + +:mod:`swap` Module +------------------ + +.. automodule:: blivet.formats.swap + :members: + :undoc-members: + :show-inheritance: + diff --git a/doc/blivet.rst b/doc/blivet.rst new file mode 100644 index 0000000..e9886cc --- /dev/null +++ b/doc/blivet.rst @@ -0,0 +1,179 @@ +blivet Package +============== + +:mod:`blivet` Package +--------------------- + +.. automodule:: blivet.__init__ + :members: + :undoc-members: + :show-inheritance: + +:mod:`arch` Module +------------------ + +.. automodule:: blivet.arch + :members: + :undoc-members: + :show-inheritance: + +:mod:`dasd` Module +------------------ + +.. automodule:: blivet.dasd + :members: + :undoc-members: + :show-inheritance: + +:mod:`deviceaction` Module +-------------------------- + +.. automodule:: blivet.deviceaction + :members: + :undoc-members: + :show-inheritance: + +:mod:`devicefactory` Module +--------------------------- + +.. automodule:: blivet.devicefactory + :members: + :undoc-members: + :show-inheritance: + +:mod:`devices` Module +--------------------- + +.. automodule:: blivet.devices + :members: + :undoc-members: + :show-inheritance: + +:mod:`devicetree` Module +------------------------ + +.. automodule:: blivet.devicetree + :members: + :undoc-members: + :show-inheritance: + +:mod:`errors` Module +-------------------- + +.. automodule:: blivet.errors + :members: + :undoc-members: + :show-inheritance: + +:mod:`fcoe` Module +------------------ + +.. automodule:: blivet.fcoe + :members: + :undoc-members: + :show-inheritance: + +:mod:`flags` Module +------------------- + +.. automodule:: blivet.flags + :members: + :undoc-members: + :show-inheritance: + +:mod:`iscsi` Module +------------------- + +.. automodule:: blivet.iscsi + :members: + :undoc-members: + :show-inheritance: + +:mod:`partitioning` Module +-------------------------- + +.. automodule:: blivet.partitioning + :members: + :undoc-members: + :show-inheritance: + +:mod:`partspec` Module +---------------------- + +.. automodule:: blivet.partspec + :members: + :undoc-members: + :show-inheritance: + +:mod:`platform` Module +---------------------- + +.. automodule:: blivet.platform + :members: + :undoc-members: + :show-inheritance: + +:mod:`pyudev` Module +-------------------- + +.. automodule:: blivet.pyudev + :members: + :undoc-members: + :show-inheritance: + +:mod:`size` Module +------------------ + +.. automodule:: blivet.size + :members: + :undoc-members: + :show-inheritance: + +:mod:`storage_log` Module +------------------------- + +.. automodule:: blivet.storage_log + :members: + :undoc-members: + :show-inheritance: + +:mod:`tsort` Module +------------------- + +.. automodule:: blivet.tsort + :members: + :undoc-members: + :show-inheritance: + +:mod:`udev` Module +------------------ + +.. automodule:: blivet.udev + :members: + :undoc-members: + :show-inheritance: + +:mod:`util` Module +------------------ + +.. automodule:: blivet.util + :members: + :undoc-members: + :show-inheritance: + +:mod:`zfcp` Module +------------------ + +.. automodule:: blivet.zfcp + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + blivet.devicelibs + blivet.formats + diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..a1dda21 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- +# +# Blivet documentation build configuration file, created by +# sphinx-quickstart on Wed Sep 18 14:37:01 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.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage'] + +# 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'Blivet' +copyright = u'2013, David Lehman' + +# 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.22' +# The full version, including alpha/beta/rc tags. +release = '0.22' + +# 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 = [] + +autoclass_content = 'both' + + +# -- 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 = 'Blivetdoc' + + +# -- 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', 'Blivet.tex', u'Blivet Documentation', + u'David Lehman', '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', 'blivet', u'Blivet Documentation', + [u'David Lehman'], 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', 'Blivet', u'Blivet Documentation', + u'David Lehman', 'Blivet', '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'Blivet' +epub_author = u'David Lehman' +epub_publisher = u'David Lehman' +epub_copyright = u'2013, David Lehman' + +# 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 + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..4a5b677 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,23 @@ +.. Blivet documentation master file, created by + sphinx-quickstart on Wed Sep 18 14:37:01 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Blivet's documentation! +================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro + modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/intro.rst b/doc/intro.rst new file mode 100644 index 0000000..e377597 --- /dev/null +++ b/doc/intro.rst @@ -0,0 +1,197 @@ +Introduction to Blivet +====================== + +Blivet is a python module for system storage configuration. + +The main thing that blivet offers is the ability to model a series of changes +without necessarily commiting any of the changes to disk. You can schedule an +arbitrarily large series of changes (called 'actions'), seeing the effects of +each (within the :class:`~.devicetree.DeviceTree` instance) as it is scheduled. +Nothing is written to disk, however, until you execute the actions. + +Building Blocks +=============== + +Individual block devices are represented by the various subclasses of +:class:`~.devices.StorageDevice`, while the formatting of the data they contain +is represented by the various subclasses of :class:`~.formats.DeviceFormat`. +The hierarchy of devices is represented by :class:`~.devicetree.DeviceTree`. + +:class:`~.devices.DiskDevice`, :class:`~.devices.PartitionDevice`, +:class:`~.devices.LVMLogicalVolumeDevice`, and +:class:`~.devices.MDRaidArrayDevice` are some of the most important examples of +:class:`~.devices.StorageDevice` subclasses. + +Some examples of :class:`~.formats.DeviceFormat` subclasses include +:class:`~.formats.swap.SwapSpace`, :class:`~.formats.disklabel.DiskLabel`, +and subclasses of :class:`~.formats.fs.FS` such as :class:`~.formats.fs.Ext4FS` +and :class:`~.formats.fs.XFS`. + +Every :class:`~.devices.StorageDevice` instance has a :attr:`~.devices.StorageDevice.format` that contains an instance of some :class:`~.formats.DeviceFormat` +subclass -- even if it is "blank" or "unformatted" (in which case it is an instance of :class:`~.formats.DeviceFormat` itself). + +Every :class:`~.formats.DeviceFormat` has a +:attr:`~.formats.DeviceFormat.device` attribute that is a string representing +the path to the device node for the block device containing the formatting. +:class:`~.devices.StorageDevice` and :class:`~.formats.DeviceFormat` can +represent either existent or non-existent devices and formatting. + +:class:`~.devices.StorageDevice` and :class:`~.formats.DeviceFormat` share a similar API, which consists of methods to control existing devices/formats +(:meth:`~.devices.StorageDevice.setup`, +:meth:`~.devices.StorageDevice.teardown`), methods to create or modify +devices/formats (:meth:`~.devices.StorageDevice.create`, +:meth:`~.devices.StorageDevice.destroy`, :meth:`~.devices.StorageDevice.resize`) +, and attributes to store critical data +(:attr:`~.devices.StorageDevice.status`, :attr:`~.devices.StorageDevice.exists`) +. Some useful attributes of :class:`~.devices.StorageDevice` that are not found +in :class:`~.formats.DeviceFormat` include +:attr:`~.devices.Device.parents`, :attr:`~.devices.Device.isleaf`, +:attr:`~.devices.Device.ancestors`, and :attr:`~.devices.StorageDevice.disks`. + +:class:`~.devicetree.DeviceTree` provides +:attr:`~.devicetree.DeviceTree.devices` which is a list of +:class:`~.devices.StorageDevice` instances representing the current state of the +system as configured within blivet. It also provides some methods for looking up +devices (:meth:`~.devicetree.DeviceTree.getDeviceByName`), for listing devices +that build upon a device (:meth:`~.devicetree.DeviceTree.getDependentDevices`), +and for listing direct descendants of a given device +(:meth:`~.devicetree.DeviceTree.getChildren`). + +Getting Started +=============== + +First Steps +----------- + +First, create an instance of the :class:`~.Blivet` class:: + + import blivet + b = blivet.Blivet() + +Next, scan the system's storage configuration and store it in the tree:: + + b.reset() + +Now, you can do some simple things like listing the devices:: + + for device in b.devices: + print device + +To make changes to the configuration you'll schedule actions, but +:class:`~.Blivet` provides some convenience methods to hide the details. Here's an example of removing partition '/dev/sda3':: + + sda3 = b.devicetree.getDeviceByName("sda3") + b.destroyDevice(sda3) # schedules actions to destroy format and device + +At this point, the StorageDevice representing sda3 is no longer in the tree. +That means you could allocate a new partition from the newly free space if you +wanted to (via blivet, that is, since there is not actually any free space on +the physical disk yet -- you haven't commited the changes). If you now ran the +following line:: + + sda3 = b.devicetree.getDeviceByName("sda3") + +sda3 would be None since that device has been removed from the tree. + +When you are ready to commit your changes to disk, here's how:: + + b.doIt() + +That's it. Now you have actually removed /dev/sda3 from the disk. + +Here's an alternative approach that uses the lower-level +:class:`~.devicetree.DeviceTree` class directly:: + + import blivet + dt = blivet.devicetree.DeviceTree() + dt.populate() + sda3 = dt.getDeviceByName("sda3") + action1 = ActionDestroyFormat(sda3) + action2 = ActionDestroyDevice(sda3) + dt.registerAction(action1) + dt.registerAction(action2) + dt.processActions() + +Here's the Blivet approach again for comparison:: + + import blivet + b = blivet.Blivet() # contains a DeviceTree instance + b.reset() # calls DeviceTree.populate() + sda3 = b.devicetree.getDeviceByName("sda3") + b.destroyDevice(sda3) # schedules actions to destroy format and device + b.doIt() # calls DeviceTree.processActions() + + +Scheduling a Series of Actions +------------------------------ + +Start out as before:: + + import blivet + b = blivet.Blivet() + b.reset() + sda3 = b.devicetree.getDeviceByName("sda3") + +Now we're going to wipe the existing formatting from sda3:: + + b.destroyFormat(sda3) + +Now let's assume sda3 is larger than 10GB and resize it to that size:: + + b.resizeDevice(sda3, 10000) # size is in MB + +And then let's create a new ext4 filesystem there:: + + new_fmt = blivet.formats.getFormat("ext4", device=sda3.path) + b.formatDevice(sda3, new_fmt) + +If you want to commit the whole set of changes in one shot, it's easy:: + + b.doIt() + +Now you can mount the new filesystem at the directory "/mnt/test":: + + sda3.format.setup(mountpoint="/mnt/test") + +Once you're finished, unmount it as follows:: + + sda3.format.teardown() + + +Disk Partitions +--------------- + +Disk partitions are a little bit tricky in that they require an extra step to +actually allocate the partitions from free space on the disk(s). What that +means is deciding exactly which sectors on which disk the new partition will +occupy. Blivet offers some powerful means for deciding for you where to place +the partitions, but it also allows you to specify an exact start and end +sector on a specific disk if that's how you want to do it. Here's an example +of letting Blivet handle the details of creating a partition of minimum size +10GB on either sdb or sdc that is also growable to a maximum size of 20GB:: + + sdb = b.devicetree.getDeviceByName("sdb") + sdc = b.devicetree.getDeviceByName("sdc") + new_part = b.newPartition(size=10000, grow=True, maxsize=20000, + parents=[sdb, sdc]) + b.createDevice(new_part) + blivet.partitioning.doPartitioning(b) + +Now you could see where it ended up:: + + print("partition %s of size %dMiB on disk %s" % (new_part.name, + new_part.size, + new_part.disk.name)) + +From here, everything is the same as it was in the first examples. All that's +left is to execute the scheduled action:: + + b.doIt() # or b.devicetree.processActions() + +Backing up, let's see how it looks if you want to specify the start and end +sectors. If you specify a start sector you have to also specify a single disk +from which to allocate the partition:: + + new_part = b.newPartition(start=2048, end=204802048, parents=[sdb]) + +All the rest is the same as the previous partitioning example. diff --git a/doc/modules.rst b/doc/modules.rst new file mode 100644 index 0000000..3910865 --- /dev/null +++ b/doc/modules.rst @@ -0,0 +1,7 @@ +Blivet +====== + +.. toctree:: + :maxdepth: 4 + + blivet
doc/Makefile | 153 ++++++++++++++++++++++++ doc/blivet.devicelibs.rst | 75 ++++++++++++ doc/blivet.formats.rst | 91 +++++++++++++++ doc/blivet.rst | 179 ++++++++++++++++++++++++++++ doc/conf.py | 291 ++++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 23 ++++ doc/intro.rst | 197 +++++++++++++++++++++++++++++++ doc/modules.rst | 7 ++ 8 files changed, 1016 insertions(+) create mode 100644 doc/Makefile create mode 100644 doc/blivet.devicelibs.rst create mode 100644 doc/blivet.formats.rst create mode 100644 doc/blivet.rst create mode 100644 doc/conf.py create mode 100644 doc/index.rst create mode 100644 doc/intro.rst create mode 100644 doc/modules.rst
ACK, though I think if you wanted to be extra tricky, you could condense down the repetitive Makefile bits some. You know, if you get bored.
- Chris
--- blivet/__init__.py | 330 +++++++++++++++--- blivet/dasd.py | 3 - blivet/devices.py | 833 ++++++++++++++++++++++++-------------------- blivet/devicetree.py | 248 +++++++++++-- blivet/formats/__init__.py | 107 ++++-- blivet/formats/biosboot.py | 12 +- blivet/formats/disklabel.py | 72 ++-- blivet/formats/dmraid.py | 16 +- blivet/formats/fs.py | 146 +++++--- blivet/formats/luks.py | 74 +++- blivet/formats/lvmpv.py | 47 ++- blivet/formats/mdraid.py | 20 +- blivet/formats/multipath.py | 15 +- blivet/formats/prepboot.py | 29 +- blivet/formats/swap.py | 60 +++- blivet/iscsi.py | 3 - blivet/partitioning.py | 447 ++++++++++++++++-------- 17 files changed, 1645 insertions(+), 817 deletions(-)
diff --git a/blivet/__init__.py b/blivet/__init__.py index d5904e5..20bed1c 100644 --- a/blivet/__init__.py +++ b/blivet/__init__.py @@ -1,5 +1,4 @@ # __init__.py -# Entry point for anaconda's storage configuration module. # # Copyright (C) 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. # @@ -95,6 +94,7 @@ import logging log = logging.getLogger("blivet")
def enable_installer_mode(): + """ Configure the module for use by anaconda (OS installer). """ global isys global ROOT_PATH global shortProductName @@ -238,13 +238,17 @@ class StorageDiscoveryConfig(object): self.initializeDisks = False self.protectedDevSpecs = [] self.diskImages = {} - self.mpathFriendlyNames = True
# Whether clearPartitions removes scheduled/non-existent devices and # disklabels depends on this flag. self.clearNonExistent = False
def update(self, ksdata): + """ Update configuration from ksdata source. + + :param ksdata: kickstart data used as data source + :type ksdata: :class:`pykickstart.Handler` + """ self.ignoredDisks = ksdata.ignoredisk.ignoredisk[:] self.exclusiveDisks = ksdata.ignoredisk.onlyuse[:] self.clearPartType = ksdata.clearpart.type @@ -256,11 +260,9 @@ class StorageDiscoveryConfig(object): class Blivet(object): """ Top-level class for managing storage configuration. """ def __init__(self, ksdata=None): - """ Create a Blivet instance. - - Keyword Arguments: - - ksdata - a pykickstart Handler instance + """ + :keyword ksdata: kickstart data store + :type ksdata: :class:`pykickstart.Handler` """ self.ksdata = ksdata self._bootloader = None @@ -304,6 +306,7 @@ class Blivet(object): self.services = set()
def doIt(self): + """ Commit queued changes to disk. """ self.devicetree.processActions() if not flags.installer_mode: return @@ -368,11 +371,13 @@ class Blivet(object):
@property def nextID(self): + """ Used for creating unique placeholder names. """ id = self._nextID self._nextID += 1 return id
def shutdown(self): + """ Deactivate all devices (installer_mode only). """ try: self.devicetree.teardownAll() except Exception as e: @@ -381,9 +386,14 @@ class Blivet(object): def reset(self, cleanupOnly=False): """ Reset storage configuration to reflect actual system state.
- This should rescan from scratch but not clobber user-obtained - information like passphrases, iscsi config, &c + This will cancel any queued actions and rescan from scratch but not + clobber user-obtained information like passphrases, iscsi config, &c
+ :keyword cleanupOnly: prepare the tree only to deactivate devices + :type cleanupOnly: bool + + See :meth:`devicetree.Devicetree.populate` for more information + about the cleanupOnly keyword argument. """ log.info("resetting Blivet (version %s) instance %s" % (__version__, self)) if flags.installer_mode: @@ -470,7 +480,7 @@ class Blivet(object): def disks(self): """ A list of the disks in the device tree.
- Ignored disks are not included, as are disks with no media present. + Ignored disks are excluded, as are disks with no media present.
This is based on the current state of the device tree and does not necessarily reflect the actual on-disk state of the @@ -551,12 +561,24 @@ class Blivet(object):
@property def thinlvs(self): + """ A list of the LVM Thin Logical Volumes in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ thin = self.devicetree.getDevicesByType("lvmthinlv") thin.sort(key=lambda d: d.name) return thin
@property def thinpools(self): + """ A list of the LVM Thin Pool Logical Volumes in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ pools = self.devicetree.getDevicesByType("lvmthinpool") pools.sort(key=lambda d: d.name) return pools @@ -608,6 +630,12 @@ class Blivet(object):
@property def btrfsVolumes(self): + """ A list of the BTRFS volumes in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ return sorted(self.devicetree.getDevicesByType("btrfs volume"), key=lambda d: d.name)
@@ -625,6 +653,21 @@ class Blivet(object): return swaps
def shouldClear(self, device, **kwargs): + """ Return True if a clearpart settings say a device should be cleared. + + :param device: the device (required) + :type device: :class:`~.devices.StorageDevice` + :keyword clearPartType: overrides :attr:`self.config.clearPartType` + :type clearPartType: int + :keyword clearPartDisks: overrides + :attr:`self.config.clearPartDisks` + :type clearPartDisks: list + :keyword clearPartDevices: overrides + :attr:`self.config.clearPartDevices` + :type clearPartDevices: list + :returns: whether or not clearPartitions should remove this device + :rtype: bool + """ clearPartType = kwargs.get("clearPartType", self.config.clearPartType) clearPartDisks = kwargs.get("clearPartDisks", self.config.clearPartDisks) @@ -704,6 +747,14 @@ class Blivet(object): return True
def recursiveRemove(self, device): + """ Remove a device after removing its dependent devices. + + If the device is not a leaf, all of its dependents are removed + recursively until it is a leaf device. At that point the device is + removed, unless it is a disk. If the device is a disk, its + formatting is removed by no attempt is made to actually remove the + disk device. + """ log.debug("removing %s" % device.name) devices = self.deviceDeps(device)
@@ -727,17 +778,7 @@ class Blivet(object): self.destroyDevice(device)
def clearPartitions(self): - """ Clear partitions and dependent devices from disks. - - Arguments: - - None - - NOTES: - - - Needs some error handling - - """ + """ Clear partitions and dependent devices from disks. """ # Sort partitions by descending partition number to minimize confusing # things like multiple "destroy sda5" actions due to parted renumbering # partitions. This can still happen through the UI but it makes sense to @@ -771,7 +812,14 @@ class Blivet(object): """ (Re)initialize a disk by creating a disklabel on it.
The disk should not contain any partitions except perhaps for a - magic partitions on mac and sun disklabels. + magic partitions on mac and sun disklabels. If the disk does contain + partitions other than the disklabel-type-specific "magic" partitions + ValueError will be raised. + + :param disk: the disk to initialize + :type disk: :class:`~.devices.StorageDevice` + :returns None: + :raises: ValueError """ # first, remove magic mac/sun partitions from the parted Disk if disk.partitioned: @@ -825,6 +873,18 @@ class Blivet(object): disks and clearPartType allow specifying a set of disks other than self.disks and a clearPartType value other than self.config.clearPartType. + + :keyword disks: overrides :attr:`disks` + :type disks: list + :keyword clearPartType: overrides :attr:`self.config.clearPartType` + :type clearPartType: int + :returns: dict with disk name keys and tuple (disk, fs) free values + :rtype: dict + + .. note:: + + The free space values are :class:`~.size.Size` instances. + """ from size import Size if disks is None: @@ -868,13 +928,32 @@ class Blivet(object):
@property def names(self): + """ A list of all of the known in-use device names. """ return self.devicetree.names
def deviceDeps(self, device): + """ Return a list of the devices that depend on the specified device. + + :param device: the subtree root device + :type device: :class:`~.devices.StorageDevice` + :returns: list of dependent devices + :rtype: list + """ return self.devicetree.getDependentDevices(device)
def newPartition(self, *args, **kwargs): - """ Return a new PartitionDevice instance for configuring. """ + """ Return a new (unallocated) PartitionDevice instance. + + :keyword fmt_type: format type + :type fmt_type: str + :keyword fmt_args: arguments for format constructor + :type fmt_args: dict + :keyword mountpoint: mountpoint for format (filesystem) + :type mountpoint: str + + All other arguments are passed on to the + :class:`~.devices.PartitionDevice` constructor. + """ if kwargs.has_key("fmt_type"): kwargs["format"] = getFormat(kwargs.pop("fmt_type"), mountpoint=kwargs.pop("mountpoint", @@ -898,7 +977,23 @@ class Blivet(object): return PartitionDevice(name, *args, **kwargs)
def newMDArray(self, *args, **kwargs): - """ Return a new MDRaidArrayDevice instance for configuring. """ + """ Return a new MDRaidArrayDevice instance. + + :keyword fmt_type: format type + :type fmt_type: str + :keyword fmt_args: arguments for format constructor + :type fmt_args: dict + :keyword mountpoint: mountpoint for format (filesystem) + :type mountpoint: str + :returns: the new md array device + :rtype: :class:`~.devices.MDRaidArrayDevice` + + All other arguments are passed on to the + :class:`~.devices.MDRaidArrayDevice` constructor. + + If a name is not specified, one will be generated based on the + format type, mountpoint, hostname, and/or product name. + """ if kwargs.has_key("fmt_type"): kwargs["format"] = getFormat(kwargs.pop("fmt_type"), mountpoint=kwargs.pop("mountpoint", @@ -922,7 +1017,17 @@ class Blivet(object): return MDRaidArrayDevice(name, *args, **kwargs)
def newVG(self, *args, **kwargs): - """ Return a new LVMVolumeGroupDevice instance. """ + """ Return a new LVMVolumeGroupDevice instance. + + :returns: the new volume group device + :rtype: :class:`~.devices.LVMVolumeGroupDevice` + + All arguments are passed on to the + :class:`~.devices.LVMVolumeGroupDevice` constructor. + + If a name is not specified, one will be generated based on the + hostname, and/or product name. + """ pvs = kwargs.pop("parents", []) for pv in pvs: if pv not in self.devices: @@ -948,7 +1053,32 @@ class Blivet(object): return LVMVolumeGroupDevice(name, pvs, *args, **kwargs)
def newLV(self, *args, **kwargs): - """ Return a new LVMLogicalVolumeDevice instance. """ + """ Return a new LVMLogicalVolumeDevice instance. + + :keyword fmt_type: format type + :type fmt_type: str + :keyword fmt_args: arguments for format constructor + :type fmt_args: dict + :keyword mountpoint: mountpoint for format (filesystem) + :type mountpoint: str + :keyword thin_pool: whether to create a thin pool + :type thin_pool: bool + :keyword thin_volume: whether to create a thin volume + :type thin_volume: bool + :returns: the new device + :rtype: :class:`~.devices.LVMLogicalVolumeDevice` + + All other arguments are passed on to the appropriate + :class:`~.devices.LVMLogicalVolumeDevice` constructor. + + If a name is not specified, one will be generated based on the + format type and/or mountpoint. + + .. note:: + + If you are creating a thin volume, the parents kwarg should + contain the pool -- not the vg. + """ thin_volume = kwargs.pop("thin_volume", False) thin_pool = kwargs.pop("thin_pool", False) vg = kwargs.get("parents", [None])[0] @@ -1001,7 +1131,30 @@ class Blivet(object): return device_class(name, *args, **kwargs)
def newBTRFS(self, *args, **kwargs): - """ Return a new BTRFSVolumeDevice or BRFSSubVolumeDevice. """ + """ Return a new BTRFSVolumeDevice or BRFSSubVolumeDevice. + + :keyword fmt_args: arguments for format constructor + :type fmt_args: dict + :keyword mountpoint: mountpoint for format (filesystem) + :type mountpoint: str + :keyword subvol: whether this is a subvol (as opposed to a volume) + :type subvol: bool + :returns: the new device + :rtype: :class:`~.devices.BTRFSDevice` + + All other arguments are passed on to the appropriate + :class:`~.devices.BTRFSDevice` constructor. + + For volumes, the label is the same as the name. If a name/label is + not specified, one will be generated based on the hostname and/or + product name. + + .. note:: + + If you are creating a subvolume, the parents kwarg should + contain the volume you want to contain the subvolume. + + """ log.debug("newBTRFS: args = %s ; kwargs = %s" % (args, kwargs)) name = kwargs.pop("name", None) if args: @@ -1049,6 +1202,24 @@ class Blivet(object): return device
def newBTRFSSubVolume(self, *args, **kwargs): + """ Return a new BRFSSubVolumeDevice. + + :keyword fmt_args: arguments for format constructor + :type fmt_args: dict + :keyword mountpoint: mountpoint for format (filesystem) + :type mountpoint: str + :returns: the new device + :rtype: :class:`~.devices.BTRFSSubVolumeDevice` + + All other arguments are passed on to the + :class:`~.devices.BTRFSSubVolumeDevice` constructor. + + .. note:: + + Since you are creating a subvolume, the parents kwarg should + contain the volume you want to contain the subvolume. + + """ kwargs["subvol"] = True return self.newBTRFS(*args, **kwargs)
@@ -1059,15 +1230,21 @@ class Blivet(object): def createDevice(self, device): """ Schedule creation of a device.
- TODO: We could do some things here like assign the next - available raid minor if one isn't already set. + :param device: the device to schedule creation of + :type device: :class:`~.devices.StorageDevice` + :rtype: None """ self.devicetree.registerAction(ActionCreateDevice(device)) if device.format.type: self.devicetree.registerAction(ActionCreateFormat(device))
def destroyDevice(self, device): - """ Schedule destruction of a device. """ + """ Schedule destruction of a device. + + :param device: the device to schedule destruction of + :type device: :class:`~.devices.StorageDevice` + :rtype: None + """ if device.format.exists and device.format.type: # schedule destruction of any formatting while we're at it self.devicetree.registerAction(ActionDestroyFormat(device)) @@ -1076,12 +1253,29 @@ class Blivet(object): self.devicetree.registerAction(action)
def formatDevice(self, device, format): - """ Schedule formatting of a device. """ + """ Schedule formatting of a device. + + :param device: the device to create the formatting on + :type device: :class:`~.devices.StorageDevice` + :param format: the format to create on the device + :type format: :class:`~.formats.DeviceFormat` + :rtype: None + + A format destroy action will be scheduled first, so it is not + necessary to create and schedule an + :class:`~.deviceaction.ActionDestroyFormat` prior to calling this + method. + """ self.devicetree.registerAction(ActionDestroyFormat(device)) self.devicetree.registerAction(ActionCreateFormat(device, format))
def resetDevice(self, device): - """ Cancel all scheduled actions and reset formatting. """ + """ Cancel all scheduled actions and reset formatting. + + :param device: the device to reset + :type device: :class:`~.devices.StorageDevice` + :rtype: None + """ actions = self.devicetree.findActions(device=device) for action in reversed(actions): self.devicetree.cancelAction(action) @@ -1090,6 +1284,17 @@ class Blivet(object): device.format = copy.copy(device.originalFormat)
def resizeDevice(self, device, new_size): + """ Schedule a resize of a device and its formatting, if any. + + :param device: the device to resize + :type device: :class:`~.devices.StorageDevice` + :param new_size: the new target size for the device + :type new_size: :class:`~.size.Size` + :rtype: None + + If the device has formatting that is recognized as being resizable + an action will be scheduled to resize it as well. + """ classes = [] if device.resizable: classes.append(ActionResizeDevice) @@ -1165,7 +1370,13 @@ class Blivet(object): return tmp
def suggestContainerName(self, hostname=None, prefix=""): - """ Return a reasonable, unused device name. """ + """ Return a reasonable, unused device name. + + :keyword hostname: the system's hostname + :keyword prefix: a prefix for the container name + :returns: the suggested name + :rtype: str + """ if not prefix: prefix = shortProductName
@@ -1198,7 +1409,19 @@ class Blivet(object):
def suggestDeviceName(self, parent=None, swap=None, mountpoint=None, prefix=""): - """ Return a suitable, unused name for a new logical volume. """ + """ Return a suitable, unused name for a new device. + + :keyword parent: the parent device + :type parent: :class:`~.devices.StorageDevice` + :keyword swap: will this be a swap device + :type swap: bool + :keyword mountpoint: the device's mountpoint + :type mountpoint: str + :keyword prefix: device name prefix + :type prefix: str + :returns: the suggested name + :rtype: str + """ body = "" if mountpoint: if mountpoint == "/": @@ -1253,6 +1476,7 @@ class Blivet(object):
@property def fileSystemFreeSpace(self): + """ Combined free space in / and /usr as :class:`~.size.Size`. """ mountpoints = ["/", "/usr"] free = 0 btrfs_volumes = [] @@ -1279,6 +1503,8 @@ class Blivet(object): def sanityCheck(self): """ Run a series of tests to verify the storage configuration.
+ This is installer-specific. + This function is called at the end of partitioning so that we can make sure you don't have anything silly (like no /, a really small /, etc). Returns (errors, warnings) where @@ -1482,6 +1708,7 @@ class Blivet(object): return list(pkgs)
def write(self): + """ Write out all storage-related configuration files. """ if not os.path.isdir("%s/etc" % ROOT_PATH): os.mkdir("%s/etc" % ROOT_PATH)
@@ -1702,26 +1929,14 @@ class Blivet(object): def factoryDevice(self, device_type, size, **kwargs): """ Schedule creation of a device based on a top-down specification.
- Arguments: - - device_type an AUTOPART_TYPE constant (lvm|btrfs|plain) - size device's requested size - - Keyword arguments: - - mountpoint the device's mountpoint - fstype the device's filesystem type, or swap - label filesystem label - disks the set of disks we can allocate from - encrypted boolean + :param device_type: device type constant + :type device_type: int (:const:`~.devicefactory.DEVICE_TYPE_*`) + :param size: requested size + :type size: :class:`~.size.Size` + :returns: the newly configured device + :rtype: :class:`~.devices.StorageDevice`
- raid_level (btrfs/md/lvm only) RAID level (string) - - name name for new device - container_name name of requested container - - device an already-defined but non-existent device - to adjust instead of creating a new device + See :class:`~.devicefactory.DeviceFactory` for possible kwargs.
""" log_method_call(self, device_type, size, **kwargs) @@ -2826,7 +3041,16 @@ def findExistingInstallations(devicetree): return roots
class Root(object): + """ A Root represents an existing OS installation. """ def __init__(self, mounts=None, swaps=None, name=None): + """ + :keyword mounts: mountpoint dict + :type mounts: dict (mountpoint keys and :class:`~.devices.StorageDevice` values) + :keyword swaps: swap device list + :type swaps: list of :class:`~.devices.StorageDevice` + :keyword name: name for this installed OS + :type name: str + """ # mountpoint key, StorageDevice value if not mounts: self.mounts = {} diff --git a/blivet/dasd.py b/blivet/dasd.py index 12cfaa7..24e7703 100644 --- a/blivet/dasd.py +++ b/blivet/dasd.py @@ -213,7 +213,4 @@ class DASD: self._completedCylinders += 1.0 callback_data.set(self._completedCylinders / self.totalCylinders)
-# Create DASD singleton -DASD = DASD() - # vim:tw=78:ts=4:et:sw=4 diff --git a/blivet/devices.py b/blivet/devices.py index c7b0811..d4e5d5b 100644 --- a/blivet/devices.py +++ b/blivet/devices.py @@ -20,79 +20,6 @@ # Red Hat Author(s): Dave Lehman dlehman@redhat.com #
- -""" - Device classes for use by anaconda. - - This is the hierarchy of device objects that anaconda will use for - managing storage devices in the system. These classes will - individually make use of external support modules as needed to - perform operations specific to the type of device they represent. - - TODO: - - see how to do network devices (NetworkManager may help) - - perhaps just a wrapper here - - document return values of all methods/functions - - find out what other kinds of wild and crazy devices we need to - represent here (iseries? xen? more mainframe? mac? ps?) - - PReP - - this is a prime candidate for a PseudoDevice - - DASD - - ZFCP - - XEN - - What specifications do we allow? new existing - partitions - usage + + - filesystem, partition type are implicit - mountpoint + + - size - exact + - - range + - - resize - + - format - + - encryption + + - - disk - exact + - - set + - - how will we specify this? - partition w/ multiple parents cannot otherwise occur - primary + - - - mdraid sets - filesystem (*) + + - mountpoint + + - size? - format - + - encryption + + - - level + ? - device minor + ? - member devices + ? - spares + ? - name? - bitmap? (boolean) + - - - volume groups - name + - - member pvs + + - pesize + ? - - logical volumes - filesystem + + - mountpoint + + - size - exact + ? - format - + - encryption + + - - name + ? - vgname + ? - - -""" - import os import math import copy @@ -138,6 +65,13 @@ device_majors = get_device_majors()
def devicePathToName(devicePath): + """ Return a name based on the given path to a device node. + + :param devicePath: the path to a device node + :type devicePath: str + :returns: the name + :rtype: str + """ if devicePath.startswith("/dev/"): name = devicePath[5:] else: @@ -153,6 +87,13 @@ def devicePathToName(devicePath):
def deviceNameToDiskByPath(deviceName=None): + """ Return a /dev/disk/by-path/ symlink path for the given device name. + + :param deviceName: the device name + :type deviceName: str + :returns: the full path to a /dev/disk/by-path/ symlink, or None + :rtype: str or NoneType + """ if not deviceName: return ""
@@ -208,16 +149,11 @@ class Device(object): _services = []
def __init__(self, name, parents=None): - """ Create a Device instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - parents -- a list of required Device instances - + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword parents: a list of parent devices + :type parents: list of :class:`Device` instances """ self._name = name if parents is None: @@ -276,10 +212,12 @@ class Device(object): return d
def removeChild(self): + """ Decrement the child counter for this device. """ log_method_call(self, name=self.name, kids=self.kids) self.kids -= 1
def addChild(self): + """ Increment the child counter for this device. """ log_method_call(self, name=self.name, kids=self.kids) self.kids += 1
@@ -300,18 +238,36 @@ class Device(object): raise NotImplementedError("destroy method not defined for Device")
def setupParents(self, orig=False): - """ Run setup method of all parent devices. """ + """ Run setup method of all parent devices. + + :keyword orig: set up original format instead of current format + :type orig: bool + """ log_method_call(self, name=self.name, orig=orig, kids=self.kids) for parent in self.parents: parent.setup(orig=orig)
def teardownParents(self, recursive=None): - """ Run teardown method of all parent devices. """ + """ Run teardown method of all parent devices. + + :keyword recursive: tear down all ancestor devices recursively + :type recursive: bool + """ for parent in self.parents: parent.teardown(recursive=recursive)
def dependsOn(self, dep): - """ Return True if this device depends on dep. """ + """ Return True if this device depends on dep. + + This device depends on another device if the other device is an + ancestor of this device. For example, a PartitionDevice depends on + the DiskDevice on which it resides. + + :param dep: the other device + :type dep: :class:`Device` + :returns: whether this device depends on 'dep' + :rtype: bool + """ # XXX does a device depend on itself? if dep in self.parents: return True @@ -327,12 +283,7 @@ class Device(object):
@property def status(self): - """ This device's status. - - For now, this should return a boolean: - True the device is open and ready for use - False the device is not open - """ + """ Is this device currently active and ready for use? """ return False
@property @@ -357,6 +308,7 @@ class Device(object):
@property def ancestors(self): + """ A list of all of this device's ancestors, including itself. """ l = set([self]) for p in [d for d in self.parents if d not in l]: l.update(set(p.ancestors)) @@ -392,6 +344,7 @@ class Device(object):
@property def mediaPresent(self): + """ True if this device contains usable media. """ return True
@@ -399,9 +352,8 @@ class NetworkStorageDevice(object): """ Virtual base class for network backed storage devices """
def __init__(self, host_address=None, nic=None): - """ Create a NetworkStorage Device instance. Note this class is only - to be used as a baseclass and then only with multiple inheritance. - The only correct use is: + """ Note this class is only to be used as a baseclass and then only with + multiple inheritance. The only correct use is: class MyStorageDevice(StorageDevice, NetworkStorageDevice):
The sole purpose of this class is to: @@ -410,10 +362,10 @@ class NetworkStorageDevice(object): 2) To be able to get the host address of the host (server) backing the storage *or* the NIC through which the storage is connected
- Arguments: - - host_address -- host address of the backing server - nic -- nic to which the storage is bound + :keyword host_address: host address of the backing server + :type host_address: str + :keyword nic: NIC to which the block device is bound + :type nic: str """ self.host_address = host_address self.nic = nic @@ -426,10 +378,6 @@ class StorageDevice(Device): path attribute, although it is not guaranteed to be useful, or even present, unless the StorageDevice's setup method has been run. - - StorageDevice instances can optionally contain a filesystem, - represented by an FS instance. A StorageDevice's create method - should create a filesystem if one has been specified. """ _type = "blivet" _devDir = "/dev" @@ -442,25 +390,33 @@ class StorageDevice(Device): size=None, major=None, minor=None, sysfsPath='', parents=None, exists=False, serial=None, vendor="", model="", bus=""): - """ Create a StorageDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - uuid -- universally unique identifier - parents -- a list of required Device instances - serial -- the ID_SERIAL_SHORT for this device - vendor -- the manufacturer of this Device - model -- manufacturer's device model string - bus -- the interconnect this device uses + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword uuid: universally unique identifier (device -- not fs) + :type uuid: str + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword major: the device major + :type major: int + :keyword minor: the device minor + :type minor: int + :keyword serial: the ID_SERIAL_SHORT for this device + :type serial: str + :keyword vendor: the manufacturer of this Device + :type vendor: str + :keyword model: manufacturer's device model string + :type model: str + :keyword bus: the interconnect this device uses + :type bus: str
""" # allow specification of individual parents @@ -1014,7 +970,12 @@ class StorageDevice(Device): data.mountpoint += str(self.id)
class DiskDevice(StorageDevice): - """ A disk """ + """ A local/generic disk. + + This is not the only kind of device that is treated as a disk. More + useful than checking isinstance(device, DiskDevice) is checking + device.isDisk. + """ _type = "disk" _partitionable = True _isDisk = True @@ -1023,26 +984,29 @@ class DiskDevice(StorageDevice): size=None, major=None, minor=None, sysfsPath='', parents=None, serial=None, vendor="", model="", bus="", exists=True): - """ Create a DiskDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - removable -- whether or not this is a removable device - serial -- the ID_SERIAL_SHORT for this device - vendor -- the manufacturer of this Device - model -- manufacturer's device model string - bus -- the interconnect this device uses - + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword uuid: universally unique identifier (device -- not fs) + :type uuid: str + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword removable: whether or not this is a removable device + :type removable: bool + :keyword serial: the ID_SERIAL_SHORT for this device + :type serial: str + :keyword vendor: the manufacturer of this Device + :type vendor: str + :keyword model: manufacturer's device model string + :type model: str + :keyword bus: the interconnect this device uses + :type bus: str
DiskDevices always exist. """ @@ -1109,38 +1073,49 @@ class PartitionDevice(StorageDevice): major=None, minor=None, bootable=None, sysfsPath='', parents=None, exists=False, partType=None, primary=False, weight=0): - """ Create a PartitionDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - exists -- indicates whether this is an existing device - format -- the device's format (DeviceFormat instance) - - For existing partitions: - - parents -- the disk that contains this partition - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - - For new partitions: - - partType -- primary,extended,&c (as parted constant) - grow -- whether or not to grow the partition - maxsize -- max size for growable partitions (in MB) - size -- the device's size (in MB) - start -- start sector (see note, below) - end -- end sector (see note, below) - bootable -- whether the partition is bootable - parents -- a list of potential containing disks - weight -- an initial sorting weight to assign - - NOTE: If start/end sectors are specified they will not be adjusted - for optimal alignment. It is up to the caller to do that. + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + + For existing partitions only: + + :keyword major: the device major + :type major: long + :keyword minor: the device minor + :type minor: long + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + + For non-existent partitions only: + + :keyword partType: parted type constant, eg: + :const:`parted.PARTITION_NORMAL` + :type partType: parted partition type constant + :keyword grow: whether or not to grow the partition + :type grow: bool + :keyword maxsize: max size for growable partitions in MB + :type maxsize: int or float + :keyword start: start sector (see note, below) + :type start: long + :keyword end: end sector (see note, below) + :type end: long + :keyword bootable: whether the partition is bootable + :type bootable: bool + :keyword weight: an initial sorting weight to assign + :type weight: int + + .. note:: + + If a start sector is specified the partition will not be + adjusted for optimal alignment. That is up to the caller. """ self.req_disks = [] self.req_partType = None @@ -1794,21 +1769,23 @@ class DMDevice(StorageDevice):
def __init__(self, name, format=None, size=None, dmUuid=None, target=None, exists=False, parents=None, sysfsPath=''): - """ Create a DMDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - target -- the device-mapper target type (string) - size -- the device's size (units/format TBD) - dmUuid -- the device's device-mapper UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword dmUuid: device-mapper UUID + :type dmUuid: str + :keyword target: device mapper table/target name (eg: "linear") + :type target: str """ StorageDevice.__init__(self, name, format=format, size=size, exists=exists, @@ -1911,20 +1888,21 @@ class DMLinearDevice(DMDevice):
def __init__(self, name, format=None, size=None, dmUuid=None, exists=False, parents=None, sysfsPath=''): - """ Create a DMLinearDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - dmUuid -- the device's device-mapper UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword dmUuid: device-mapper UUID + :type dmUuid: str """ if not parents: raise ValueError("DMLinearDevice requires a backing block device") @@ -1975,19 +1953,19 @@ class DMCryptDevice(DMDevice):
def __init__(self, name, format=None, size=None, uuid=None, exists=False, sysfsPath='', parents=None): - """ Create a DMCryptDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - size -- the device's size (units/format TBD) - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str """ DMDevice.__init__(self, name, format=format, size=size, parents=parents, sysfsPath=sysfsPath, @@ -2000,20 +1978,21 @@ class LUKSDevice(DMCryptDevice):
def __init__(self, name, format=None, size=None, uuid=None, exists=False, sysfsPath='', parents=None): - """ Create a LUKSDevice instance. - - Arguments: - - name -- the device name - - Keyword Arguments: - - size -- the device's size in MB - uuid -- the device's UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - parents -- a list of required Device instances - exists -- indicates whether this is an existing device + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword uuid: the device UUID + :type uuid: str """ DMCryptDevice.__init__(self, name, format=format, size=size, parents=parents, sysfsPath=sysfsPath, @@ -2059,28 +2038,32 @@ class LVMVolumeGroupDevice(DMDevice): def __init__(self, name, parents=None, size=None, free=None, peSize=None, peCount=None, peFree=None, pvCount=None, uuid=None, exists=False, sysfsPath=''): - """ Create a LVMVolumeGroupDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - parents -- a list of physical volumes (StorageDevice) - - Keyword Arguments: - - peSize -- physical extent size (in MB) - exists -- indicates whether this is an existing device - sysfsPath -- sysfs device path - - For existing VG's only: - - size -- the VG's size (in MB) - free -- amount of free space in the VG - peFree -- number of free extents - peCount -- total number of extents - pvCount -- number of PVs in this VG - uuid -- the VG's UUID - + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword peSize: physical extent size + :type peSize: int or float + + For existing VG's only: + + :keyword size: the VG's size + :type size: int or float + :keyword free -- amount of free space in the VG + :type free: int or float + :keyword peFree: number of free extents + :type peFree: int + :keyword peCount -- total number of extents + :type peCount: int + :keyword pvCount: number of PVs in this VG + :type pvCount: int + :keyword uuid: the VG UUID + :type uuid: str """ self.pvClass = get_device_format_class("lvmpv") if not self.pvClass: @@ -2510,31 +2493,43 @@ class LVMLogicalVolumeDevice(DMDevice): format=None, exists=False, sysfsPath='', grow=None, maxsize=None, percent=None, singlePV=False): - """ Create a LVMLogicalVolumeDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - vgdev -- volume group (LVMVolumeGroupDevice instance) - - Keyword Arguments: - - size -- the device's size (in MB) - uuid -- the device's UUID - copies -- number of copies in the vg (>1 for mirrored lvs) - logSize -- size of log volume (for mirrored lvs) - snapshotSpace -- sum of sizes of snapshots of this lv - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - exists -- indicates whether this is an existing device - singlePV -- if true, maps this lv to a single pv - segType -- segment type (eg: "linear", "raid1") - - For new (non-existent) LVs only: - - grow -- whether to grow this LV - maxsize -- maximum size for growable LV (in MB) - percent -- percent of VG space to take + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword uuid: the device UUID + :type uuid: str + + For existing LVs only: + + :keyword copies: number of copies in the vg (>1 for mirrored lvs) + :type copies: int + :keyword logSize: size of log volume (for mirrored lvs) + :type logSize: int or float + :keyword snapshotSpace: sum of sizes of snapshots of this lv + :type snapshotSpace: int or float + :keyword singlePV: if true, maps this lv to a single pv + :type singlePV: bool + :keyword segType: segment type (eg: "linear", "raid1") + :type segType: str + + For non-existent LVs only: + + :keyword grow: whether to grow this LV + :type grow: bool + :keyword maxsize: maximum size for growable LV + :type maxsize: int or float + :keyword percent -- percent of VG space to take + :type percent: int
""" if self.__class__.__name__ == "LVMLogicalVolumeDevice": @@ -2802,29 +2797,36 @@ class LVMThinPoolDevice(LVMLogicalVolumeDevice): format=None, exists=False, sysfsPath='', grow=None, maxsize=None, percent=None, metadatasize=None, chunksize=None, segType=None): - """ Create a LVMLogicalVolumeDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - parents -- vg (LVMVolumeGroupDevice instance) - - Keyword Arguments: - - size -- the device's size (in MB) - uuid -- the device's UUID - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - exists -- indicates whether this is an existing device - - For new (non-existent) LVs only: - - grow -- whether to grow this LV - maxsize -- maximum size for growable LV (in MB) - percent -- percent of VG space to take - metadatasize -- the size of the metadata LV (in MB) - chunksize -- chunk size for the pool (in MB) - + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword uuid: the device UUID + :type uuid: str + :keyword segType: segment type + :type segType: str + + For non-existent pools only: + + :keyword grow: whether to grow this LV + :type grow: bool + :keyword maxsize: maximum size for growable LV + :type maxsize: int or float + :keyword percent: percent of VG space to take + :type percent: int + :keyword metadatasize: the size of the metadata LV + :type metadatasize: int or float + :keyword chunksize: chunk size for the pool + :type chunksize: int or float """ if metadatasize is not None and \ not lvm.is_valid_thin_pool_metadata_size(metadatasize): @@ -2955,23 +2957,28 @@ class MDRaidArrayDevice(StorageDevice): memberDevices=None, totalDevices=None, uuid=None, format=None, exists=False, metadataVersion=None, parents=None, sysfsPath=''): - """ Create a MDRaidArrayDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - level -- the device's RAID level (a string, eg: '1' or 'raid1') - metadataVersion -- the version of the device's md metadata - parents -- list of member devices (StorageDevice instances) - size -- the device's size (units/format TBD) - uuid -- the device's UUID - minor -- the device minor - sysfsPath -- sysfs device path - format -- a DeviceFormat instance - exists -- indicates whether this is an existing device + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword uuid: the device UUID + :type uuid: str + + :keyword level: the device's RAID level + :type level: str (eg: "raid1") + :keyword metadataVersion: the version of the device's md metadata + :type metadataVersion: str (eg: "0.90") + :keyword minor: the device minor (obsolete?) + :type minor: int """ StorageDevice.__init__(self, name, format=format, exists=exists, major=major, minor=minor, size=size, @@ -3488,19 +3495,22 @@ class DMRaidArrayDevice(DMDevice):
def __init__(self, name, raidSet=None, format=None, size=None, parents=None, sysfsPath=''): - """ Create a DMRaidArrayDevice instance. - - Arguments: - - name -- the dmraid name also the device node's basename - - Keyword Arguments: - - raidSet -- the RaidSet object from block - parents -- a list of the member devices - sysfsPath -- sysfs device path - size -- the device's size - format -- a DeviceFormat instance + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword raidSet: the RaidSet object from block + :type raidSet: :class:`block.RaidSet` + + DMRaidArrayDevices always exist. Blivet cannot create or destroy + them. """ if isinstance(parents, list): for parent in parents: @@ -3598,19 +3608,22 @@ class MultipathDevice(DMDevice):
def __init__(self, name, format=None, size=None, serial=None, parents=None, sysfsPath=''): - """ Create a MultipathDevice instance. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - sysfsPath -- sysfs device path - size -- the device's size - format -- a DeviceFormat instance - parents -- a list of the backing devices (Device instances) - serial -- the device's serial number + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword sysfsPath: sysfs device path + :type sysfsPath: str + :keyword serial: the device's serial number + :type serial: str + + MultipathDevices always exist. Blivet cannot create or destroy + them. """
DMDevice.__init__(self, name, format=format, size=size, @@ -3703,13 +3716,9 @@ class NoDevice(StorageDevice): _type = "nodev"
def __init__(self, format=None): - """ Create a NoDevice instance. - - Arguments: - - Keyword Arguments: - - format -- a DeviceFormat instance + """ + :keyword format: the device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it """ if format: name = format.device @@ -3783,18 +3792,17 @@ class FileDevice(StorageDevice):
def __init__(self, path, format=None, size=None, exists=False, parents=None): - """ Create a FileDevice instance. - - Arguments: - - path -- full path to the file - - Keyword Arguments: - - format -- a DeviceFormat instance - size -- the file size (units TBD) - parents -- a list of required devices (Device instances) - exists -- indicates whether this is an existing device + """ + :param path: full path to the file + :type path: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it """ if not path.startswith("/"): raise ValueError("FileDevice requires an absolute path") @@ -3887,19 +3895,17 @@ class LoopDevice(StorageDevice):
def __init__(self, name=None, format=None, size=None, sysfsPath=None, exists=False, parents=None): - """ Create a LoopDevice instance. - - Arguments: - - name -- the device's name - - Keyword Arguments: - - format -- a DeviceFormat instance - size -- the device's size in MB - parents -- a list of required devices (Device instances) - exists -- indicates whether this is an existing device - + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it
Loop devices always exist. """ @@ -3972,6 +3978,29 @@ class iScsiDiskDevice(DiskDevice, NetworkStorageDevice): _packages = ["iscsi-initiator-utils", "dracut-network"]
def __init__(self, device, **kwargs): + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword node: ??? + :type node: str + :keyword ibft: use iBFT + :type ibft: bool + :keyword nic: name of NIC to use + :type nic: str + :keyword initiator: initiator name + :type initiator: str + :keyword fw_name: qla4xxx partial offload + :keyword fw_address: qla4xxx partial offload + :keyword fw_port: qla4xxx partial offload + """ self.node = kwargs.pop("node") self.ibft = kwargs.pop("ibft") self.nic = kwargs.pop("nic") @@ -4037,6 +4066,20 @@ class FcoeDiskDevice(DiskDevice, NetworkStorageDevice): _packages = ["fcoe-utils", "dracut-network"]
def __init__(self, device, **kwargs): + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword nic: name of NIC to use + :keyword identifier: ??? + """ self.nic = kwargs.pop("nic") self.identifier = kwargs.pop("identifier") DiskDevice.__init__(self, device, **kwargs) @@ -4116,6 +4159,21 @@ class ZFCPDiskDevice(DiskDevice): _type = "zfcp"
def __init__(self, device, **kwargs): + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword hba_id: ??? + :keyword wwpn: ??? + :keyword fcp_lun: ??? + """ self.hba_id = kwargs.pop("hba_id") self.wwpn = kwargs.pop("wwpn") self.fcp_lun = kwargs.pop("fcp_lun") @@ -4144,6 +4202,21 @@ class DASDDevice(DiskDevice): _type = "dasd"
def __init__(self, device, **kwargs): + """ + :param name: the device name (generally a device node's basename) + :type name: str + :keyword exists: does this device exist? + :type exists: bool + :keyword size: the device's size in MB + :type size: int or float + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + :keyword busid: bus ID + :keyword opts: options + :type opts: dict with option name keys and option value values + """ self.busid = kwargs.pop('busid') self.opts = kwargs.pop('opts') DiskDevice.__init__(self, device, **kwargs) @@ -4209,6 +4282,14 @@ class NFSDevice(StorageDevice, NetworkStorageDevice): _packages = ["dracut-network"]
def __init__(self, device, format=None, parents=None): + """ + :param device: the device name (generally a device node's basename) + :type device: str + :keyword parents: a list of parent devices + :type parents: list of :class:`StorageDevice` + :keyword format: this device's formatting + :type format: :class:`~.formats.DeviceFormat` or a subclass of it + """ # we could make host/ip, path, &c but will anything use it? StorageDevice.__init__(self, device, format=format, parents=parents) NetworkStorageDevice.__init__(self, device.split(":")[0]) diff --git a/blivet/devicetree.py b/blivet/devicetree.py index e72ce7c..80f52b6 100644 --- a/blivet/devicetree.py +++ b/blivet/devicetree.py @@ -57,29 +57,43 @@ log = logging.getLogger("blivet") class DeviceTree(object): """ A quasi-tree that represents the devices in the system.
- The tree contains a list of device instances, which does not - necessarily reflect the actual state of the system's devices. - DeviceActions are used to perform modifications to the tree, - except when initially populating the tree. + The tree contains a list of :class:`~.devices.StorageDevice` instances, + which does not necessarily reflect the actual state of the system's + devices. :class:`~.deviceaction.DeviceAction` is used to perform + modifications to the tree, except when initially populating the tree.
- DeviceAction instances are registered, possibly causing the - addition or removal of Device instances to/from the tree. The - DeviceActions are all reversible up to the time their execute - method has been called. + :class:`~.deviceaction.DeviceAction` instances are registered, possibly + causing the addition or removal of :class:`~.devices.StorageDevice` + instances to/from the tree. A :class:`~.deviceaction.DeviceAction` + is reversible up to the time its 'execute' method is called.
Only one action of any given type/object pair should exist for any given device at any given time.
- DeviceAction instances can only be registered for leaf devices, - except for resize actions. + :class:`~.deviceaction.DeviceAction` instances can only be registered + for leaf devices, except for resize actions. """
def __init__(self, conf=None, passphrase=None, luksDict=None, iscsi=None, dasd=None): + """ + + :keyword conf: storage discovery configuration + :type conf: :class:`~.StorageDiscoveryConfig` + :keyword passphrase: default LUKS passphrase + :keyword luksDict: a dict with UUID keys and passphrase values + :type luksDict: dict + :keyword iscsi: ISCSI control object + :type iscsi: :class:`~.iscsi.iscsi` + :keyword dasd: DASD control object + :type dasd: :class:`~.dasd.DASD` + + """ self.reset(conf, passphrase, luksDict, iscsi, dasd)
def reset(self, conf=None, passphrase=None, luksDict=None, iscsi=None, dasd=None): + """ Reset the instance to its initial state. """ # internal data members self._devices = [] self._actions = [] @@ -127,7 +141,17 @@ class DeviceTree(object): self._cleanup = False
def setDiskImages(self, images): - """ Set the disk images and reflect them in exclusiveDisks. """ + """ Set the disk images and reflect them in exclusiveDisks. + + :param images: dict with image name keys and filename values + :type images: dict + + .. note:: + + Disk images are automatically exclusive. That means that, in the + presence of disk images, any local storage not associated with + the disk images is ignored. + """ self.diskImages = images # disk image files are automatically exclusive self.exclusiveDisks = self.diskImages.keys() @@ -261,6 +285,9 @@ class DeviceTree(object): def _addDevice(self, newdev): """ Add a device to the tree.
+ :param newdev: the device to add + :type newdev: a subclass of :class:`~.devices.StorageDevice` + Raise ValueError if the device's identifier is already in the list. """ @@ -288,7 +315,16 @@ class DeviceTree(object): def _removeDevice(self, dev, force=None, moddisk=True): """ Remove a device from the tree.
- Only leaves may be removed. + :param dev: the device to remove + :type dev: a subclass of :class:`~.devices.StorageDevice` + :keyword force: whether to force removal of a non-leaf device + :type force: bool + :keyword moddisk: update parent disk's format (partitions only) + :type moddisk: bool + + .. note:: + + Only leaves may be removed. """ if dev not in self._devices: raise ValueError("Device '%s' not in tree" % dev.name) @@ -338,6 +374,9 @@ class DeviceTree(object): def registerAction(self, action): """ Register an action to be performed at a later time.
+ :param action: the action + :type action: :class:`~.deviceaction.DeviceAction` + Modifications to the Device instance are handled before we get here. """ @@ -363,6 +402,9 @@ class DeviceTree(object): def cancelAction(self, action): """ Cancel a registered action.
+ :param action: the action + :type action: :class:`~.deviceaction.DeviceAction` + This will unregister the action and do any required modifications to the device list.
@@ -383,12 +425,21 @@ class DeviceTree(object): devid=None): """ Find all actions that match all specified parameters.
- Keyword arguments: - - device -- device to match (Device, or None to match any) - type -- action type to match (string, or None to match any) - object -- operand type to match (string, or None to match any) - path -- device path to match (string, or None to match any) + A value of None for any of the keyword arguments indicates that any + value is acceptable for that field. + + :keyword device: device to match + :type device: :class:`~.devices.StorageDevice` or None + :keyword type: action type to match (eg: "create", "destroy") + :type type: str or None + :keyword object: operand type to match (eg: "device" or "format") + :type object: str or None + :keyword path: device path to match + :type path: str or None + :keyword devid: device id to match + :type devid: int or None + :returns: a list of matching actions + :rtype: list of :class:`~.deviceaction.DeviceAction`
""" if device is None and type is None and object is None and \ @@ -424,6 +475,9 @@ class DeviceTree(object): """ Return a list of devices that depend on dep.
The list includes both direct and indirect dependents. + + :param dep: the device whose dependents we are looking for + :type dep: :class:`~.devices.StorageDevice` """ dependents = []
@@ -442,14 +496,10 @@ class DeviceTree(object): def isIgnored(self, info): """ Return True if info is a device we should ignore.
- Arguments: - - info -- a dict representing a udev db entry - - TODO: - - - filtering of SAN/FC devices - - filtering by driver? + :param info: udevdb device entry + :type info: dict + :returns: whether the device will be ignored + :rtype: bool
""" sysfs_path = udev_device_get_sysfs_path(info) @@ -513,12 +563,20 @@ class DeviceTree(object): # FIXME: check for virtual devices whose slaves are on the ignore list
def udevDeviceIsDisk(self, info): - # We want exclusiveDisks to operate on anything that could be - # considered a directly usable disk, ie: fwraid array, mpath, or disk. - # - # Unfortunately, since so many things are represented as disks by - # udev/sysfs, we have to define what is a disk in terms of what is - # not a disk. + """ Return True if the udev device looks like a disk. + + :param info: udevdb device entry + :type info: dict + :returns: whether the device is a disk + :rtype: bool + + We want exclusiveDisks to operate on anything that could be + considered a directly usable disk, ie: fwraid array, mpath, or disk. + + Unfortunately, since so many things are represented as disks by + udev/sysfs, we have to define what is a disk in terms of what is + not a disk. + """ return (udev_device_is_disk(info) and not (udev_device_is_cdrom(info) or udev_device_is_partition(info) or @@ -1764,6 +1822,20 @@ class DeviceTree(object): devicelibs.lvm.lvm_cc_addFilterRejectRegexp(pv.name)
def hide(self, device): + """ Hide the specified device. + + :param device: the device to hide + :type device: :class:`~.devices.StorageDevice` + + If the device is not a leaf device, all devices that depend on it + will be hidden leaves-first until the device is a leaf device. + + .. note:: + + Hiding a device will cancel any actions scheduled on that device + or any device that depends on it. + + """ if device in self._hidden: return
@@ -1805,6 +1877,18 @@ class DeviceTree(object): self.dasd.removeDASD(device)
def unhide(self, device): + """ Restore a device's visibility. + + :param device: the device to restore/unhide + :type device: :class:`~.devices.StorageDevice` + + .. note:: + + Actions canceled while hiding the device are not rescheduled + automatically. + + """ + # the hidden list should be in leaves-first order for hidden in reversed(self._hidden): if hidden == device or hidden.dependsOn(device): @@ -1900,7 +1984,15 @@ class DeviceTree(object): self.backupConfigs(restore=True)
def populate(self, cleanupOnly=False): - """ Locate all storage devices. """ + """ Locate all storage devices. + + Everything should already be active. We just go through and gather + details as needed and set up the relations between various devices. + + Devices excluded via disk filtering (or because of disk images) are + scanned just the rest, but then they are hidden at the end of this + process. + """ self.backupConfigs() if cleanupOnly: self._cleanup = True @@ -2017,6 +2109,15 @@ class DeviceTree(object): log.error("setup of %s failed: %s" % (device.name, msg))
def getDeviceBySysfsPath(self, path, incomplete=False, hidden=False): + """ Return a list of devices with a matching sysfs path. + + :param path: the sysfs path to match + :type path: str + :keyword incomplete: include incomplete devices in results + :type incomplete: bool + :keyword hidden: include hidden devices in results + :type hidden: bool + """ if not path: return None
@@ -2037,6 +2138,15 @@ class DeviceTree(object): return found
def getDeviceByUuid(self, uuid, incomplete=False, hidden=False): + """ Return a list of devices with a matching UUID. + + :param uuid: the UUID to match + :type uuid: str + :keyword incomplete: include incomplete devices in results + :type incomplete: bool + :keyword hidden: include hidden devices in results + :type hidden: bool + """ if not uuid: return None
@@ -2060,6 +2170,15 @@ class DeviceTree(object): return found
def getDevicesBySerial(self, serial, incomplete=False, hidden=False): + """ Return a list of devices with a matching serial. + + :param serial: the serial to match + :type serial: str + :keyword incomplete: include incomplete devices in results + :type incomplete: bool + :keyword hidden: include hidden devices in results + :type hidden: bool + """ devices = self._devices[:] if hidden: devices += self._hidden @@ -2079,6 +2198,15 @@ class DeviceTree(object): return retval
def getDeviceByLabel(self, label, incomplete=False, hidden=False): + """ Return a device with a matching filesystem label. + + :param label: the filesystem label to match + :type label: str + :keyword incomplete: search incomplete devices + :type incomplete: bool + :keyword hidden: search hidden devices + :type hidden: bool + """ if not label: return None
@@ -2103,6 +2231,15 @@ class DeviceTree(object): return found
def getDeviceByName(self, name, incomplete=False, hidden=False): + """ Return a device with a matching name. + + :param name: the name to look for + :type name: str + :keyword incomplete: search incomplete devices + :type incomplete: bool + :keyword hidden: search hidden devices + :type hidden: bool + """ log_method_call(self, name=name) if not name: log_method_return(self, None) @@ -2129,6 +2266,15 @@ class DeviceTree(object): return found
def getDeviceByPath(self, path, preferLeaves=True, incomplete=False, hidden=False): + """ Return a device with a matching path. + + :param path: the path to match + :type path: str + :keyword incomplete: include incomplete devices in results + :type incomplete: bool + :keyword hidden: include hidden devices in results + :type hidden: bool + """ log_method_call(self, path=path) if not path: log_method_return(self, None) @@ -2166,20 +2312,35 @@ class DeviceTree(object): return found
def getDevicesByType(self, device_type): + """ Return a list of devices with a matching device type. + + :param device_type: the type to match + :type device_type: str + """ # TODO: expand this to catch device format types return [d for d in self._devices if d.type == device_type]
def getDevicesByInstance(self, device_class): + """ Return a list of devices with a matching device class. + + :param path: the device class to match + :type path: class + """ return [d for d in self._devices if isinstance(d, device_class)]
def getDeviceByID(self, id_num): + """ Return a device with specified device id. + + :param id_num: the id to look for + :type id_num: int + """ for device in self._devices: if device.id == id_num: return device
@property def devices(self): - """ List of device instances """ + """ List of devices currently in the tree """ devices = [] for device in self._devices: if not getattr(device, "complete", True): @@ -2206,7 +2367,7 @@ class DeviceTree(object):
@property def uuids(self): - """ Dict with uuid keys and Device values. """ + """ Dict with uuid keys and :class:`~.devices.Device` values. """ uuids = {} for dev in self._devices: try: @@ -2253,6 +2414,23 @@ class DeviceTree(object): return [c for c in self._devices if device in c.parents]
def resolveDevice(self, devspec, blkidTab=None, cryptTab=None, options=None): + """ Return the device matching the provided device specification. + + The spec can be anything from a device name (eg: 'sda3') to a device + node path (eg: '/dev/mapper/fedora-root' or '/dev/dm-2') to + something like 'UUID=xyz-tuv-qrs' or 'LABEL=rootfs'. + + :param devspec: a string describing a block device + :type devspec: str + :keyword blkidTab: blkid info + :type blkidTab: :class:`~.BlkidTab` + :keyword cryptTab: crypto info + :type cryptTab: :class:`~.CryptTab` + :keyword options: mount options + :type options: str + :returns: the device + :rtype: :class:`~.devices.StorageDevice` or None + """ # find device in the tree device = None if devspec.startswith("UUID="): diff --git a/blivet/formats/__init__.py b/blivet/formats/__init__.py index a7416f1..5e0ea32 100644 --- a/blivet/formats/__init__.py +++ b/blivet/formats/__init__.py @@ -60,26 +60,19 @@ def get_default_filesystem_type(): raise DeviceFormatError("None of %s is supported by your kernel" % ",".join(default_fstypes))
def getFormat(fmt_type, *args, **kwargs): - """ Return a DeviceFormat instance based on fmt_type and args. - - Given a device format type and a set of constructor arguments, - return a DeviceFormat instance. - - Return None if no suitable format class is found. - - Arguments: - - fmt_type -- the name of the format type (eg: 'ext3', 'swap') - - Keyword Arguments: - - The keyword arguments may vary according to the format type, - but here is the common set: - - device -- path to the device on which the format resides - uuid -- the UUID of the (preexisting) formatted device - exists -- whether or not the format exists on the device - + """ Return an instance of the appropriate DeviceFormat class. + + :param fmt_type: The name of the formatting type + :type fmt_type: str. + :return: the format instance + :rtype: :class:`DeviceFormat` + :raises: ValueError + + .. note:: + + Any additional arguments will be passed on to the constructor for + the format class. See the various :class:`DeviceFormat` subclasses + for an exhaustive list of the arguments that can be passed. """ fmt_class = get_device_format_class(fmt_type) fmt = None @@ -103,8 +96,10 @@ def getFormat(fmt_type, *args, **kwargs): def collect_device_format_classes(): """ Pick up all device format classes from this directory.
- Note: Modules must call register_device_format(FormatClass) in - order for the format class to be picked up. + .. note:: + + Modules must call :func:`register_device_format` to make format + classes available to :func:`getFormat`. """ dir = os.path.dirname(__file__) for module_file in os.listdir(dir): @@ -120,7 +115,13 @@ def collect_device_format_classes(): log.debug(format_exc())
def get_device_format_class(fmt_type): - """ Return an appropriate format class based on fmt_type. """ + """ Return an appropriate format class. + + :param fmt_type: The name of the format type. + :type fmt_type: str. + :returns: The chosen DeviceFormat class + :rtype: class. + """ if not device_formats: collect_device_format_classes()
@@ -141,7 +142,12 @@ def get_device_format_class(fmt_type): return fmt
class DeviceFormat(object): - """ Generic device format. """ + """ Generic device format. + + This represents the absence of recognized formatting. That could mean a + device is uninitialized, has had zeros written to it, or contains some + valid formatting that this module does not support. + """ _type = None _name = N_("Unknown") _udevTypes = [] @@ -161,14 +167,21 @@ class DeviceFormat(object): _ksMountpoint = None
def __init__(self, *args, **kwargs): - """ Create a DeviceFormat instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this format's UUID - exists -- indicates whether this is an existing format - + """ + :keyword device: The path to the device node. + :type device: str + :keyword uuid: the formatting's UUID. + :type uuid: str + :keyword exists: Whether the formatting exists. (default: False) + :raises: ValueError + + .. note:: + + The 'device' kwarg is required for existing formats. For non- + existent formats, it is only necessary that the :attr:`device` + attribute be set before the :meth:`create` method runs. Note + that you can specify the device at the last moment by specifying + it via the 'device' kwarg to the :meth:`create` method. """ self.device = kwargs.get("device") self.uuid = kwargs.get("uuid") @@ -298,6 +311,18 @@ class DeviceFormat(object): return self._majorminor
def create(self, *args, **kwargs): + """ Write the formatting to the specified block device. + + :keyword device: path to device node + :type device: str. + :raises: FormatCreateError + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) # allow late specification of device path @@ -309,6 +334,11 @@ class DeviceFormat(object): raise FormatCreateError("invalid device specification", self.device)
def destroy(self, *args, **kwargs): + """ Remove the formatting from the associated block device. + + :raises: FormatDestroyError + :returns: None. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) try: @@ -327,6 +357,18 @@ class DeviceFormat(object): self.exists = False
def setup(self, *args, **kwargs): + """ Activate the formatting. + + :keyword device: device node path + :type device: str. + :raises: FormatSetupError. + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, type=self.type, status=self.status)
@@ -345,6 +387,7 @@ class DeviceFormat(object): raise FormatSetupError("invalid device specification")
def teardown(self, *args, **kwargs): + """ Deactivate the formatting. """ log_method_call(self, device=self.device, type=self.type, status=self.status)
diff --git a/blivet/formats/biosboot.py b/blivet/formats/biosboot.py index 73b980e..5ed0305 100644 --- a/blivet/formats/biosboot.py +++ b/blivet/formats/biosboot.py @@ -39,13 +39,15 @@ class BIOSBoot(DeviceFormat): _minSize = 0.5 # minimum size in MB
def __init__(self, *args, **kwargs): - """ Create a BIOSBoot instance. - - Keyword Arguments: + """ + :keyword device: path to the block device node + :type device: str + :keyword exists: whether the formatting exists + :type exists: bool
- device -- path to the underlying device - exists -- indicates whether this is an existing format + .. note::
+ The 'device' kwarg is required for existing formats. """ DeviceFormat.__init__(self, *args, **kwargs)
diff --git a/blivet/formats/disklabel.py b/blivet/formats/disklabel.py index bfb707a..323b36b 100644 --- a/blivet/formats/disklabel.py +++ b/blivet/formats/disklabel.py @@ -45,18 +45,20 @@ class DiskLabel(DeviceFormat): _supported = False # is supported
def __init__(self, *args, **kwargs): - """ Create a DiskLabel instance. - - Keyword Arguments: - - labelType -- type of disklabel to create - device -- path to the underlying device - exists -- indicates whether this is an existing format - + """ + :keyword device: full path to the block device node (required) + :type device: str + :keyword labelType: type of disklabel to create + :type labelType: str + :keyword exists: whether the formatting exists + :type exists: bool """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs)
+ if not self.device: + raise ValueError("DiskLabel requires a valid device path") + if not self.exists: self._labelType = kwargs.get("labelType", "msdos") else: @@ -231,25 +233,6 @@ class DiskLabel(DeviceFormat): """ Device status. """ return False
- def setup(self, *args, **kwargs): - """ Open, or set up, a device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise DeviceFormatError("format has not been created") - - if self.status: - return - - DeviceFormat.setup(self, *args, **kwargs) - - def teardown(self, *args, **kwargs): - """ Close, or tear down, a device. """ - log_method_call(self, device=self.device, - type=self.type, status=self.status) - if not self.exists: - raise DeviceFormatError("format has not been created") - def create(self, *args, **kwargs): """ Create the device. """ log_method_call(self, device=self.device, @@ -305,15 +288,23 @@ class DiskLabel(DeviceFormat): else: self.updateOrigPartedDisk()
- def addPartition(self, *args, **kwargs): - partition = kwargs.get("partition", None) - if not partition: - partition = args[0] + def addPartition(self, partition, constraint=None): + """ Add a partition to the disklabel. + + :param partition: partition from which to get geom, type (required) + :type partition: parted.Partition. + :keyword constraint: constraint to use + :type constraint: parted.Constraint. + + .. note:: + + This does not add the :class:`parted.Partition` instance you + pass in. The geometry and type are taken from that instance and + used to create a new :class:`parted.Partition` to add to the + disklabel. + """ geometry = partition.geometry - constraint = kwargs.get("constraint", None) - if not constraint and len(args) > 1: - constraint = args[1] - elif not constraint: + if not constraint: constraint = parted.Constraint(exactGeom=geometry)
new_partition = parted.Partition(disk=self.partedDisk, @@ -323,6 +314,17 @@ class DiskLabel(DeviceFormat): constraint=constraint)
def removePartition(self, partition): + """ Remove a partition from the disklabel. + + :param partition: the partition to remove + :type partition: parted.Partition. + + .. note:: + + Unlike :meth:`addPartition`, the actual partition instance + passed is what will be removed from the disklabel. + + """ self.partedDisk.removePartition(partition)
@property diff --git a/blivet/formats/dmraid.py b/blivet/formats/dmraid.py index b42c90a..875a4ce 100644 --- a/blivet/formats/dmraid.py +++ b/blivet/formats/dmraid.py @@ -58,15 +58,13 @@ class DMRaidMember(DeviceFormat): _hidden = True # hide devices with this formatting?
def __init__(self, *args, **kwargs): - """ Create a DeviceFormat instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this format's UUID - exists -- indicates whether this is an existing format - - On initialization this format is like DeviceFormat + """ + :keyword device: path to the underlying device (required) + :type device: str + :keyword uuid: this format's UUID + :type uuid: str + :keyword exists: whether this is an existing format + :type exists: bool
""" log_method_call(self, *args, **kwargs) diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py index 5a05569..cf54c31 100644 --- a/blivet/formats/fs.py +++ b/blivet/formats/fs.py @@ -109,7 +109,7 @@ def fsConfigFromFile(config_file): fs_configs[fs_attrs['type']] = fs_attrs
class FS(DeviceFormat): - """ Filesystem class. """ + """ Filesystem base class. """ _type = "Abstract Filesystem Class" # fs type name _mountType = None # like _type but for passing to mount _name = None @@ -129,18 +129,25 @@ class FS(DeviceFormat): _fsProfileSpecifier = None # mkfs option specifying fsprofile
def __init__(self, *args, **kwargs): - """ Create a FS instance. - - Keyword Args: - - device -- path to the device containing the filesystem - mountpoint -- the filesystem's mountpoint - label -- the filesystem label - uuid -- the filesystem UUID - mountopts -- mount options for the filesystem - size -- the filesystem's size in MiB - exists -- indicates whether this is an existing filesystem + """ + :keyword device: path to the block device node (required for + existing filesystems) + :keyword mountpoint: the filesystem's planned mountpoint + :keyword label: the filesystem label + :keyword uuid: the filesystem UUID + :keyword mountopts: mount options for the filesystem + :type mountopts: str + :keyword size: the filesystem's size in MiB + :keyword exists: indicates whether this is an existing filesystem + :type exists: bool
+ .. note:: + + The 'device' kwarg is required for existing formats. For non- + existent formats, it is only necessary that the :attr:`device` + attribute be set before the :meth:`create` method runs. Note + that you can specify the device at the last moment by specifying + it via the 'device' kwarg to the :meth:`create` method. """ if self.__class__ is FS: raise TypeError("FS is an abstract class.") @@ -247,9 +254,10 @@ class FS(DeviceFormat): return buf
def _getExistingSize(self, info=None): - """ Determine the size of this filesystem. Filesystem must - exist. Each filesystem varies, but the general procedure - is to run the filesystem dump or info utility and read + """ Determine the size of this filesystem. + + Filesystem must exist. Each filesystem varies, but the general + procedure is to run the filesystem dump or info utility and read the block size and number of blocks for the filesystem and compute megabytes from that.
@@ -278,6 +286,11 @@ class FS(DeviceFormat): are looking for, the command to run and arguments, or something else. If you catch an exception from this method, assume the filesystem cannot be resized. + + :keyword info: filesystem info buffer + :type info: str (output of :attr:`infofsProg`) + :returns: size of existing fs in MiB. + :rtype: float. """ size = self._size
@@ -356,14 +369,9 @@ class FS(DeviceFormat): def doFormat(self, *args, **kwargs): """ Create the filesystem.
- Arguments: - - None - - Keyword Arguments: - - options -- list of options to pass to mkfs - + :keyword options: options to pass to mkfs + :type options: list of strings + :raises: FormatCreateError, FSError """ log_method_call(self, type=self.mountType, device=self.device, mountpoint=self.mountpoint) @@ -407,13 +415,10 @@ class FS(DeviceFormat): return argv
def doResize(self, *args, **kwargs): - """ Resize this filesystem to new size @newsize. + """ Resize this filesystem based on this instance's targetSize attr.
- Arguments: - - None + :raises: FSResizeError, FSError """ - if not self.exists: raise FSResizeError("filesystem does not exist", self.device)
@@ -468,6 +473,10 @@ class FS(DeviceFormat): return _("Unknown return code: %d.") % (rc,)
def doCheck(self): + """ Run a filesystem check. + + :raises: FSError + """ if not self.exists: raise FSError("filesystem has not been created")
@@ -509,7 +518,7 @@ class FS(DeviceFormat): self._supported = False return
- # If we successfully loaded a kernel module, for this filesystem, we + # If we successfully loaded a kernel module for this filesystem, we # also need to update the list of supported filesystems. update_kernel_filesystems()
@@ -518,7 +527,7 @@ class FS(DeviceFormat): ret = False
if self.status: - raise RuntimeError("filesystem is already mounted") + return True
# create a temp dir prefix = "%s.%s" % (os.path.basename(self.device), self.type) @@ -540,15 +549,11 @@ class FS(DeviceFormat): def mount(self, *args, **kwargs): """ Mount this filesystem.
- Arguments: - - None - - Keyword Arguments: - - options -- mount options (overrides all other option strings) - chroot -- prefix to apply to mountpoint - mountpoint -- mountpoint (overrides self.mountpoint) + :keyword options: mount options (overrides all other option strings) + :type options: str. + :keyword chroot: prefix to apply to mountpoint + :keyword mountpoint: mountpoint (overrides self.mountpoint) + :raises: FSError """ options = kwargs.get("options", "") chroot = kwargs.get("chroot", "/") @@ -634,7 +639,11 @@ class FS(DeviceFormat): return argv
def writeLabel(self, label): - """ Create a label for this filesystem. """ + """ Create a label for this filesystem. + + :param label: the label text + :raises: FSError + """ if not self.exists: raise FSError("filesystem has not been created")
@@ -773,6 +782,18 @@ class FS(DeviceFormat): # generically named methods so filesystems and formatted devices # like swap and LVM physical volumes can have a common API. def create(self, *args, **kwargs): + """ Create the filesystem on the specified block device. + + :keyword device: path to device node + :type device: str. + :raises: FormatCreateError, FSError + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ if self.exists: raise FSError("filesystem already exists")
@@ -784,7 +805,19 @@ class FS(DeviceFormat): """ Mount the filesystem.
The filesystem will be mounted at the directory indicated by - self.mountpoint. + self.mountpoint unless overridden via the 'mountpoint' kwarg. + + :keyword device: device node path + :type device: str. + :keyword mountpoint: mountpoint (overrides self.mountpoint) + :type mountpoint: str. + :raises: FormatSetupError. + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. """ return self.mount(**kwargs)
@@ -880,7 +913,12 @@ class Ext2FS(FS): raise FSError("failed to set UUID for %s: %s" % (self.device, err))
def _getMinSize(self, info=None): - """ Minimum size for this filesystem in MB. """ + """ Set the minimum size for this filesystem in MiB. + + :keyword info: filesystem info buffer + :type info: str (output of :attr:`infofsProg`) + :rtype: None. + """ size = self._minSize blockSize = None
@@ -1050,6 +1088,23 @@ class BTRFS(FS): pass
def setup(self, *args, **kwargs): + """ Mount the filesystem. + + The filesystem will be mounted at the directory indicated by + self.mountpoint unless overridden via the 'mountpoint' kwarg. + + :keyword device: device node path + :type device: str. + :keyword mountpoint: mountpoint (overrides self.mountpoint) + :type mountpoint: str. + :raises: FormatSetupError. + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, type=self.mountType, device=self.device, mountpoint=self.mountpoint) if not self.mountpoint and "mountpoint" not in kwargs: @@ -1306,7 +1361,12 @@ class NTFS(FS): return False
def _getMinSize(self, info=None): - # try to determine the minimum size. + """ Set the minimum size for this filesystem in MiB. + + :keyword info: filesystem info buffer + :type info: str (output of :attr:`infofsProg`) + :rtype: None + """ size = self._minSize if self.exists and os.path.exists(self.device) and \ util.find_program_in_path(self.resizefsProg): diff --git a/blivet/formats/luks.py b/blivet/formats/luks.py index fac1bf8..685725f 100644 --- a/blivet/formats/luks.py +++ b/blivet/formats/luks.py @@ -41,7 +41,7 @@ log = logging.getLogger("blivet")
class LUKS(DeviceFormat): - """ A LUKS device. """ + """ LUKS """ _type = "luks" _name = N_("LUKS") _lockedName = N_("Encrypted") @@ -52,20 +52,32 @@ class LUKS(DeviceFormat): _packages = ["cryptsetup"] # required packages
def __init__(self, *args, **kwargs): - """ Create a LUKS instance. - - Keyword Arguments: - - device -- the path to the underlying device - name -- the name of the mapped device - uuid -- this device's UUID - passphrase -- device passphrase (string) - key_file -- path to a file containing a key (string) - cipher -- cipher mode string - key_size -- key size in bits - exists -- indicates whether this is an existing format - escrow_cert -- certificate to use for key escrow - add_backup_passphrase -- generate a backup passphrase? + """ + :keyword device: the path to the underlying device + :keyword uuid: the LUKS UUID + :keyword exists: indicates whether this is an existing format + :type exists: bool + :keyword name: the name of the mapped device + :keyword passphrase: device passphrase + :type passphrase: str + :keyword key_file: path to a file containing a key + :type key_file: str + :keyword cipher: cipher mode + :type cipher: str + :keyword key_size: key size in bits + :type key_size: int + :keyword escrow_cert: certificate (contents) to use for key escrow + :type escrow_cert: str + :keyword add_backup_passphrase: generate a backup passphrase? + :type add_backup_passphrase: bool. + + .. note:: + + The 'device' kwarg is required for existing formats. For non- + existent formats, it is only necessary that the :attr:`device` + attribute be set before the :meth:`create` method runs. Note + that you can specify the device at the last moment by specifying + it via the 'device' kwarg to the :meth:`create` method. """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) @@ -154,7 +166,18 @@ class LUKS(DeviceFormat): raise NotImplementedError("probe method not defined for LUKS")
def setup(self, *args, **kwargs): - """ Open, or set up, the format. """ + """ Open the encrypted block device. + + :keyword device: device node path + :type device: str. + :raises: FormatSetupError. + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, mapName=self.mapName, type=self.type, status=self.status) if not self.configured: @@ -180,7 +203,18 @@ class LUKS(DeviceFormat): crypto.luks_close(self.mapName)
def create(self, *args, **kwargs): - """ Create the format. """ + """ Write the formatting to the specified block device. + + :keyword device: path to device node + :type device: str. + :raises: FormatCreateError + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.hasKey: @@ -204,7 +238,11 @@ class LUKS(DeviceFormat): self.notifyKernel()
def destroy(self, *args, **kwargs): - """ Create the format. """ + """ Remove the formatting from the associated block device. + + :raises: FormatDestroyError + :returns: None. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) self.teardown() diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py index 554f238..034825e 100644 --- a/blivet/formats/lvmpv.py +++ b/blivet/formats/lvmpv.py @@ -47,17 +47,23 @@ class LVMPhysicalVolume(DeviceFormat): _ksMountpoint = "pv."
def __init__(self, *args, **kwargs): - """ Create an LVMPhysicalVolume instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this PV's uuid (not the VG uuid) - vgName -- the name of the VG this PV belongs to - vgUuid -- the UUID of the VG this PV belongs to - peStart -- offset of first physical extent - exists -- indicates whether this is an existing format - + """ + :keyword device: path to the block device node + :keyword uuid: this PV's uuid (not the VG uuid) + :keyword exists: indicates whether this is an existing format + :type exists: bool + :keyword vgName: the name of the VG this PV belongs to + :keyword vgUuid: the UUID of the VG this PV belongs to + :keyword peStart: offset of first physical extent + :type peStart: float + + .. note:: + + The 'device' kwarg is required for existing formats. For non- + existent formats, it is only necessary that the :attr:`device` + attribute be set before the :meth:`create` method runs. Note + that you can specify the device at the last moment by specifying + it via the 'device' kwarg to the :meth:`create` method. """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) @@ -96,7 +102,18 @@ class LVMPhysicalVolume(DeviceFormat): #self.vgUuid = info['vg_uuid']
def create(self, *args, **kwargs): - """ Create the format. """ + """ Write the formatting to the specified block device. + + :keyword device: path to device node + :type device: str + :raises: FormatCreateError + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, type=self.type, status=self.status)
@@ -119,7 +136,11 @@ class LVMPhysicalVolume(DeviceFormat): self.notifyKernel()
def destroy(self, *args, **kwargs): - """ Destroy the format. """ + """ Remove the formatting from the associated block device. + + :raises: FormatDestroyError + :returns: None. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: diff --git a/blivet/formats/mdraid.py b/blivet/formats/mdraid.py index 7fbf6bb..8050b24 100644 --- a/blivet/formats/mdraid.py +++ b/blivet/formats/mdraid.py @@ -47,15 +47,16 @@ class MDRaidMember(DeviceFormat): _ksMountpoint = "raid."
def __init__(self, *args, **kwargs): - """ Create a MDRaidMember instance. - - Keyword Arguments: + """ + :keyword device: path to block device node + :keyword uuid: this member device's uuid + :keyword exists: whether this is an existing format + :type exists: bool + :keyword mdUuid: the uuid of the array this device belongs to
- device -- path to underlying device - uuid -- this member device's uuid - mdUuid -- the uuid of the array this device belongs to - exists -- indicates whether this is an existing format + .. note::
+ The 'device' kwarg is required for existing formats. """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) @@ -91,6 +92,11 @@ class MDRaidMember(DeviceFormat): self.raidMinor = info['mdMinor']
def destroy(self, *args, **kwargs): + """ Remove the formatting from the associated block device. + + :raises: FormatDestroyError + :returns: None. + """ if not self.exists: raise MDMemberError("format does not exist")
diff --git a/blivet/formats/multipath.py b/blivet/formats/multipath.py index 67c32e4..b7e7076 100644 --- a/blivet/formats/multipath.py +++ b/blivet/formats/multipath.py @@ -47,16 +47,11 @@ class MultipathMember(DeviceFormat): _hidden = True # hide devices with this formatting?
def __init__(self, *args, **kwargs): - """ Create a DeviceFormat instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this format's UUID - exists -- indicates whether this is an existing format - - On initialization this format is like DeviceFormat - + """ + :keyword device: path to the underlying device (required) + :keyword uuid: this format's UUID + :keyword exists: whether this is an existing format + :type exists: bool """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) diff --git a/blivet/formats/prepboot.py b/blivet/formats/prepboot.py index 4d20f0f..605486f 100644 --- a/blivet/formats/prepboot.py +++ b/blivet/formats/prepboot.py @@ -41,19 +41,36 @@ class PPCPRePBoot(DeviceFormat): _minSize = 4 # minimum size in MB
def __init__(self, *args, **kwargs): - """ Create a PRePBoot instance. - - Keyword Arguments: + """ + :keyword device: path to block device node + :keyword exists: whether this is an existing format + :type exists: bool
- device -- path to the underlying device - exists -- indicates whether this is an existing format + .. note::
+ The 'device' kwarg is required for existing formats. For non- + existent formats, it is only necessary that the :attr:`device` + attribute be set before the :meth:`create` method runs. Note + that you can specify the device at the last moment by specifying + it via the 'device' kwarg to the :meth:`create` method. """ DeviceFormat.__init__(self, *args, **kwargs)
def create(self, *args, **kwargs): + """ Write the formatting to the specified block device. + + :keyword device: path to device node + :type device: str + :raises: FormatCreateError + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ if self.exists: - raise FSError("PReP Boot format already exists") + raise FormatCreateError("PReP Boot format already exists")
DeviceFormat.create(self, *args, **kwargs)
diff --git a/blivet/formats/swap.py b/blivet/formats/swap.py index 72ca84f..f0c1fd4 100644 --- a/blivet/formats/swap.py +++ b/blivet/formats/swap.py @@ -46,16 +46,22 @@ class SwapSpace(DeviceFormat): _maxSize = 128 * 1024
def __init__(self, *args, **kwargs): - """ Create a SwapSpace instance. - - Keyword Arguments: - - device -- path to the underlying device - uuid -- this swap space's uuid - label -- this swap space's label - priority -- this swap space's priority - exists -- indicates whether this is an existing format - + """ + :keyword device: path to the block device node + :keyword uuid: this swap space's uuid + :keyword exists: whether this is an existing format + :type exists: bool + :keyword label: this swap space's label + :keyword priority: this swap space's priority + :type priority: int + + .. note:: + + The 'device' kwarg is required for existing formats. For non- + existent formats, it is only necessary that the 'device' + attribute be set before the :meth:`~.SwapSpace.create` method + runs. You can specify the device at the last moment by via the + 'device' kwarg to the :meth:`~.SwapSpace.create` method. """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) @@ -120,7 +126,18 @@ class SwapSpace(DeviceFormat): return self.exists and swap.swapstatus(self.device)
def setup(self, *args, **kwargs): - """ Open, or set up, a device. """ + """ Activate the formatting. + + :keyword device: device node path + :type device: str. + :raises: FormatSetupError. + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: @@ -143,18 +160,23 @@ class SwapSpace(DeviceFormat): swap.swapoff(self.device)
def create(self, *args, **kwargs): - """ Create the device. """ + """ Write the formatting to the specified block device. + + :keyword device: path to device node + :type device: str. + :raises: FormatCreateError + :returns: None. + + .. :note:: + + If a device node path is passed to this method it will overwrite + any previously set value of this instance's "device" attribute. + """ log_method_call(self, device=self.device, type=self.type, status=self.status) - force = kwargs.get("force") - if not force and self.exists: + if self.exists: raise SwapSpaceError("format already exists")
- if force: - self.teardown() - elif self.status: - raise SwapSpaceError("device exists and is active") - try: DeviceFormat.create(self, *args, **kwargs) swap.mkswap(self.device, label=self.label) diff --git a/blivet/iscsi.py b/blivet/iscsi.py index 45a0ac8..f30a861 100644 --- a/blivet/iscsi.py +++ b/blivet/iscsi.py @@ -405,7 +405,4 @@ class iscsi(object):
return nodeDisks
-# Create iscsi singleton -iscsi = iscsi() - # vim:tw=78:ts=4:et:sw=4 diff --git a/blivet/partitioning.py b/blivet/partitioning.py index 4216be2..c88c90d 100644 --- a/blivet/partitioning.py +++ b/blivet/partitioning.py @@ -39,7 +39,17 @@ import logging log = logging.getLogger("blivet")
def _getCandidateDisks(storage): - """ Return a list of disks with space for a default-sized partition. """ + """ Return a list of disks to be used for autopart. + + Disks must be partitioned and have a single free region large enough + for a default-sized (500MB) partition. They must also be in + :attr:`StorageDiscoveryConfig.clearPartDisks` if it is non-empty. + + :param storage: a Blivet instance + :type storage: :class:`~.Blivet` + :returns: a list of partitioned disks with at least 500MB of free space + :rtype: list of :class:`~.devices.StorageDevice` + """ disks = [] for disk in storage.partitioned: if storage.config.clearPartDisks and \ @@ -61,7 +71,18 @@ def _getCandidateDisks(storage): return disks
def _scheduleImplicitPartitions(storage, disks): - """ Schedule creation of a lvm/btrfs partition on each disk in disks. """ + """ Schedule creation of a lvm/btrfs member partitions for autopart. + + We create one such partition on each disk. They are not allocated until + later (in :func:`doPartitioning`). + + :param storage: a :class:`~.Blivet` instance + :type storage: :class:`~.Blivet` + :param disks: list of partitioned disks with free space + :type disks: list of :class:`~.devices.StorageDevice` + :returns: list of newly created (unallocated) partitions + :rtype: list of :class:`~.devices.PartitionDevice` + """ # create a separate pv or btrfs partition for each disk with free space devs = []
@@ -92,7 +113,17 @@ def _scheduleImplicitPartitions(storage, disks): return devs
def _schedulePartitions(storage, disks): - """ Schedule creation of autopart partitions. """ + """ Schedule creation of autopart partitions. + + This only schedules the requests for actual partitions. + + :param storage: a :class:`~.Blivet` instance + :type storage: :class:`~.Blivet` + :param disks: list of partitioned disks with free space + :type disks: list of :class:`~.devices.StorageDevice` + :returns: None + :rtype: None + """ # basis for requests with requiredSpace is the sum of the sizes of the # two largest free regions all_free = getFreeRegions(disks) @@ -197,7 +228,23 @@ def _schedulePartitions(storage, disks): return
def _scheduleVolumes(storage, devs): - """ Schedule creation of autopart lvm/btrfs volumes. """ + """ Schedule creation of autopart lvm/btrfs volumes. + + Schedules encryption of member devices if requested, schedules creation + of the container (:class:`~.devices.LVMVolumeGroupDevice` or + :class:`~.devices.BTRFSVolumeDevice`) then schedules creation of the + autopart volume requests. + + :param storage: a :class:`~.Blivet` instance + :type storage: :class:`~.Blivet` + :param devs: list of member partitions + :type devs: list of :class:`~.devices.PartitionDevice` + :returns: None + :rtype: None + + If an appropriate bootloader stage1 device exists on the boot drive, any + autopart request to create another one will be skipped/discarded. + """ if not devs: return
@@ -283,6 +330,24 @@ def _scheduleVolumes(storage, devs): storage.createDevice(dev)
def doAutoPartition(storage, data): + """ Perform automatic partitioning. + + :param storage: a :class:`~.Blivet` instance + :type storage: :class:`~.Blivet` + :param data: kickstart data + :type data: :class:`pykickstart.BaseHandler` + + :attr:`Blivet.doAutoPart` controls whether this method creates the + automatic partitioning layout. :attr:`Blivet.autoPartType` controls + which variant of autopart used. It uses one of the pykickstart + AUTOPART_TYPE_* constants. The set of eligible disks is defined in + :attr:`StorageDiscoveryConfig.clearPartDisks`. + + .. note:: + + Clearing of partitions is handled separately, in + :meth:`~.Blivet.clearPartitions`. + """ log.debug("doAutoPart: %s" % storage.doAutoPart) log.debug("encryptedAutoPart: %s" % storage.encryptedAutoPart) log.debug("autoPartType: %s" % storage.autoPartType) @@ -342,6 +407,13 @@ def partitionCompare(part1, part2): < 1 => x < y 0 => x == y > 1 => x > y + + :param part1: the first partition + :type part1: :class:`devices.PartitionDevice` + :param part2: the other partition + :type part2: :class:`devices.PartitionDevice` + :return: see above + :rtype: int """ ret = 0
@@ -401,7 +473,7 @@ def partitionCompare(part1, part2): return ret
def getNextPartitionType(disk, no_primary=None): - """ Find the type of partition to create next on a disk. + """ Return the type of partition to create next on a disk.
Return a parted partition type value representing the type of the next partition we will create on this disk. @@ -413,15 +485,11 @@ def getNextPartitionType(disk, no_primary=None): recommend creating a primary partition. This can be overridden with the keyword argument no_primary.
- Arguments: - - disk -- a parted.Disk instance representing the disk - - Keyword arguments: - - no_primary -- given a choice between primary and logical - partitions, prefer logical - + :param disk: the disk from which a partition may be allocated + :type disk: :class:`parted.Disk` + :keyword no_primary: refuse to return :const:`parted.PARTITION_NORMAL` + :returns: the chosen partition type + :rtype: a parted PARTITION_* constant """ part_type = None extended = disk.getExtendedPartition() @@ -474,20 +542,20 @@ def getBestFreeSpaceRegion(disk, part_type, req_size, start=None, free region with which to compare the best from this disk. The overall best region is returned.
- Arguments: - - disk -- the disk (a parted.Disk instance) - part_type -- the type of partition we want to allocate - (one of parted's partition type constants) - req_size -- the requested size of the partition (in MB) - - Keyword arguments: - - boot -- indicates whether this will be a bootable partition - (boolean) - best_free -- current best free region for this partition - grow -- indicates whether this is a growable request - start -- requested start sector for the partition + :param disk: the disk + :type disk: :class:`parted.Disk` + :param part_type: the type of partition we want to allocate + :type part_type: one of parted's PARTITION_* constants + :param req_size: the requested size of the partition in MB + :type req_size: int or float + :keyword start: requested start sector for the partition + :type start: int + :keyword boot: whether this will be a bootable partition + :type boot: bool + :keyword best_free: current best free region for this partition + :type best_free: :class:`parted.Geometry` + :keyword grow: indicates whether this is a growable request + :type grow: bool
""" log.debug("getBestFreeSpaceRegion: disk=%s part_type=%d req_size=%dMB " @@ -550,31 +618,38 @@ def getBestFreeSpaceRegion(disk, part_type, req_size, start=None, def sectorsToSize(sectors, sectorSize): """ Convert length in sectors to size in MB.
- Arguments: - - sectors - sector count - sectorSize - sector size for the device, in bytes + :param sectors: sector count + :type sectors: int + :param sectorSize: sector size + :type sectorSize: int + :returns: the size + :rtype: float """ return (sectors * sectorSize) / (1024.0 * 1024.0)
def sizeToSectors(size, sectorSize): """ Convert size in MB to length in sectors.
- Arguments: - - size - size in MB - sectorSize - sector size for the device, in bytes + :param size: size in MB + :type size: int or float + :param sectorSize: sector size + :type sectorSize: int + :returns: sector count + :rtype: int """ return (size * 1024.0 * 1024.0) / sectorSize
def removeNewPartitions(disks, partitions): - """ Remove newly added input partitions from input disks. - - Arguments: + """ Remove newly added partitions from disks.
- disks -- list of StorageDevice instances with DiskLabel format - partitions -- list of PartitionDevice instances + Remove all non-existent partitions from the disks in blivet's model.
+ :param: disks: list of partitioned disks + :type disks: list of :class:`~.devices.StorageDevice` + :param partitions: list of partitions + :type partitions: list of :class:`~.devices.PartitionDevice` + :returns: None + :rtype: NoneType """ log.debug("removing all non-preexisting partitions %s from disk(s) %s" % (["%s(id %d)" % (p.name, p.id) for p in partitions @@ -605,18 +680,28 @@ def removeNewPartitions(disks, partitions): disk.format.partedDisk.removePartition(extended)
def addPartition(disklabel, free, part_type, size, start=None, end=None): - """ Return new partition after adding it to the specified disk. - - Arguments: - - disklabel -- disklabel instance to add partition to - free -- where to add the partition (parted.Geometry instance) - part_type -- partition type (parted.PARTITION_* constant) - size -- size (in MB) of the new partition - - The new partition will be aligned unless start/end sectors are provided. - - Return value is a parted.Partition instance. + """ Add a new partition to a disk. + + :param disklabel: the disklabel to add the partition to + :type disklabel: :class:`~.formats.DiskLabel` + :param free: the free region in which to place the new partition + :type free: :class:`parted.Geometry` + :param part_type: the partition type + :type part_type: a parted.PARTITION_* constant + :param size: size of the new partition in MB + :type size: int or float + :keyword start: starting sector for the partition + :type start: int + :keyword end: ending sector for the partition + :type end: int + :raises: :class:`~.errors.PartitioningError` + :returns: the newly added partitions + :rtype: :class:`parted.Partition` + + .. note:: + + The new partition will be aligned using the kernel-provided optimal + alignment unless a start sector is provided.
""" if start is not None: @@ -673,12 +758,10 @@ def addPartition(disklabel, free, part_type, size, start=None, end=None): def getFreeRegions(disks): """ Return a list of free regions on the specified disks.
- Arguments: - - disks -- list of parted.Disk instances - - Return value is a list of unaligned parted.Geometry instances. - + :param disks: list of disks + :type disks: list of :class:`~.devices.Disks` + :returns: list of free regions + :rtype: list of :class:`parted.Geometry` """ free = [] for disk in disks: @@ -689,6 +772,15 @@ def getFreeRegions(disks): return free
def updateExtendedPartitions(storage, disks): + """ Reconcile extended partition changes with the DeviceTree. + + :param storage: the Blivet instance + :type storage: :class:`~.Blivet` + :param disks: list of disks + :type disks: list of :class:`~.devices.StorageDevice` + :returns: :const:`None` + :rtype: NoneType + """ # XXX hack -- if we created any extended partitions we need to add # them to the tree now for disk in disks: @@ -742,14 +834,10 @@ def doPartitioning(storage): appropriate parted.Partition instance from their containing disk. All req_xxxx attributes must be unchanged.
- Arguments: - - storage - Main anaconda Storage instance - - Keyword/Optional Arguments: - - None - + :param storage: Blivet instance + :type storage: :class:`~.Blivet` + :raises: :class:`~.errors.PartitioningError` + :returns: :const:`None` """ disks = storage.partitioned for disk in disks: @@ -824,16 +912,26 @@ def doPartitioning(storage): def allocatePartitions(storage, disks, partitions, freespace): """ Allocate partitions based on requested features.
+ :param storage: a Blivet instance + :type storage: :class:`~.Blivet` + :param disks: list of usable disks + :type disks: list of :class:`~.devices.StorageDevice` + :param partitions: list of partitions + :type partitions: list of :class:`~.devices.PartitionDevice` + :param freespace: list of free regions on disks + :type freespace: list of :class:`parted.Geometry` + :raises: :class:`~.errors.PartitioningError` + :returns: :const:`None` + Non-existing partitions are sorted according to their requested attributes, and then allocated.
The basic approach to sorting is that the more specifically- defined a request is, the earlier it will be allocated. See - the function partitionCompare for details on the sorting - criteria. + :func:`partitionCompare` for details of the sorting criteria.
- The PartitionDevice instances will have their name and parents - attributes set once they have been allocated. + The :class:`~.devices.PartitionDevice` instances will have their name + and parents attributes set once they have been allocated. """ log.debug("allocatePartitions: disks=%s ; partitions=%s" % ([d.name for d in disks], @@ -1114,10 +1212,9 @@ class Request(object): partitions. """ def __init__(self, device): - """ Create a Request instance. - - Arguments: - + """ + :param device: the device being requested + :type device: :class:`~.devices.StorageDevice` """ self.device = device self.growth = 0 # growth in sectors @@ -1150,12 +1247,9 @@ class Request(object):
class PartitionRequest(Request): def __init__(self, partition): - """ Create a PartitionRequest instance. - - Arguments: - - partition -- a PartitionDevice instance - + """ + :param partition: the partition being requested + :type partition: :class:`~.devices.PartitionDevice` """ super(PartitionRequest, self).__init__(partition) self.base = partition.partedPartition.geometry.length # base sectors @@ -1178,12 +1272,9 @@ class PartitionRequest(Request):
class LVRequest(Request): def __init__(self, lv): - """ Create a LVRequest instance. - - Arguments: - - lv -- an LVMLogicalVolumeDevice instance - + """ + :param lv: the logical volume being requested + :type lv: :class:`~.devices.LVMLogicalVolumeDevice` """ super(LVRequest, self).__init__(lv)
@@ -1207,17 +1298,11 @@ class LVRequest(Request): class Chunk(object): """ A free region from which devices will be allocated """ def __init__(self, length, requests=None): - """ Create a Chunk instance. - - Arguments: - - length -- the length of the chunk in allocation units - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - + """ + :param length: the length of the chunk (units vary with subclass) + :type length: int + :keyword requests: list of requests to add + :type requests: list of :class:`Request` """ if not hasattr(self, "path"): self.path = None @@ -1247,7 +1332,11 @@ class Chunk(object): return s
def addRequest(self, req): - """ Add a Request to this chunk. """ + """ Add a request to this chunk. + + :param req: the request to add + :type req: :class:`Request` + """ log.debug("adding request %d to chunk %s" % (req.device.id, self))
self.requests.append(req) @@ -1257,7 +1346,15 @@ class Chunk(object): self.base += req.base
def reclaim(self, request, amount): - """ Reclaim units from a request and return them to the pool. """ + """ Reclaim units from a request and return them to the pool. + + :param request: the request to reclaim units from + :type request: :class:`Request` + :param amount: number of units to reclaim from the request + :type amount: int + :raises: ValueError + :returns: None + """ log.debug("reclaim: %s %d (%d MB)" % (request, amount, self.lengthToSize(amount))) if request.growth < amount: log.error("tried to reclaim %d from request with %d of growth" @@ -1305,7 +1402,15 @@ class Chunk(object): return size
def trimOverGrownRequest(self, req, base=None): - """ Enforce max growth and return extra units to the pool. """ + """ Enforce max growth and return extra units to the pool. + + :param req: the request to trim + :type req: :class:`Request` + :keyword base: base unit count to adjust if req is done growing + :type base: int + :returns: the new base or None if no base was given + :rtype: int or None + """ max_growth = self.maxGrowth(req) if max_growth and req.growth >= max_growth: if req.growth > max_growth: @@ -1330,7 +1435,19 @@ class Chunk(object): pass
def growRequests(self, uniform=False): - """ Calculate growth amounts for requests in this chunk. """ + """ Calculate growth amounts for requests in this chunk. + + :keyword uniform: grow requests uniformly instead of proportionally + :type uniform: bool + + The default mode of growth is as follows: given a total number of + available units, requests receive an allotment proportional to their + base sizes. That means a request with base size 1000 will grow four + times as fast as a request with base size 250. + + Under uniform growth, all requests receive an equal portion of the + free units. + """ log.debug("Chunk.growRequests: %r" % self)
self.sortRequests() @@ -1404,17 +1521,11 @@ class Chunk(object): class DiskChunk(Chunk): """ A free region on disk from which partitions will be allocated """ def __init__(self, geometry, requests=None): - """ Create a Chunk instance. - - Arguments: - - geometry -- parted.Geometry instance describing the free space - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - + """ + :param geometry: the free region this chunk represents + :type geometry: :class:`parted.Geometry` + :keyword requests: list of requests to add initially + :type requests: list of :class:`PartitionRequest`
Note: We will limit partition growth based on disklabel limitations for partition end sector, so a 10TB disk with an @@ -1440,7 +1551,11 @@ class DiskChunk(Chunk): return s
def addRequest(self, req): - """ Add a Request to this chunk. """ + """ Add a request to this chunk. + + :param req: the request to add + :type req: :class:`PartitionRequest` + """ if not isinstance(req, PartitionRequest): raise ValueError(_("DiskChunk requests must be of type " "PartitionRequest")) @@ -1467,6 +1582,11 @@ class DiskChunk(Chunk): super(DiskChunk, self).addRequest(req)
def maxGrowth(self, req): + """ Return the maximum possible growth for a request. + + :param req: the request + :type req: :class:`PartitionRequest` + """ req_end = req.device.partedPartition.geometry.end req_start = req.device.partedPartition.geometry.start
@@ -1515,17 +1635,11 @@ class DiskChunk(Chunk): class VGChunk(Chunk): """ A free region in an LVM VG from which LVs will be allocated """ def __init__(self, vg, requests=None): - """ Create a VGChunk instance. - - Arguments: - - vg -- an LVMVolumeGroupDevice within which this chunk resides - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - + """ + :param vg: the volume group whose free space this chunk represents + :type vg: :class:`~.devices.LVMVolumeGroupDevice` + :keyword requests: list of requests to add initially + :type requests: list of :class:`LVRequest` """ self.vg = vg self.path = vg.path @@ -1533,7 +1647,11 @@ class VGChunk(Chunk): super(VGChunk, self).__init__(usable_extents, requests=requests)
def addRequest(self, req): - """ Add a Request to this chunk. """ + """ Add a request to this chunk. + + :param req: the request to add + :type req: :class:`LVRequest` + """ if not isinstance(req, LVRequest): raise ValueError(_("VGChunk requests must be of type " "LVRequest")) @@ -1550,7 +1668,20 @@ class VGChunk(Chunk): # sort the partitions by start sector self.requests.sort(key=lambda r: r.device, cmp=lvCompare)
- def growRequests(self): + def growRequests(self, uniform=False): + """ Calculate growth amounts for requests in this chunk. + + :keyword uniform: grow requests uniformly instead of proportionally + :type uniform: bool + + The default mode of growth is as follows: given a total number of + available units, requests receive an allotment proportional to their + base sizes. That means a request with base size 1000 will grow four + times as fast as a request with base size 250. + + Under uniform growth, all requests receive an equal portion of the + free units. + """ self.sortRequests()
# grow the percentage-based requests @@ -1578,23 +1709,17 @@ class VGChunk(Chunk): self.base -= req.base req.done = True
- super(VGChunk, self).growRequests() + super(VGChunk, self).growRequests(uniform=uniform)
class ThinPoolChunk(VGChunk): """ A free region in an LVM thin pool from which LVs will be allocated """ def __init__(self, pool, requests=None): - """ Create a ThinPoolChunk instance. - - Arguments: - - pool -- an LVMThinPoolDevice within which this chunk resides - - - Keyword Arguments: - - requests -- list of Request instances allocated from this chunk - + """ + :param pool: the thin pool whose free space this chunk represents + :type pool: :class:`~.devices.LVMThinPoolDevice` + :keyword requests: list of requests to add initially + :type requests: list of :class:`LVRequest` """ self.vg = pool.vg # only used for align, &c self.path = pool.path @@ -1605,14 +1730,16 @@ class ThinPoolChunk(VGChunk): def getDiskChunks(disk, partitions, free): """ Return a list of Chunk instances representing a disk.
- Arguments: - - disk -- a StorageDevice with a DiskLabel format - partitions -- list of PartitionDevice instances - free -- list of parted.Geometry instances representing free space + :param disk: the disk + :type disk: :class:`~.devices.StorageDevice` + :param partitions: list of partitions + :type partitions: list of :class:`~.devices.PartitionDevice` + :param free: list of free regions + :type free: list of :class:`parted.Geometry` + :returns: list of chunks representing the disk + :rtype: list of :class:`DiskChunk`
Partitions and free regions not on the specified disk are ignored. - """ # list of all new partitions on this disk disk_parts = [p for p in partitions if p.disk == disk and not p.exists] @@ -1641,6 +1768,12 @@ class TotalSizeSet(object): size has been achieved. """ def __init__(self, devices, size): + """ + :param devices: the set of devices + :type devices: list of :class:`~.devices.PartitionDevice` + :param size: the target combined size in MB + :type size: int or float + """ self.devices = [] for device in devices: if isinstance(device, LUKSDevice): @@ -1674,6 +1807,16 @@ class TotalSizeSet(object): class SameSizeSet(object): """ Set of device requests with a common target size. """ def __init__(self, devices, size, grow=False, max_size=None): + """ + :param devices: the set of devices + :type devices: list of :class:`~.devices.PartitionDevice` + :param size: target size for each device/request in MB + :type size: int or float + :keyword grow: whether the devices can be grown + :type grow: bool + :keyword max_size: the maximum size for growable devices in MB + :type max_size: int or float + """ self.devices = [] for device in devices: if isinstance(device, LUKSDevice): @@ -1784,11 +1927,15 @@ def growPartitions(disks, partitions, free, size_sets=None): the ratio of that partition's base size to the sum of the base sizes of all growable partitions allocated from the chunk.
- Arguments: - - disks -- a list of all usable disks (DiskDevice instances) - partitions -- a list of all partitions (PartitionDevice instances) - free -- a list of all free regions (parted.Geometry instances) + :param disks: all usable disks + :type disks: list of :class:`~.devices.StorageDevice` + :param partitions: all partitions + :type partitions: list of :class:`~.devices.PartitionDevice` + :param free: all free regions on disks + :type free: list of :class:`parted.Geometry` + :keyword size_sets: list of size-related partition sets + :type size_sets: list of :class:`TotalSizeSet` or :class:`SameSizeSet` + :returns: :const:`None` """ log.debug("growPartitions: disks=%s, partitions=%s" % ([d.name for d in disks],
On Fri, 2013-12-13 at 09:58 -0600, David Lehman wrote:
diff --git a/blivet/formats/disklabel.py b/blivet/formats/disklabel.py index bfb707a..323b36b 100644 --- a/blivet/formats/disklabel.py +++ b/blivet/formats/disklabel.py @@ -45,18 +45,20 @@ class DiskLabel(DeviceFormat): _supported = False # is supported
def __init__(self, *args, **kwargs):
""" Create a DiskLabel instance.
Keyword Arguments:
labelType -- type of disklabel to create
device -- path to the underlying device
exists -- indicates whether this is an existing format
"""
:keyword device: full path to the block device node (required)
:type device: str
:keyword labelType: type of disklabel to create
:type labelType: str
:keyword exists: whether the formatting exists
:type exists: bool """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs)
if not self.device:
raise ValueError("DiskLabel requires a valid device path")
I am dropping the above and the associated piece of the docstring. All other formats can be instantiated without any arguments and that's useful.
----- Original Message -----
From: "David Lehman" dlehman@redhat.com To: anaconda-patches@lists.fedorahosted.org Sent: Friday, December 13, 2013 10:58:42 AM Subject: [PATCH 2/2] Convert to sphinx docstrings.
blivet/__init__.py | 330 +++++++++++++++--- blivet/dasd.py | 3 - blivet/devices.py | 833 ++++++++++++++++++++++++-------------------- blivet/devicetree.py | 248 +++++++++++-- blivet/formats/__init__.py | 107 ++++-- blivet/formats/biosboot.py | 12 +- blivet/formats/disklabel.py | 72 ++-- blivet/formats/dmraid.py | 16 +- blivet/formats/fs.py | 146 +++++--- blivet/formats/luks.py | 74 +++- blivet/formats/lvmpv.py | 47 ++- blivet/formats/mdraid.py | 20 +- blivet/formats/multipath.py | 15 +- blivet/formats/prepboot.py | 29 +- blivet/formats/swap.py | 60 +++- blivet/iscsi.py | 3 - blivet/partitioning.py | 447 ++++++++++++++++-------- 17 files changed, 1645 insertions(+), 817 deletions(-)
Might as well put this all in and then proceed to bring the rest in line.
Note that sphinx generates documentation for the abstractproperty function, not for the property that it's defining. It works fine on the property function.
-- 1.8.1.4
anaconda-patches mailing list anaconda-patches@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches
anaconda-patches@lists.fedorahosted.org