<commit_before># -*- coding: utf-8 -*-
#
# SunPy documentation build configuration file, created by
# sphinx-quickstart on Sat Apr  9 13:09:06 2011.
#
# 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, math

class Mock(object):
    __all__ = []
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

    def __call__(self, *args, **kwargs):
        return Mock()

    __add__  = __mul__  = __getitem__ = __setitem__ = \
__delitem__ = __sub__ =  __floordiv__ = __mod__ = __divmod__ = \
__pow__ = __lshift__ = __rshift__ = __and__ = __xor__ = __or__ = \
__rmul__  = __rsub__  = __rfloordiv__ = __rmod__ = __rdivmod__ = \
__rpow__ = __rlshift__ = __rrshift__ = __rand__ = __rxor__ = __ror__ = \
__imul__  = __isub__  = __ifloordiv__ = __imod__ = __idivmod__ = \
__ipow__ = __ilshift__ = __irshift__ = __iand__ = __ixor__ = __ior__ = \
__neg__ = __pos__ = __abs__ = __invert__ = __call__

    def __getattr__(self, name):
        if name in ('__file__', '__path__'):
            return '/dev/null'
        if name == 'sqrt':
            return math.sqrt
        elif name[0] != '_' and name[0] == name[0].upper():
            return type(name, (), {})
        else:
            return Mock(**vars(self))

    def __lt__(self, *args, **kwargs):
        return True

    __nonzero__ = __le__ = __eq__ = __ne__ = __gt__ = __ge__ = __contains__ = \
__lt__

    def __repr__(self):
        return "Mock"

    def __hash__(self):
        return 1

    __len__ = __int__ = __long__ = __index__ = __hash__

    def __oct__(self):
        return '01'

    def __hex__(self):
        return '0x1'

    def __float__(self):
        return 0.1

    def __complex__(self):
        return 1j

MOCK_MODULES = [
    'scipy', 'matplotlib', 'matplotlib.pyplot', 'pyfits',
    'scipy.constants.constants', 'matplotlib.cm',
    'matplotlib.image', 'matplotlib.colors', 'sunpy.cm',
    'pandas', 'pandas.io', 'pandas.io.parsers',
    'suds', 'matplotlib.ticker', 'matplotlib.colorbar',
    'matplotlib.dates', 'scipy.optimize', 'scipy.ndimage',
    'matplotlib.figure', 'scipy.ndimage.interpolation', 'bs4']
for mod_name in MOCK_MODULES:
    sys.modules[mod_name] = Mock(pi=math.pi, G=6.67364e-11)

sys.modules['numpy'] = Mock(pi=math.pi, G=6.67364e-11, ndarray=type('ndarray', (), {}))
sys.modules['scipy.constants'] = Mock(pi=math.pi, G=6.67364e-11)

# 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.append(os.path.abspath('.'))
sys.path.append(os.path.abspath('../../'))
sys.path.append(os.path.abspath('../../sunpy/'))

# -- General configuration -----------------------------------------------------

# Note: numpydoc extension is required and can be found at
# is available with the numpy source code
# https://github.com/numpy/numpy/tree/master/doc/sphinxext

# 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', 'numpydoc', 'sphinx.ext.todo',
              'sphinx.ext.pngmath', 'sphinx.ext.viewcode',
              'sphinx.ext.autosummary']

# 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'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'SunPy'
copyright = u'2012, SunPy Community' #pylint: disable=W0622

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
default_role = "autolink"

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
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 = '../logo/sunpy_logo_compact_300x373.png'

# 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 = 'favicon.ico'

# 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_use_modindex = 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, 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 = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'SunPydoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'SunPy.tex', u'SunPy Documentation',
   u'SunPy Community', '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

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True

# Autosummary
import glob
autosummary_generate = (glob.glob("reference/*.rst") +
                        glob.glob("reference/*/*.rst"))
<commit_msg>Ignore prefix for module index (modindex)<commit_after># -*- coding: utf-8 -*-
#
# SunPy documentation build configuration file, created by
# sphinx-quickstart on Sat Apr  9 13:09:06 2011.
#
# 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, math

class Mock(object):
    __all__ = []
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

    def __call__(self, *args, **kwargs):
        return Mock()

    __add__  = __mul__  = __getitem__ = __setitem__ = \
__delitem__ = __sub__ =  __floordiv__ = __mod__ = __divmod__ = \
__pow__ = __lshift__ = __rshift__ = __and__ = __xor__ = __or__ = \
__rmul__  = __rsub__  = __rfloordiv__ = __rmod__ = __rdivmod__ = \
__rpow__ = __rlshift__ = __rrshift__ = __rand__ = __rxor__ = __ror__ = \
__imul__  = __isub__  = __ifloordiv__ = __imod__ = __idivmod__ = \
__ipow__ = __ilshift__ = __irshift__ = __iand__ = __ixor__ = __ior__ = \
__neg__ = __pos__ = __abs__ = __invert__ = __call__

    def __getattr__(self, name):
        if name in ('__file__', '__path__'):
            return '/dev/null'
        if name == 'sqrt':
            return math.sqrt
        elif name[0] != '_' and name[0] == name[0].upper():
            return type(name, (), {})
        else:
            return Mock(**vars(self))

    def __lt__(self, *args, **kwargs):
        return True

    __nonzero__ = __le__ = __eq__ = __ne__ = __gt__ = __ge__ = __contains__ = \
__lt__

    def __repr__(self):
        return "Mock"

    def __hash__(self):
        return 1

    __len__ = __int__ = __long__ = __index__ = __hash__

    def __oct__(self):
        return '01'

    def __hex__(self):
        return '0x1'

    def __float__(self):
        return 0.1

    def __complex__(self):
        return 1j

MOCK_MODULES = [
    'scipy', 'matplotlib', 'matplotlib.pyplot', 'pyfits',
    'scipy.constants.constants', 'matplotlib.cm',
    'matplotlib.image', 'matplotlib.colors', 'sunpy.cm',
    'pandas', 'pandas.io', 'pandas.io.parsers',
    'suds', 'matplotlib.ticker', 'matplotlib.colorbar',
    'matplotlib.dates', 'scipy.optimize', 'scipy.ndimage',
    'matplotlib.figure', 'scipy.ndimage.interpolation', 'bs4']
for mod_name in MOCK_MODULES:
    sys.modules[mod_name] = Mock(pi=math.pi, G=6.67364e-11)

sys.modules['numpy'] = Mock(pi=math.pi, G=6.67364e-11, ndarray=type('ndarray', (), {}))
sys.modules['scipy.constants'] = Mock(pi=math.pi, G=6.67364e-11)

# 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.append(os.path.abspath('.'))
sys.path.append(os.path.abspath('../../'))
sys.path.append(os.path.abspath('../../sunpy/'))

# -- General configuration -----------------------------------------------------

# Note: numpydoc extension is required and can be found at
# is available with the numpy source code
# https://github.com/numpy/numpy/tree/master/doc/sphinxext

# 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', 'numpydoc', 'sphinx.ext.todo',
              'sphinx.ext.pngmath', 'sphinx.ext.viewcode',
              'sphinx.ext.autosummary']

# 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'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'SunPy'
copyright = u'2012, SunPy Community' #pylint: disable=W0622

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
default_role = "autolink"

# 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 = ['sunpy.']

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = '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 = '../logo/sunpy_logo_compact_300x373.png'

# 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 = 'favicon.ico'

# 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_use_modindex = 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, 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 = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'SunPydoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'SunPy.tex', u'SunPy Documentation',
   u'SunPy Community', '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

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True

# Autosummary
import glob
autosummary_generate = (glob.glob("reference/*.rst") +
                        glob.glob("reference/*/*.rst"))
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Manage DNS records and zones using libcloud

    :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>`

.. versionadded:: 2016.11.0

Create and delete DNS records or zones through Libcloud. Libcloud's DNS system supports over 20 DNS
providers including Amazon, Google, GoDaddy, Softlayer

This module uses ``libcloud``, which can be installed via package, or pip.

:configuration:
    This module uses a configuration profile for one or multiple DNS providers

    .. code-block:: yaml

        libcloud_dns:
          profile1:
            driver: godaddy
            key: 2orgk34kgk34g
          profile2:
            driver: route53
            key: blah
            secret: blah

Example:

.. code-block:: yaml

    webserver:
      libcloud_dns.zone_present:
        name: mywebsite.com
        profile: profile1
      libcloud_dns.record_present:
        name: www
        zone: mywebsite.com
        type: A
        data: 12.34.32.3
        profile: profile1

:depends: apache-libcloud
'''

# Import Python Libs
from __future__ import absolute_import

from distutils.version import LooseVersion as _LooseVersion  # pylint: disable=import-error,no-name-in-module

import salt.modules.libcloud_dns as libcloud_dns_module

# Import salt libs
import salt.utils
import logging
log = logging.getLogger(__name__)

# Import third party libs
REQUIRED_LIBCLOUD_VERSION = '1.0.0'
try:
    #pylint: disable=unused-import
    import libcloud
    from libcloud.dns.providers import get_driver
    #pylint: enable=unused-import
    if hasattr(libcloud, '__version__') and _LooseVersion(libcloud.__version__) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION):
        raise ImportError()
    logging.getLogger('libcloud').setLevel(logging.CRITICAL)
    HAS_LIBCLOUD = True
except ImportError:
    HAS_LIBCLOUD = False

def __virtual__():
    '''
    Only load if libcloud libraries exist.
    '''
    if not HAS_LIBCLOUD:
        msg = ('A apache-libcloud library with version at least {0} was not '
               'found').format(REQUIRED_LIBCLOUD_VERSION)
        return (False, msg)
    return True

def __init__(opts):
    salt.utils.compat.pack_dunder(__name__)

def _get_driver(profile):
    config = __salt__['config.option']('libcloud_dns')[profile]
    cls = get_driver(config['driver'])
    key = config.get('key')
    secret = config.get('secret', None)
    secure = config.get('secure', True)
    host = config.get('host', None)
    port = config.get('port', None)
    return cls(key, secret, secure, host, port)

def state_result(result, message):
    return {'result': result, 'comment': message}

def zone_present(domain, type, profile):
    '''
    Ensures a record is present.

    :param domain: Zone name, i.e. the domain name
    :type  domain: ``str``

    :param type: Zone type (master / slave), defaults to master
    :type  type: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = libcloud_dns_module.list_zones(profile)
    if not type:
        type = 'master'
    matching_zone = [z for z in zones if z.domain == domain]
    if len(matching_zone) > 0:
        return state_result(True, "Zone already exists")
    else:
        result = libcloud_dns_module.create_zone(domain, profile, type)
        return state_result(result, "Created new zone")

def zone_absent(domain, profile):
    '''
    Ensures a record is absent.

    :param domain: Zone name, i.e. the domain name
    :type  domain: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = libcloud_dns_module.list_zones(profile)
    matching_zone = [z for z in zones if z.domain == domain]
    if len(matching_zone) == 0:
        return state_result(True, "Zone already absent")
    else:
        result = libcloud_dns_module.delete_zone(matching_zone[0].id, profile)
        return state_result(result, "Deleted zone")

def record_present(name, zone, type, data, profile):
    '''
    Ensures a record is present.

    :param name: Record name without the domain name (e.g. www).
                 Note: If you want to create a record for a base domain
                 name, you should specify empty string ('') for this
                 argument.
    :type  name: ``str``

    :param zone: Zone where the requested record is created, the domain name
    :type  zone: ``str``

    :param type: DNS record type (A, AAAA, ...).
    :type  type: ``str``

    :param data: Data for the record (depends on the record type).
    :type  data: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = libcloud_dns_module.list_zones(profile)
    try:
        matching_zone = [z for z in zones if z.domain == zone][0]
    except IndexError:
        return state_result(False, "Could not locate zone")
    records = libcloud_dns_module.list_records(matching_zone.id, profile)
    matching_records = [record for record in records
                        if record.name == name and
                        record.type == type and
                        record.data == data]
    if len(matching_records) == 0:
        result = libcloud_dns_module.create_record(
            name, matching_zone.id,
            type, data, profile)
        return state_result(result, "Created new record")
    else:
        return state_result(True, "Record already exists")

def record_absent(name, zone, type, data, profile):
    '''
    Ensures a record is absent.

    :param name: Record name without the domain name (e.g. www).
                 Note: If you want to create a record for a base domain
                 name, you should specify empty string ('') for this
                 argument.
    :type  name: ``str``

    :param zone: Zone where the requested record is created, the domain name
    :type  zone: ``str``

    :param type: DNS record type (A, AAAA, ...).
    :type  type: ``str``

    :param data: Data for the record (depends on the record type).
    :type  data: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = libcloud_dns_module.list_zones(profile)
    try:
        matching_zone = [z for z in zones if z.domain == zone][0]
    except IndexError:
        return state_result(False, "Zone could not be found")
    records = libcloud_dns_module.list_records(matching_zone.id, profile)
    matching_records = [record for record in records
                        if record.name == name and
                        record.type == type and
                        record.data == data]
    if len(matching_records) > 0:
        result = []
        for record in matching_records:
            result.append(libcloud_dns_module.delete_record(
                matching_zone.id,
                record.id,
                profile))
        return state_result(all(result), "Removed {0} records".format(len(result)))
    else:
        return state_result(True, "Records already absent")
<commit_msg>add fixes for incorrectly importing modules directly instead of using __salt__<commit_after># -*- coding: utf-8 -*-
'''
Manage DNS records and zones using libcloud

    :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>`

.. versionadded:: 2016.11.0

Create and delete DNS records or zones through Libcloud. Libcloud's DNS system supports over 20 DNS
providers including Amazon, Google, GoDaddy, Softlayer

This module uses ``libcloud``, which can be installed via package, or pip.

:configuration:
    This module uses a configuration profile for one or multiple DNS providers

    .. code-block:: yaml

        libcloud_dns:
          profile1:
            driver: godaddy
            key: 2orgk34kgk34g
          profile2:
            driver: route53
            key: blah
            secret: blah

Example:

.. code-block:: yaml

    webserver:
      libcloud_dns.zone_present:
        name: mywebsite.com
        profile: profile1
      libcloud_dns.record_present:
        name: www
        zone: mywebsite.com
        type: A
        data: 12.34.32.3
        profile: profile1

:depends: apache-libcloud
'''

# Import Python Libs
from __future__ import absolute_import

# Import salt libs
import salt.modules.libcloud_dns as libcloud_dns_module
import salt.utils
from salt.utils.versions import LooseVersion as _LooseVersion

# Import salt libs
import salt.utils
import logging
log = logging.getLogger(__name__)

def __virtual__():
    return True

def __init__(opts):
    salt.utils.compat.pack_dunder(__name__)

def state_result(result, message):
    return {'result': result, 'comment': message}

def zone_present(domain, type, profile):
    '''
    Ensures a record is present.

    :param domain: Zone name, i.e. the domain name
    :type  domain: ``str``

    :param type: Zone type (master / slave), defaults to master
    :type  type: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = __salt__['libcloud_dns.list_zones'](profile)
    if not type:
        type = 'master'
    matching_zone = [z for z in zones if z.domain == domain]
    if len(matching_zone) > 0:
        return state_result(True, "Zone already exists")
    else:
        result = libcloud_dns_module.create_zone(domain, profile, type)
        return state_result(result, "Created new zone")

def zone_absent(domain, profile):
    '''
    Ensures a record is absent.

    :param domain: Zone name, i.e. the domain name
    :type  domain: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = __salt__['libcloud_dns.list_zones'](profile)
    matching_zone = [z for z in zones if z.domain == domain]
    if len(matching_zone) == 0:
        return state_result(True, "Zone already absent")
    else:
        result = __salt__['libcloud_dns.delete_zone'](matching_zone[0].id, profile)
        return state_result(result, "Deleted zone")

def record_present(name, zone, type, data, profile):
    '''
    Ensures a record is present.

    :param name: Record name without the domain name (e.g. www).
                 Note: If you want to create a record for a base domain
                 name, you should specify empty string ('') for this
                 argument.
    :type  name: ``str``

    :param zone: Zone where the requested record is created, the domain name
    :type  zone: ``str``

    :param type: DNS record type (A, AAAA, ...).
    :type  type: ``str``

    :param data: Data for the record (depends on the record type).
    :type  data: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = __salt__['libcloud_dns.list_zones'](profile)
    try:
        matching_zone = [z for z in zones if z.domain == zone][0]
    except IndexError:
        return state_result(False, "Could not locate zone")
    records = __salt__['libcloud_dns.list_records'](matching_zone.id, profile)
    matching_records = [record for record in records
                        if record.name == name and
                        record.type == type and
                        record.data == data]
    if len(matching_records) == 0:
        result = __salt__['libcloud_dns.create_record'](
            name, matching_zone.id,
            type, data, profile)
        return state_result(result, "Created new record")
    else:
        return state_result(True, "Record already exists")

def record_absent(name, zone, type, data, profile):
    '''
    Ensures a record is absent.

    :param name: Record name without the domain name (e.g. www).
                 Note: If you want to create a record for a base domain
                 name, you should specify empty string ('') for this
                 argument.
    :type  name: ``str``

    :param zone: Zone where the requested record is created, the domain name
    :type  zone: ``str``

    :param type: DNS record type (A, AAAA, ...).
    :type  type: ``str``

    :param data: Data for the record (depends on the record type).
    :type  data: ``str``

    :param profile: The profile key
    :type  profile: ``str``
    '''
    zones = __salt__['libcloud_dns.list_zones'](profile)
    try:
        matching_zone = [z for z in zones if z.domain == zone][0]
    except IndexError:
        return state_result(False, "Zone could not be found")
    records = __salt__['libcloud_dns.list_records'](matching_zone.id, profile)
    matching_records = [record for record in records
                        if record.name == name and
                        record.type == type and
                        record.data == data]
    if len(matching_records) > 0:
        result = []
        for record in matching_records:
            result.append(__salt__['libcloud_dns.delete_record'](
                matching_zone.id,
                record.id,
                profile))
        return state_result(all(result), "Removed {0} records".format(len(result)))
    else:
        return state_result(True, "Records already absent")
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# documentation build configuration file, created by
# sphinx-quickstart on Fri Jun 28 22:48:23 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('../lib/python'))

# -- 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','sphinx.ext.pngmath', 'sphinx.ext.viewcode']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'HGVS'
copyright = u'2014, InVitae'

# 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.
import hgvs
version = hgvs.__version__
# The full version, including alpha/beta/rc tags.
release = str(hgvs.__version__)

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['build','static','templates','themes']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'invitae'

# 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 = None

# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['../themes']

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
html_title = '{project} {release}'.format(project = project, release = release)

# 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 = '../static/hgvs-logo.svg'

# 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 = '../static/favicon.ico'

# 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 = {
    '**':  ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html', 'instlogo.html']
    }

# 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 = 'HGVSdoc'

# -- 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', 'HGVS.tex', u'HGVS Documentation',
   u'Reece Hart', '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', 'uta', u'HGVS Documentation',
     [u'Reece Hart'], 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', 'HGVS', u'HGVS Documentation',
   u'Reece Hart', 'HGVS', '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'

# Example configuration for intersphinx: refer to the Python standard library.
# intersphinx_mapping = {'http://docs.python.org/': None}
<commit_msg>updated sphinx doc/source/conf.py<commit_after>import sys, os

import hgvs
version = hgvs.__version__
release = str(hgvs.__version__)

project = u'HGVS'
copyright = u'2014, InVitae'

source_suffix = '.rst'
master_doc = 'index'
exclude_patterns = ['build','static','templates','themes']
pygments_style = 'sphinx'
templates_path = ['templates']

html_theme = 'invitae'
html_theme_path = ['../themes']
html_title = '{project} {release}'.format(project = project, release = release)
html_logo = '../static/hgvs-logo.svg'
html_favicon = '../static/favicon.ico'
html_static_path = ['../static']

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.coverage',
    'sphinx.ext.intersphinx',
    'sphinx.ext.pngmath',
    'sphinx.ext.todo',
    'sphinx.ext.viewcode'
    ]

extlinks={
    'issue': ('https://bitbucket.org/invitae/hgvs/issue/%s' 'HGVS issue '),
    }

intersphinx_mapping = {
    'http://docs.python.org/': None,
    }

latex_documents = [
  ('index', 'HGVS.tex', u'HGVS Documentation', u'HGVS Contributors', 'manual'),
]

man_pages = [
    ('index', 'uta', u'HGVS Documentation', [u'HGVS Contributors'], 1)
]

texinfo_documents = [
  ('index', 'HGVS', u'HGVS Documentation',
   u'HGVS Contributors', 'HGVS', 'One line description of project.',
   'Miscellaneous'),
]
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# NiftyNet documentation build configuration file, created by
# sphinx-quickstart on Wed Aug 30 14:13:50 2017.
#
# 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 subprocess
import os
import sys
import pip
import shutil

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

working_dir = os.path.abspath(os.path.dirname(__file__))
root_dir_rel = os.path.join('..', '..')
root_dir_abs = os.path.abspath(root_dir_rel)
module_path = root_dir_abs
sys.path.insert(0, module_path)
logo_file = 'niftynet-logo.png'
logo_path_abs = os.path.join(root_dir_abs, logo_file)
static_images_folder = 'images'
logo_path = os.path.join('..', '..', logo_file)

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = [
    'tests',
    'run_*',
    'net_*',
    'setup.py',
]

# 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".
static_folder = '_static'
html_static_path = [static_folder]

def generate_apidocs(*args):
    """Generate API docs automatically by trawling the available modules"""
    # SimpleITK is an optional dependency, which is why it doesn't get
    # installed automatically via the requirements file installation hook
    # on ReadTheDocs.
    pip.main(['install', 'simpleitk'])

    # Copy logo to static sub-folder for RTD to be able to locate it
    static_images_path = os.path.join(static_folder, static_images_folder)
    if not os.path.exists(static_images_path):
        os.mkdir(static_images_path)
    shutil.copy(logo_path_abs, static_images_path)

    global working_dir, module_path
    output_path = working_dir
    apidoc_command_path = 'sphinx-apidoc'
    if hasattr(sys, 'real_prefix'):  # called from a virtualenv
        apidoc_command_path = os.path.join(sys.prefix, 'bin', 'sphinx-apidoc')
        apidoc_command_path = os.path.abspath(apidoc_command_path)
    subprocess.check_call(
        [apidoc_command_path, '-o', output_path, module_path] +
        [os.path.join(root_dir_abs, pattern) for pattern in exclude_patterns])

def setup(app):
    # Hook to allow for automatic generation of API docs
    # before doc deployment begins.
    app.connect('builder-inited', generate_apidocs)

# -- 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.imgmath']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'NiftyNet'
copyright = u'2017, NiftyNet Consortium'
author = u'NiftyNet Consortium'

# 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 = u''
# The full version, including alpha/beta/rc tags.
release = u''

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'classic'

# 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.
#
nn_black = '#0A0A0A'
nn_red = '#CC0000'
nn_gray = '#DDDDDD'
nn_dark_red = '#AE1111'
nn_white = '#FEFEFE'
html_theme_options = {
    'footerbgcolor': nn_gray,
    'footertextcolor': nn_black,
    'sidebarbgcolor': nn_white,
    'sidebartextcolor': nn_black,
    'sidebarlinkcolor': nn_red,
    'relbarbgcolor': nn_white,
    'relbartextcolor': nn_black,
    'relbarlinkcolor': nn_red,
    'bgcolor': nn_white,
    'textcolor': nn_black,
    'linkcolor': nn_red,
    'visitedlinkcolor': nn_dark_red,
    'headbgcolor': nn_white,
    'headtextcolor': nn_black,
    'headlinkcolor': nn_red,
    'codebgcolor': nn_gray,
    'codetextcolor': nn_black,
    'stickysidebar': 'true',
}

html_logo = logo_path

# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'NiftyNetdoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'NiftyNet.tex', u'NiftyNet Documentation',
     u'NiftyNet Consortium', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'niftynet', u'NiftyNet Documentation',
     [author], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'NiftyNet', u'NiftyNet Documentation',
     author, 'NiftyNet', 'One line description of project.',
     'Miscellaneous'),
]

<commit_msg>Issue #112: using '--separate' option to sphinx-apidoc to have separate page for each module<commit_after># -*- coding: utf-8 -*-
#
# NiftyNet documentation build configuration file, created by
# sphinx-quickstart on Wed Aug 30 14:13:50 2017.
#
# 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 subprocess
import os
import sys
import pip
import shutil

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

working_dir = os.path.abspath(os.path.dirname(__file__))
root_dir_rel = os.path.join('..', '..')
root_dir_abs = os.path.abspath(root_dir_rel)
module_path = root_dir_abs
sys.path.insert(0, module_path)
logo_file = 'niftynet-logo.png'
logo_path_abs = os.path.join(root_dir_abs, logo_file)
static_images_folder = 'images'
logo_path = os.path.join('..', '..', logo_file)

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = [
    'tests',
    'run_*',
    'net_*',
    'setup.py',
]

# 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".
static_folder = '_static'
html_static_path = [static_folder]

def generate_apidocs(*args):
    """Generate API docs automatically by trawling the available modules"""
    # SimpleITK is an optional dependency, which is why it doesn't get
    # installed automatically via the requirements file installation hook
    # on ReadTheDocs.
    pip.main(['install', 'simpleitk'])

    # Copy logo to static sub-folder for RTD to be able to locate it
    static_images_path = os.path.join(static_folder, static_images_folder)
    if not os.path.exists(static_images_path):
        os.mkdir(static_images_path)
    shutil.copy(logo_path_abs, static_images_path)

    global working_dir, module_path
    output_path = working_dir
    apidoc_command_path = 'sphinx-apidoc'
    if hasattr(sys, 'real_prefix'):  # called from a virtualenv
        apidoc_command_path = os.path.join(sys.prefix, 'bin', 'sphinx-apidoc')
        apidoc_command_path = os.path.abspath(apidoc_command_path)
    subprocess.check_call(
        [apidoc_command_path, '--separate', '-o', output_path, module_path] +
        [os.path.join(root_dir_abs, pattern) for pattern in exclude_patterns])

def setup(app):
    # Hook to allow for automatic generation of API docs
    # before doc deployment begins.
    app.connect('builder-inited', generate_apidocs)

# -- 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.imgmath']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'NiftyNet'
copyright = u'2017, NiftyNet Consortium'
author = u'NiftyNet Consortium'

# 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 = u''
# The full version, including alpha/beta/rc tags.
release = u''

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'classic'

# 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.
#
nn_black = '#0A0A0A'
nn_red = '#CC0000'
nn_gray = '#DDDDDD'
nn_dark_red = '#AE1111'
nn_white = '#FEFEFE'
html_theme_options = {
    'footerbgcolor': nn_gray,
    'footertextcolor': nn_black,
    'sidebarbgcolor': nn_white,
    'sidebartextcolor': nn_black,
    'sidebarlinkcolor': nn_red,
    'relbarbgcolor': nn_white,
    'relbartextcolor': nn_black,
    'relbarlinkcolor': nn_red,
    'bgcolor': nn_white,
    'textcolor': nn_black,
    'linkcolor': nn_red,
    'visitedlinkcolor': nn_dark_red,
    'headbgcolor': nn_white,
    'headtextcolor': nn_black,
    'headlinkcolor': nn_red,
    'codebgcolor': nn_gray,
    'codetextcolor': nn_black,
    'stickysidebar': 'true',
}

html_logo = logo_path

# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'NiftyNetdoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'NiftyNet.tex', u'NiftyNet Documentation',
     u'NiftyNet Consortium', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'niftynet', u'NiftyNet Documentation',
     [author], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'NiftyNet', u'NiftyNet Documentation',
     author, 'NiftyNet', 'One line description of project.',
     'Miscellaneous'),
]

<|endoftext|>


<commit_before>import asyncio
import os
import struct

import libnacl
import libnacl.public
import websockets

from . import util
from .common import (
    KEEP_ALIVE_INTERVAL_DEFAULT,
    KEEP_ALIVE_INTERVAL_MIN,
    KEEP_ALIVE_TIMEOUT,
    KEY_LENGTH,
    AddressType,
    OverflowSentinel,
    available_slot_range,
    is_initiator_id,
    is_responder_id,
)
from .exception import (
    Disconnected,
    MessageError,
    MessageFlowError,
    SlotsFullError,
)
from .message import unpack

__all__ = (
    'Path',
    'PathClient',
    'Protocol',
)

class Path:
    __slots__ = ('_slots', 'log', 'initiator_key', 'number')

    def __init__(self, initiator_key, number):
        self._slots = {id_: None for id_ in available_slot_range()}
        self.log = util.get_logger('path.{}'.format(number))
        self.initiator_key = initiator_key
        self.number = number

    @property
    def empty(self):
        """
        Return `True` in case the path is empty. A call to this property
        will also remove clients from the path whose connections are
        closed but have not been removed from the path. (However, in
        case that the path is not empty, this property does not ensure
        that all disconnected clients will be removed.)
        """
        for client in self._slots.values():
            if client is not None:
                if client.connection_closed.done():
                    self.remove_client(client)
                    self.log.warning('Removed dead client {}', client)
                else:
                    return False
        return True

    def get_initiator(self):
        """
        Return the initiator's :class:`PathClient` instance or `None`.
        """
        return self._slots.get(AddressType.initiator)

    def set_initiator(self, initiator):
        """
        Set the initiator's :class:`PathClient` instance.

        Arguments:
            - `initiator`: A :class:`PathClient` instance.

        Return the previously set initiator or `None`.
        """
        previous_initiator = self._slots.get(AddressType.initiator)
        self._slots[AddressType.initiator] = initiator
        self.log.debug('Set initiator {}', initiator)
        # Update initiator's log name
        initiator.update_log_name(AddressType.initiator)
        # Authenticated, assign id
        initiator.authenticated = True
        initiator.id = AddressType.initiator
        # Return previous initiator
        return previous_initiator

    def get_responder(self, id_):
        """
        Return a responder's :class:`PathClient` instance or `None`.

        Arguments:
            - `id_`: The identifier of the responder.

        Raises :exc:`ValueError` if `id_` is not a valid responder
        identifier.
        """
        if not is_responder_id(id_):
            raise ValueError('Invalid responder identifier')
        return self._slots.get(id_)

    def get_responder_ids(self):
        """
        Return a list of responder's identifiers (slots).
        """
        return [id_ for id_, responder in self._slots.items()
                if is_responder_id(id_) and responder is not None]

    def add_responder(self, responder):
        """
        Set a responder's :class:`PathClient` instance.

        Arguments:
            - `client`: A :class:`PathClient` instance.

        Raises :exc:`SlotsFullError` if no free slot exists on the path.

        Return the assigned slot identifier.
        """
        for id_, client in self._slots.items():
            if is_responder_id(id_) and client is None:
                self._slots[id_] = responder
                self.log.debug('Added responder {}', responder)
                # Update responder's log name
                responder.update_log_name(id_)
                # Authenticated, set and return assigned slot id
                responder.authenticated = True
                responder.id = id_
                return id_
        raise SlotsFullError('No free slots on path')

    def remove_client(self, client):
        """
        Remove a client (initiator or responder) from the
        :class:`Path`.

        Arguments:
             - `client`: The :class:`PathClient` instance.

        Raises :exc:`ValueError` in case the client provided an
        invalid slot identifier.
        """

        if not client.authenticated:
            # Client has not been authenticated. Nothing to do.
            return
        id_ = client.id

        # Get client instance
        try:
            slot_client = self._slots[id_]
        except KeyError:
            raise ValueError('Invalid slot identifier: {}'.format(id_))

        # Compare client instances
        if client != slot_client:
            # Note: This is absolutely fine and happens when another initiator
            #        takes the place of a previous initiator.
            return

        # Remove client from slot
        self._slots[id_] = None
        self.log.debug('Removed {}', 'initiator' if is_initiator_id(id_) else 'responder')

class PathClient:
    __slots__ = (
        '_loop',
        '_connection',
        '_client_key',
        '_server_permanent_key',
        '_server_session_key',
        '_sequence_number_out',
        '_sequence_number_in',
        '_cookie_out',
        '_cookie_in',
        '_combined_sequence_number_out',
        '_combined_sequence_number_in',
        '_box',
        '_sign_box',
        '_id',
        '_keep_alive_interval',
        'log',
        'type',
        'authenticated',
        'keep_alive_timeout',
        'keep_alive_pings',
        '_task_queue'
    )

    def __init__(
            self, connection, path_number, initiator_key, server_permanent_key,
            server_session_key=None, loop=None
    ):
        self._loop = asyncio.get_event_loop() if loop is None else loop
        self._connection = connection
        self._client_key = initiator_key
        self._server_permanent_key = server_permanent_key
        self._server_session_key = server_session_key
        self._cookie_out = None
        self._cookie_in = None
        self._combined_sequence_number_out = None
        self._combined_sequence_number_in = None
        self._box = None
        self._sign_box = None
        self._id = AddressType.server
        self._keep_alive_interval = KEEP_ALIVE_INTERVAL_DEFAULT
        self.log = util.get_logger('path.{}.client.{:x}'.format(path_number, id(self)))
        self.type = None
        self.authenticated = False
        self.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
        self.keep_alive_pings = 0

        # Queue for tasks to be run on the client (relay messages, closing, ...)
        self._task_queue = asyncio.Queue(loop=self._loop)

    def __str__(self):
        type_ = self.type
        if type_ is None:
            type_ = 'undetermined'
        return 'PathClient(role=0x{:02x}, id={}, at={})'.format(
            type_, self._id, hex(id(self)))

    @property
    def connection_closed(self):
        """
        Return the 'connection_closed' future of the underlying
        WebSocket connection.
        """
        return self._connection.connection_closed

    @property
    def id(self):
        """
        Return the assigned id on the :class:`Path`.
        """
        return self._id

    @id.setter
    def id(self, id_):
        """
        Assign the id. Only :class:`Path` may set the id!
        """
        self._id = id_
        self.log.debug('Assigned id: {}', id_)

    @property
    def keep_alive_interval(self):
        """
        Return the currently set keep alive interval.
        """
        return self._keep_alive_interval

    @keep_alive_interval.setter
    def keep_alive_interval(self, interval):
        """
        Assign a new keep alive interval. Will ignore values less than
        `KEEP_ALIVE_INTERVAL_MIN`.
        """
        if interval >= KEEP_ALIVE_INTERVAL_MIN:
            self._keep_alive_interval = interval

    @property
    def client_key(self):
        """
        Return the client's permanent key as :class:`bytes`.

        .. warning:: This is the initiator's key at default if no other
                     client key has been set!
        """
        return self._client_key

    @property
    def server_key(self):
        """
        Return the server's session :class:`libnacl.public.SecretKey`
        instance.
        """
        if self._server_session_key is None:
            self._server_session_key = libnacl.public.SecretKey()
        return self._server_session_key

    @property
    def box(self):
        """
        Return the session's :class:`libnacl.public.Box` instance.
        """
        if self._box is None:
            self._box = libnacl.public.Box(self.server_key, self._client_key)
        return self._box

    @property
    def sign_box(self):
        """
        Return the :class:`libnacl.public.Box` instance that is used for
        signing the keys in the 'server-auth' message.
        """
        if self._sign_box is None:
            self._sign_box = libnacl.public.Box(
                self._server_permanent_key, self._client_key)
        return self._sign_box

    @property
    def cookie_out(self):
        """
        Return the cookie of the server (outgoing messages).
        """
        if self._cookie_out is None:
            self._cookie_out = os.urandom(16)
        return self._cookie_out

    @property
    def cookie_in(self):
        """
        Return the cookie of the client (incoming messages).
        """
        return self._cookie_in

    @property
    def combined_sequence_number_out(self):
        """
        Return the pending combined sequence number of the server
        (outgoing messages).
        """
        if self._combined_sequence_number_out is None:
            # Initialise the trailing 32 bits of the uint48 number with random bits
            initial_number, *_ = struct.unpack('!Q', b'\x00' * 4 + os.urandom(4))
            self._combined_sequence_number_out = initial_number
        return self._combined_sequence_number_out

    @combined_sequence_number_out.setter
    def combined_sequence_number_out(self, combined_sequence_number_out):
        """
        Update the combined sequence number of the server (outgoing
        messages). Will set the number to OverflowSentinel in case
        the number would overflow a 48-bit unsigned integer.
        """
        csn = self._validate_combined_sequence_number(combined_sequence_number_out)
        self._combined_sequence_number_out = csn

    @property
    def combined_sequence_number_in(self):
        """
        Return the pending combined sequence number of the client
        (incoming messages).
        """
        return self._combined_sequence_number_in

    @combined_sequence_number_in.setter
    def combined_sequence_number_in(self, combined_sequence_number_in):
        """
        Update the combined sequence number of the client (incoming
        messages). Will set the number to OverflowSentinel in case
        the number would overflow a 48-bit unsigned integer.
        """
        csn = self._validate_combined_sequence_number(combined_sequence_number_in)
        self._combined_sequence_number_in = csn

    @staticmethod
    def _validate_combined_sequence_number(combined_sequence_number):
        """
        Validate a combined sequence number.

        Return OverflowSentinel in case the number would overflow a
        48-bit unsigned integer, otherwise the passed sequence number.
        """
        if combined_sequence_number & 0xf000000000000 != 0:
            return OverflowSentinel
        else:
            return combined_sequence_number

    def set_client_key(self, public_key):
        """
        Set the public key of the client and update the internal box.

        Arguments:
            - `public_key`: A :class:`libnacl.public.PublicKey`.
        """
        self._client_key = public_key
        self._box = libnacl.public.Box(self.server_key, public_key)
        self.log.debug('Client key updated')

    def update_log_name(self, slot_id):
        """
        Update the logger's name by the assigned slot identifier.

        Arguments:
            - `slot_id`: The slot identifier of the client.
        """
        self.log.name += '.0x{:02x}'.format(slot_id)

    def valid_cookie(self, cookie_in):
        """
        Return `True` if the 16 byte cookie is the valid cookie of the
        client (or the cookie has not been set, yet).
        """
        if self.cookie_in is None:
            # Ensure that the client uses another cookie than we do
            if cookie_in == self.cookie_out:
                self.log.notice('Server and client cookies are the same')
                return False

            # First message: Set cookie
            self._cookie_in = cookie_in
            return True
        else:
            if cookie_in != self.cookie_in:
                self.log.notice('Client sent wrong cookie')
                return False
            return True

    def validate_combined_sequence_number(self, combined_sequence_number_in):
        """
        Set or validate the combined sequence number for incoming
        messages from the client.

        Raises:
            - :exc:`MessageError` in case this is the first message
              from the client and the leading 16 bits of the combined
              sequence number are not `0`.
            - :exc:`MessageError` in case the 6 byte combined sequence
              number is not the expected sequence number of the client.
            - :exc:`MessageFlowError` in case no messages can be
              received from the client as the combined sequence number
              has reset to `0`.
        """
        if self.combined_sequence_number_in is None:
            # Ensure that the leading 16 bits are 0
            if combined_sequence_number_in & 0xffff00000000 != 0:
                raise MessageError('Invalid sequence number, leading 16 bits are not 0')

            # First message: Set combined sequence number
            self._combined_sequence_number_in = combined_sequence_number_in

        # Ensure that the incoming CSN counter did not overflow
        if self.combined_sequence_number_in == OverflowSentinel:
            raise MessageFlowError(('Cannot receive any more messages, due to a '
                                    'sequence number counter overflow'))

        # Check that the CSN matches the expected CSN
        if combined_sequence_number_in != self.combined_sequence_number_in:
            raise MessageError('Invalid sequence number, expected {}, got {}'.format(
                self.combined_sequence_number_in, combined_sequence_number_in
            ))

    def p2p_allowed(self, destination_type):
        """
        Return `True` if :class:`RawMessage` instances are allowed and
        can be sent to the requested :class:`AddressType`.
        """
        return self.authenticated and self.type != destination_type

    @asyncio.coroutine
    def enqueue_task(self, coroutine_or_task):
        """
        Enqueue a coroutine or task into the task queue of the
        client.

        Arguments:
            - `coroutine_or_task`: A coroutine or a
              :class:`asyncio.Task`.
        """
        yield from self._task_queue.put(coroutine_or_task)

    @asyncio.coroutine
    def dequeue_task(self):
        """
        Dequeue and return a coroutine or task from the task queue of
        the client.

        Shall only be called from the client's :class:`Protocol`
        instance.
        """
        return (yield from self._task_queue.get())

    @asyncio.coroutine
    def send(self, message):
        """
        Disconnected
        MessageError
        MessageFlowError
        """
        # Pack
        self.log.debug('Packing message: {}', message.type)
        data = message.pack(self)
        self.log.trace('server >> {}', message)

        # Send data
        self.log.debug('Sending message')
        try:
            yield from self._connection.send(data)
        except websockets.ConnectionClosed as exc:
            self.log.debug('Connection closed while sending')
            raise Disconnected() from exc

    @asyncio.coroutine
    def receive(self):
        """
        Disconnected
        """
        # Receive data
        try:
            data = yield from self._connection.recv()
        except websockets.ConnectionClosed as exc:
            self.log.debug('Connection closed while receiving')
            raise Disconnected() from exc
        self.log.debug('Received message')

        # Unpack data and return
        message = unpack(self, data)
        self.log.debug('Unpacked message: {}', message.type)
        self.log.trace('server << {}', message)
        return message

    @asyncio.coroutine
    def ping(self):
        """
        Disconnected
        """
        self.log.debug('Sending ping')
        try:
            return (yield from self._connection.ping())
        except websockets.ConnectionClosed as exc:
            self.log.debug('Connection closed while pinging')
            raise Disconnected() from exc

    @asyncio.coroutine
    def close(self, code=1000):
        # Note: We are not sending a reason for security reasons.
        yield from self._connection.close(code=code)

class Protocol:
    PATH_LENGTH = KEY_LENGTH * 2
<commit_msg>Adjust log level for dead clients<commit_after>import asyncio
import os
import struct

import libnacl
import libnacl.public
import websockets

from . import util
from .common import (
    KEEP_ALIVE_INTERVAL_DEFAULT,
    KEEP_ALIVE_INTERVAL_MIN,
    KEEP_ALIVE_TIMEOUT,
    KEY_LENGTH,
    AddressType,
    OverflowSentinel,
    available_slot_range,
    is_initiator_id,
    is_responder_id,
)
from .exception import (
    Disconnected,
    MessageError,
    MessageFlowError,
    SlotsFullError,
)
from .message import unpack

__all__ = (
    'Path',
    'PathClient',
    'Protocol',
)

class Path:
    __slots__ = ('_slots', 'log', 'initiator_key', 'number')

    def __init__(self, initiator_key, number):
        self._slots = {id_: None for id_ in available_slot_range()}
        self.log = util.get_logger('path.{}'.format(number))
        self.initiator_key = initiator_key
        self.number = number

    @property
    def empty(self):
        """
        Return `True` in case the path is empty. A call to this property
        will also remove clients from the path whose connections are
        closed but have not been removed from the path. (However, in
        case that the path is not empty, this property does not ensure
        that all disconnected clients will be removed.)
        """
        for client in self._slots.values():
            if client is not None:
                if client.connection_closed.done():
                    self.remove_client(client)
                    self.log.notice('Removed dead client {}', client)
                else:
                    return False
        return True

    def get_initiator(self):
        """
        Return the initiator's :class:`PathClient` instance or `None`.
        """
        return self._slots.get(AddressType.initiator)

    def set_initiator(self, initiator):
        """
        Set the initiator's :class:`PathClient` instance.

        Arguments:
            - `initiator`: A :class:`PathClient` instance.

        Return the previously set initiator or `None`.
        """
        previous_initiator = self._slots.get(AddressType.initiator)
        self._slots[AddressType.initiator] = initiator
        self.log.debug('Set initiator {}', initiator)
        # Update initiator's log name
        initiator.update_log_name(AddressType.initiator)
        # Authenticated, assign id
        initiator.authenticated = True
        initiator.id = AddressType.initiator
        # Return previous initiator
        return previous_initiator

    def get_responder(self, id_):
        """
        Return a responder's :class:`PathClient` instance or `None`.

        Arguments:
            - `id_`: The identifier of the responder.

        Raises :exc:`ValueError` if `id_` is not a valid responder
        identifier.
        """
        if not is_responder_id(id_):
            raise ValueError('Invalid responder identifier')
        return self._slots.get(id_)

    def get_responder_ids(self):
        """
        Return a list of responder's identifiers (slots).
        """
        return [id_ for id_, responder in self._slots.items()
                if is_responder_id(id_) and responder is not None]

    def add_responder(self, responder):
        """
        Set a responder's :class:`PathClient` instance.

        Arguments:
            - `client`: A :class:`PathClient` instance.

        Raises :exc:`SlotsFullError` if no free slot exists on the path.

        Return the assigned slot identifier.
        """
        for id_, client in self._slots.items():
            if is_responder_id(id_) and client is None:
                self._slots[id_] = responder
                self.log.debug('Added responder {}', responder)
                # Update responder's log name
                responder.update_log_name(id_)
                # Authenticated, set and return assigned slot id
                responder.authenticated = True
                responder.id = id_
                return id_
        raise SlotsFullError('No free slots on path')

    def remove_client(self, client):
        """
        Remove a client (initiator or responder) from the
        :class:`Path`.

        Arguments:
             - `client`: The :class:`PathClient` instance.

        Raises :exc:`ValueError` in case the client provided an
        invalid slot identifier.
        """

        if not client.authenticated:
            # Client has not been authenticated. Nothing to do.
            return
        id_ = client.id

        # Get client instance
        try:
            slot_client = self._slots[id_]
        except KeyError:
            raise ValueError('Invalid slot identifier: {}'.format(id_))

        # Compare client instances
        if client != slot_client:
            # Note: This is absolutely fine and happens when another initiator
            #        takes the place of a previous initiator.
            return

        # Remove client from slot
        self._slots[id_] = None
        self.log.debug('Removed {}', 'initiator' if is_initiator_id(id_) else 'responder')

class PathClient:
    __slots__ = (
        '_loop',
        '_connection',
        '_client_key',
        '_server_permanent_key',
        '_server_session_key',
        '_sequence_number_out',
        '_sequence_number_in',
        '_cookie_out',
        '_cookie_in',
        '_combined_sequence_number_out',
        '_combined_sequence_number_in',
        '_box',
        '_sign_box',
        '_id',
        '_keep_alive_interval',
        'log',
        'type',
        'authenticated',
        'keep_alive_timeout',
        'keep_alive_pings',
        '_task_queue'
    )

    def __init__(
            self, connection, path_number, initiator_key, server_permanent_key,
            server_session_key=None, loop=None
    ):
        self._loop = asyncio.get_event_loop() if loop is None else loop
        self._connection = connection
        self._client_key = initiator_key
        self._server_permanent_key = server_permanent_key
        self._server_session_key = server_session_key
        self._cookie_out = None
        self._cookie_in = None
        self._combined_sequence_number_out = None
        self._combined_sequence_number_in = None
        self._box = None
        self._sign_box = None
        self._id = AddressType.server
        self._keep_alive_interval = KEEP_ALIVE_INTERVAL_DEFAULT
        self.log = util.get_logger('path.{}.client.{:x}'.format(path_number, id(self)))
        self.type = None
        self.authenticated = False
        self.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
        self.keep_alive_pings = 0

        # Queue for tasks to be run on the client (relay messages, closing, ...)
        self._task_queue = asyncio.Queue(loop=self._loop)

    def __str__(self):
        type_ = self.type
        if type_ is None:
            type_ = 'undetermined'
        return 'PathClient(role=0x{:02x}, id={}, at={})'.format(
            type_, self._id, hex(id(self)))

    @property
    def connection_closed(self):
        """
        Return the 'connection_closed' future of the underlying
        WebSocket connection.
        """
        return self._connection.connection_closed

    @property
    def id(self):
        """
        Return the assigned id on the :class:`Path`.
        """
        return self._id

    @id.setter
    def id(self, id_):
        """
        Assign the id. Only :class:`Path` may set the id!
        """
        self._id = id_
        self.log.debug('Assigned id: {}', id_)

    @property
    def keep_alive_interval(self):
        """
        Return the currently set keep alive interval.
        """
        return self._keep_alive_interval

    @keep_alive_interval.setter
    def keep_alive_interval(self, interval):
        """
        Assign a new keep alive interval. Will ignore values less than
        `KEEP_ALIVE_INTERVAL_MIN`.
        """
        if interval >= KEEP_ALIVE_INTERVAL_MIN:
            self._keep_alive_interval = interval

    @property
    def client_key(self):
        """
        Return the client's permanent key as :class:`bytes`.

        .. warning:: This is the initiator's key at default if no other
                     client key has been set!
        """
        return self._client_key

    @property
    def server_key(self):
        """
        Return the server's session :class:`libnacl.public.SecretKey`
        instance.
        """
        if self._server_session_key is None:
            self._server_session_key = libnacl.public.SecretKey()
        return self._server_session_key

    @property
    def box(self):
        """
        Return the session's :class:`libnacl.public.Box` instance.
        """
        if self._box is None:
            self._box = libnacl.public.Box(self.server_key, self._client_key)
        return self._box

    @property
    def sign_box(self):
        """
        Return the :class:`libnacl.public.Box` instance that is used for
        signing the keys in the 'server-auth' message.
        """
        if self._sign_box is None:
            self._sign_box = libnacl.public.Box(
                self._server_permanent_key, self._client_key)
        return self._sign_box

    @property
    def cookie_out(self):
        """
        Return the cookie of the server (outgoing messages).
        """
        if self._cookie_out is None:
            self._cookie_out = os.urandom(16)
        return self._cookie_out

    @property
    def cookie_in(self):
        """
        Return the cookie of the client (incoming messages).
        """
        return self._cookie_in

    @property
    def combined_sequence_number_out(self):
        """
        Return the pending combined sequence number of the server
        (outgoing messages).
        """
        if self._combined_sequence_number_out is None:
            # Initialise the trailing 32 bits of the uint48 number with random bits
            initial_number, *_ = struct.unpack('!Q', b'\x00' * 4 + os.urandom(4))
            self._combined_sequence_number_out = initial_number
        return self._combined_sequence_number_out

    @combined_sequence_number_out.setter
    def combined_sequence_number_out(self, combined_sequence_number_out):
        """
        Update the combined sequence number of the server (outgoing
        messages). Will set the number to OverflowSentinel in case
        the number would overflow a 48-bit unsigned integer.
        """
        csn = self._validate_combined_sequence_number(combined_sequence_number_out)
        self._combined_sequence_number_out = csn

    @property
    def combined_sequence_number_in(self):
        """
        Return the pending combined sequence number of the client
        (incoming messages).
        """
        return self._combined_sequence_number_in

    @combined_sequence_number_in.setter
    def combined_sequence_number_in(self, combined_sequence_number_in):
        """
        Update the combined sequence number of the client (incoming
        messages). Will set the number to OverflowSentinel in case
        the number would overflow a 48-bit unsigned integer.
        """
        csn = self._validate_combined_sequence_number(combined_sequence_number_in)
        self._combined_sequence_number_in = csn

    @staticmethod
    def _validate_combined_sequence_number(combined_sequence_number):
        """
        Validate a combined sequence number.

        Return OverflowSentinel in case the number would overflow a
        48-bit unsigned integer, otherwise the passed sequence number.
        """
        if combined_sequence_number & 0xf000000000000 != 0:
            return OverflowSentinel
        else:
            return combined_sequence_number

    def set_client_key(self, public_key):
        """
        Set the public key of the client and update the internal box.

        Arguments:
            - `public_key`: A :class:`libnacl.public.PublicKey`.
        """
        self._client_key = public_key
        self._box = libnacl.public.Box(self.server_key, public_key)
        self.log.debug('Client key updated')

    def update_log_name(self, slot_id):
        """
        Update the logger's name by the assigned slot identifier.

        Arguments:
            - `slot_id`: The slot identifier of the client.
        """
        self.log.name += '.0x{:02x}'.format(slot_id)

    def valid_cookie(self, cookie_in):
        """
        Return `True` if the 16 byte cookie is the valid cookie of the
        client (or the cookie has not been set, yet).
        """
        if self.cookie_in is None:
            # Ensure that the client uses another cookie than we do
            if cookie_in == self.cookie_out:
                self.log.notice('Server and client cookies are the same')
                return False

            # First message: Set cookie
            self._cookie_in = cookie_in
            return True
        else:
            if cookie_in != self.cookie_in:
                self.log.notice('Client sent wrong cookie')
                return False
            return True

    def validate_combined_sequence_number(self, combined_sequence_number_in):
        """
        Set or validate the combined sequence number for incoming
        messages from the client.

        Raises:
            - :exc:`MessageError` in case this is the first message
              from the client and the leading 16 bits of the combined
              sequence number are not `0`.
            - :exc:`MessageError` in case the 6 byte combined sequence
              number is not the expected sequence number of the client.
            - :exc:`MessageFlowError` in case no messages can be
              received from the client as the combined sequence number
              has reset to `0`.
        """
        if self.combined_sequence_number_in is None:
            # Ensure that the leading 16 bits are 0
            if combined_sequence_number_in & 0xffff00000000 != 0:
                raise MessageError('Invalid sequence number, leading 16 bits are not 0')

            # First message: Set combined sequence number
            self._combined_sequence_number_in = combined_sequence_number_in

        # Ensure that the incoming CSN counter did not overflow
        if self.combined_sequence_number_in == OverflowSentinel:
            raise MessageFlowError(('Cannot receive any more messages, due to a '
                                    'sequence number counter overflow'))

        # Check that the CSN matches the expected CSN
        if combined_sequence_number_in != self.combined_sequence_number_in:
            raise MessageError('Invalid sequence number, expected {}, got {}'.format(
                self.combined_sequence_number_in, combined_sequence_number_in
            ))

    def p2p_allowed(self, destination_type):
        """
        Return `True` if :class:`RawMessage` instances are allowed and
        can be sent to the requested :class:`AddressType`.
        """
        return self.authenticated and self.type != destination_type

    @asyncio.coroutine
    def enqueue_task(self, coroutine_or_task):
        """
        Enqueue a coroutine or task into the task queue of the
        client.

        Arguments:
            - `coroutine_or_task`: A coroutine or a
              :class:`asyncio.Task`.
        """
        yield from self._task_queue.put(coroutine_or_task)

    @asyncio.coroutine
    def dequeue_task(self):
        """
        Dequeue and return a coroutine or task from the task queue of
        the client.

        Shall only be called from the client's :class:`Protocol`
        instance.
        """
        return (yield from self._task_queue.get())

    @asyncio.coroutine
    def send(self, message):
        """
        Disconnected
        MessageError
        MessageFlowError
        """
        # Pack
        self.log.debug('Packing message: {}', message.type)
        data = message.pack(self)
        self.log.trace('server >> {}', message)

        # Send data
        self.log.debug('Sending message')
        try:
            yield from self._connection.send(data)
        except websockets.ConnectionClosed as exc:
            self.log.debug('Connection closed while sending')
            raise Disconnected() from exc

    @asyncio.coroutine
    def receive(self):
        """
        Disconnected
        """
        # Receive data
        try:
            data = yield from self._connection.recv()
        except websockets.ConnectionClosed as exc:
            self.log.debug('Connection closed while receiving')
            raise Disconnected() from exc
        self.log.debug('Received message')

        # Unpack data and return
        message = unpack(self, data)
        self.log.debug('Unpacked message: {}', message.type)
        self.log.trace('server << {}', message)
        return message

    @asyncio.coroutine
    def ping(self):
        """
        Disconnected
        """
        self.log.debug('Sending ping')
        try:
            return (yield from self._connection.ping())
        except websockets.ConnectionClosed as exc:
            self.log.debug('Connection closed while pinging')
            raise Disconnected() from exc

    @asyncio.coroutine
    def close(self, code=1000):
        # Note: We are not sending a reason for security reasons.
        yield from self._connection.close(code=code)

class Protocol:
    PATH_LENGTH = KEY_LENGTH * 2
<|endoftext|>