<commit_before># coding: utf-8
"""Utilities for installing Javascript extensions for the notebook"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from __future__ import print_function

import os
import shutil
import sys
import tarfile
import zipfile
import uuid
from os.path import basename, join as pjoin

# Deferred imports
try:
    from urllib.parse import urlparse  # Py3
    from urllib.request import urlretrieve
except ImportError:
    from urlparse import urlparse
    from urllib import urlretrieve

from IPython.utils.path import get_ipython_dir, ensure_dir_exists
from IPython.utils.py3compat import string_types, cast_unicode_py2
from IPython.utils.tempdir import TemporaryDirectory

class ArgumentConflict(ValueError):
    pass

# Packagers: modify the next block if you store system-installed nbextensions elsewhere (unlikely)
SYSTEM_NBEXTENSIONS_DIRS = []

if os.name == 'nt':
    programdata = os.environ.get('PROGRAMDATA', None)
    if programdata: # PROGRAMDATA is not defined by default on XP.
        SYSTEM_NBEXTENSIONS_DIRS = [pjoin(programdata, 'jupyter', 'nbextensions')]
    prefixes = []
else:
    prefixes = [os.path.sep + pjoin('usr', 'local'), os.path.sep + 'usr']

# add sys.prefix at the front
if sys.prefix not in prefixes:
    prefixes.insert(0, sys.prefix)

for prefix in prefixes:
    nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
    if nbext not in SYSTEM_NBEXTENSIONS_DIRS:
        SYSTEM_NBEXTENSIONS_DIRS.append(nbext)

if os.name == 'nt':
    # PROGRAMDATA
    SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-1]
else:
    # /usr/local
    SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-2]

def _should_copy(src, dest, verbose=1):
    """should a file be copied?"""
    if not os.path.exists(dest):
        return True
    if os.stat(src).st_mtime - os.stat(dest).st_mtime > 1e-6:
        # we add a fudge factor to work around a bug in python 2.x
        # that was fixed in python 3.x: http://bugs.python.org/issue12904
        if verbose >= 2:
            print("%s is out of date" % dest)
        return True
    if verbose >= 2:
        print("%s is up to date" % dest)
    return False

def _maybe_copy(src, dest, verbose=1):
    """copy a file if it needs updating"""
    if _should_copy(src, dest, verbose):
        if verbose >= 1:
            print("copying %s -> %s" % (src, dest))
        shutil.copy2(src, dest)

def _safe_is_tarfile(path):
    """safe version of is_tarfile, return False on IOError"""
    try:
        return tarfile.is_tarfile(path)
    except IOError:
        return False

def _get_nbext_dir(nbextensions_dir=None, user=False, prefix=None):
    """Return the nbextension directory specified"""
    if sum(map(bool, [user, prefix, nbextensions_dir])) > 1:
        raise ArgumentConflict("Cannot specify more than one of user, prefix, or nbextensions_dir.")
    if user:
        nbext = pjoin(get_ipython_dir(), u'nbextensions')
    else:
        if prefix:
            nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
        elif nbextensions_dir:
            nbext = nbextensions_dir
        else:
            nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
    return nbext

def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
    """Check whether nbextension files have been installed

    Returns True if all files are found, False if any are missing.

    Parameters
    ----------

    files : list(paths)
        a list of relative paths within nbextensions.
    user : bool [default: False]
        Whether to check the user's .ipython/nbextensions directory.
        Otherwise check a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
    prefix : str [optional]
        Specify install prefix, if it should differ from default (e.g. /usr/local).
        Will check prefix/share/jupyter/nbextensions
    nbextensions_dir : str [optional]
        Specify absolute path of nbextensions directory explicitly.
    """
    nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
    # make sure nbextensions dir exists
    if not os.path.exists(nbext):
        return False

    if isinstance(files, string_types):
        # one file given, turn it into a list
        files = [files]

    return all(os.path.exists(pjoin(nbext, f)) for f in files)

def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1):
    """Install a Javascript extension for the notebook

    Stages files and/or directories into the nbextensions directory.
    By default, this compares modification time, and only stages files that need updating.
    If `overwrite` is specified, matching files are purged before proceeding.

    Parameters
    ----------

    path : path to file, directory, zip or tarball archive, or URL to install
        By default, the file will be installed with its base name, so '/path/to/foo'
        will install to 'nbextensions/foo'. See the destination argument below to change this.
        Archives (zip or tarballs) will be extracted into the nbextensions directory.
    overwrite : bool [default: False]
        If True, always install the files, regardless of what may already be installed.
    symlink : bool [default: False]
        If True, create a symlink in nbextensions, rather than copying files.
        Not allowed with URLs or archives. Windows support for symlinks requires
        Vista or above, Python 3, and a permission bit which only admin users
        have by default, so don't rely on it.
    user : bool [default: False]
        Whether to install to the user's .ipython/nbextensions directory.
        Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
    prefix : str [optional]
        Specify install prefix, if it should differ from default (e.g. /usr/local).
        Will install to prefix/share/jupyter/nbextensions
    nbextensions_dir : str [optional]
        Specify absolute path of nbextensions directory explicitly.
    destination : str [optional]
        name the nbextension is installed to.  For example, if destination is 'foo', then
        the source file will be installed to 'nbextensions/foo', regardless of the source name.
        This cannot be specified if an archive is given as the source.
    verbose : int [default: 1]
        Set verbosity level. The default is 1, where file actions are printed.
        set verbose=2 for more output, or verbose=0 for silence.
    """
    nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
    # make sure nbextensions dir exists
    ensure_dir_exists(nbext)

    if isinstance(path, (list, tuple)):
        raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions")

    path = cast_unicode_py2(path)

    if path.startswith(('https://', 'http://')):
        if symlink:
            raise ValueError("Cannot symlink from URLs")
        # Given a URL, download it
        with TemporaryDirectory() as td:
            filename = urlparse(path).path.split('/')[-1]
            local_path = os.path.join(td, filename)
            if verbose >= 1:
                print("downloading %s to %s" % (path, local_path))
            urlretrieve(path, local_path)
            # now install from the local copy
            install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose)
    elif path.endswith('.zip') or _safe_is_tarfile(path):
        if symlink:
            raise ValueError("Cannot symlink from archives")
        if destination:
            raise ValueError("Cannot give destination for archives")
        if verbose >= 1:
            print("extracting %s to %s" % (path, nbext))

        if path.endswith('.zip'):
            archive = zipfile.ZipFile(path)
        elif _safe_is_tarfile(path):
            archive = tarfile.open(path)
        archive.extractall(nbext)
        archive.close()
    else:
        if not destination:
            destination = basename(path)
        destination = cast_unicode_py2(destination)
        full_dest = pjoin(nbext, destination)
        if overwrite and os.path.lexists(full_dest):
            if verbose >= 1:
                print("removing %s" % full_dest)
            if os.path.isdir(full_dest) and not os.path.islink(full_dest):
                shutil.rmtree(full_dest)
            else:
                os.remove(full_dest)

        if symlink:
            path = os.path.abspath(path)
            if not os.path.exists(full_dest):
                if verbose >= 1:
                    print("symlink %s -> %s" % (full_dest, path))
                os.symlink(path, full_dest)
        elif os.path.isdir(path):
            path = pjoin(os.path.abspath(path), '') # end in path separator
            for parent, dirs, files in os.walk(path):
                dest_dir = pjoin(full_dest, parent[len(path):])
                if not os.path.exists(dest_dir):
                    if verbose >= 2:
                        print("making directory %s" % dest_dir)
                    os.makedirs(dest_dir)
                for file in files:
                    src = pjoin(parent, file)
                    # print("%r, %r" % (dest_dir, file))
                    dest_file = pjoin(dest_dir, file)
                    _maybe_copy(src, dest_file, verbose)
        else:
            src = path
            _maybe_copy(src, full_dest, verbose)

#----------------------------------------------------------------------
# install nbextension app
#----------------------------------------------------------------------

from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
from IPython.core.application import BaseIPythonApplication

flags = {
    "overwrite" : ({
        "NBExtensionApp" : {
            "overwrite" : True,
        }}, "Force overwrite of existing files"
    ),
    "debug" : ({
        "NBExtensionApp" : {
            "verbose" : 2,
        }}, "Extra output"
    ),
    "quiet" : ({
        "NBExtensionApp" : {
            "verbose" : 0,
        }}, "Minimal output"
    ),
    "symlink" : ({
        "NBExtensionApp" : {
            "symlink" : True,
        }}, "Create symlink instead of copying files"
    ),
    "user" : ({
        "NBExtensionApp" : {
            "user" : True,
        }}, "Install to the user's IPython directory"
    ),
}
flags['s'] = flags['symlink']

aliases = {
    "ipython-dir" : "NBExtensionApp.ipython_dir",
    "prefix" : "NBExtensionApp.prefix",
    "nbextensions" : "NBExtensionApp.nbextensions_dir",
    "destination" : "NBExtensionApp.destination",
}

class NBExtensionApp(BaseIPythonApplication):
    """Entry point for installing notebook extensions"""

    description = """Install IPython notebook extensions

    Usage

        ipython install-nbextension path/url

    This copies a file or a folder into the IPython nbextensions directory.
    If a URL is given, it will be downloaded.
    If an archive is given, it will be extracted into nbextensions.
    If the requested files are already up to date, no action is taken
    unless --overwrite is specified.
    """

    examples = """
    ipython install-nbextension /path/to/myextension
    """
    aliases = aliases
    flags = flags

    overwrite = Bool(False, config=True, help="Force overwrite of existing files")
    symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
    user = Bool(False, config=True, help="Whether to do a user install")
    prefix = Unicode('', config=True, help="Installation prefix")
    nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
    destination = Unicode('', config=True, help="Destination for the copy or symlink")
    verbose = Enum((0,1,2), default_value=1, config=True,
        help="Verbosity level"
    )

    def install_extensions(self):
        if len(self.extra_args)>1:
            raise ValueError("only one nbextension allowed at a time.  Call multiple times to install multiple extensions.")
        install_nbextension(self.extra_args[0],
            overwrite=self.overwrite,
            symlink=self.symlink,
            verbose=self.verbose,
            user=self.user,
            prefix=self.prefix,
            destination=self.destination,
            nbextensions_dir=self.nbextensions_dir,
        )

    def start(self):
        if not self.extra_args:
            for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
                if os.path.exists(nbext):
                    print("Notebook extensions in %s:" % nbext)
                    for ext in os.listdir(nbext):
                        print(u"    %s" % ext)
        else:
            try:
                self.install_extensions()
            except ArgumentConflict as e:
                print(str(e), file=sys.stderr)
                self.exit(1)

if __name__ == '__main__':
    NBExtensionApp.launch_instance()

<commit_msg>minor doc improvement on nbextension<commit_after># coding: utf-8
"""Utilities for installing Javascript extensions for the notebook"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from __future__ import print_function

import os
import shutil
import sys
import tarfile
import zipfile
import uuid
from os.path import basename, join as pjoin

# Deferred imports
try:
    from urllib.parse import urlparse  # Py3
    from urllib.request import urlretrieve
except ImportError:
    from urlparse import urlparse
    from urllib import urlretrieve

from IPython.utils.path import get_ipython_dir, ensure_dir_exists
from IPython.utils.py3compat import string_types, cast_unicode_py2
from IPython.utils.tempdir import TemporaryDirectory

class ArgumentConflict(ValueError):
    pass

# Packagers: modify the next block if you store system-installed nbextensions elsewhere (unlikely)
SYSTEM_NBEXTENSIONS_DIRS = []

if os.name == 'nt':
    programdata = os.environ.get('PROGRAMDATA', None)
    if programdata: # PROGRAMDATA is not defined by default on XP.
        SYSTEM_NBEXTENSIONS_DIRS = [pjoin(programdata, 'jupyter', 'nbextensions')]
    prefixes = []
else:
    prefixes = [os.path.sep + pjoin('usr', 'local'), os.path.sep + 'usr']

# add sys.prefix at the front
if sys.prefix not in prefixes:
    prefixes.insert(0, sys.prefix)

for prefix in prefixes:
    nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
    if nbext not in SYSTEM_NBEXTENSIONS_DIRS:
        SYSTEM_NBEXTENSIONS_DIRS.append(nbext)

if os.name == 'nt':
    # PROGRAMDATA
    SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-1]
else:
    # /usr/local
    SYSTEM_NBEXTENSIONS_INSTALL_DIR = SYSTEM_NBEXTENSIONS_DIRS[-2]

def _should_copy(src, dest, verbose=1):
    """should a file be copied?"""
    if not os.path.exists(dest):
        return True
    if os.stat(src).st_mtime - os.stat(dest).st_mtime > 1e-6:
        # we add a fudge factor to work around a bug in python 2.x
        # that was fixed in python 3.x: http://bugs.python.org/issue12904
        if verbose >= 2:
            print("%s is out of date" % dest)
        return True
    if verbose >= 2:
        print("%s is up to date" % dest)
    return False

def _maybe_copy(src, dest, verbose=1):
    """copy a file if it needs updating"""
    if _should_copy(src, dest, verbose):
        if verbose >= 1:
            print("copying %s -> %s" % (src, dest))
        shutil.copy2(src, dest)

def _safe_is_tarfile(path):
    """safe version of is_tarfile, return False on IOError"""
    try:
        return tarfile.is_tarfile(path)
    except IOError:
        return False

def _get_nbext_dir(nbextensions_dir=None, user=False, prefix=None):
    """Return the nbextension directory specified"""
    if sum(map(bool, [user, prefix, nbextensions_dir])) > 1:
        raise ArgumentConflict("Cannot specify more than one of user, prefix, or nbextensions_dir.")
    if user:
        nbext = pjoin(get_ipython_dir(), u'nbextensions')
    else:
        if prefix:
            nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
        elif nbextensions_dir:
            nbext = nbextensions_dir
        else:
            nbext = SYSTEM_NBEXTENSIONS_INSTALL_DIR
    return nbext

def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None):
    """Check whether nbextension files have been installed

    Returns True if all files are found, False if any are missing.

    Parameters
    ----------

    files : list(paths)
        a list of relative paths within nbextensions.
    user : bool [default: False]
        Whether to check the user's .ipython/nbextensions directory.
        Otherwise check a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
    prefix : str [optional]
        Specify install prefix, if it should differ from default (e.g. /usr/local).
        Will check prefix/share/jupyter/nbextensions
    nbextensions_dir : str [optional]
        Specify absolute path of nbextensions directory explicitly.
    """
    nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
    # make sure nbextensions dir exists
    if not os.path.exists(nbext):
        return False

    if isinstance(files, string_types):
        # one file given, turn it into a list
        files = [files]

    return all(os.path.exists(pjoin(nbext, f)) for f in files)

def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1):
    """Install a Javascript extension for the notebook

    Stages files and/or directories into the nbextensions directory.
    By default, this compares modification time, and only stages files that need updating.
    If `overwrite` is specified, matching files are purged before proceeding.

    Parameters
    ----------

    path : path to file, directory, zip or tarball archive, or URL to install
        By default, the file will be installed with its base name, so '/path/to/foo'
        will install to 'nbextensions/foo'. See the destination argument below to change this.
        Archives (zip or tarballs) will be extracted into the nbextensions directory.
    overwrite : bool [default: False]
        If True, always install the files, regardless of what may already be installed.
    symlink : bool [default: False]
        If True, create a symlink in nbextensions, rather than copying files.
        Not allowed with URLs or archives. Windows support for symlinks requires
        Vista or above, Python 3, and a permission bit which only admin users
        have by default, so don't rely on it.
    user : bool [default: False]
        Whether to install to the user's .ipython/nbextensions directory.
        Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions).
    prefix : str [optional]
        Specify install prefix, if it should differ from default (e.g. /usr/local).
        Will install to ``<prefix>/share/jupyter/nbextensions``
    nbextensions_dir : str [optional]
        Specify absolute path of nbextensions directory explicitly.
    destination : str [optional]
        name the nbextension is installed to.  For example, if destination is 'foo', then
        the source file will be installed to 'nbextensions/foo', regardless of the source name.
        This cannot be specified if an archive is given as the source.
    verbose : int [default: 1]
        Set verbosity level. The default is 1, where file actions are printed.
        set verbose=2 for more output, or verbose=0 for silence.
    """
    nbext = _get_nbext_dir(nbextensions_dir, user, prefix)
    # make sure nbextensions dir exists
    ensure_dir_exists(nbext)

    if isinstance(path, (list, tuple)):
        raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions")

    path = cast_unicode_py2(path)

    if path.startswith(('https://', 'http://')):
        if symlink:
            raise ValueError("Cannot symlink from URLs")
        # Given a URL, download it
        with TemporaryDirectory() as td:
            filename = urlparse(path).path.split('/')[-1]
            local_path = os.path.join(td, filename)
            if verbose >= 1:
                print("downloading %s to %s" % (path, local_path))
            urlretrieve(path, local_path)
            # now install from the local copy
            install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose)
    elif path.endswith('.zip') or _safe_is_tarfile(path):
        if symlink:
            raise ValueError("Cannot symlink from archives")
        if destination:
            raise ValueError("Cannot give destination for archives")
        if verbose >= 1:
            print("extracting %s to %s" % (path, nbext))

        if path.endswith('.zip'):
            archive = zipfile.ZipFile(path)
        elif _safe_is_tarfile(path):
            archive = tarfile.open(path)
        archive.extractall(nbext)
        archive.close()
    else:
        if not destination:
            destination = basename(path)
        destination = cast_unicode_py2(destination)
        full_dest = pjoin(nbext, destination)
        if overwrite and os.path.lexists(full_dest):
            if verbose >= 1:
                print("removing %s" % full_dest)
            if os.path.isdir(full_dest) and not os.path.islink(full_dest):
                shutil.rmtree(full_dest)
            else:
                os.remove(full_dest)

        if symlink:
            path = os.path.abspath(path)
            if not os.path.exists(full_dest):
                if verbose >= 1:
                    print("symlink %s -> %s" % (full_dest, path))
                os.symlink(path, full_dest)
        elif os.path.isdir(path):
            path = pjoin(os.path.abspath(path), '') # end in path separator
            for parent, dirs, files in os.walk(path):
                dest_dir = pjoin(full_dest, parent[len(path):])
                if not os.path.exists(dest_dir):
                    if verbose >= 2:
                        print("making directory %s" % dest_dir)
                    os.makedirs(dest_dir)
                for file in files:
                    src = pjoin(parent, file)
                    # print("%r, %r" % (dest_dir, file))
                    dest_file = pjoin(dest_dir, file)
                    _maybe_copy(src, dest_file, verbose)
        else:
            src = path
            _maybe_copy(src, full_dest, verbose)

#----------------------------------------------------------------------
# install nbextension app
#----------------------------------------------------------------------

from IPython.utils.traitlets import Bool, Enum, Unicode, TraitError
from IPython.core.application import BaseIPythonApplication

flags = {
    "overwrite" : ({
        "NBExtensionApp" : {
            "overwrite" : True,
        }}, "Force overwrite of existing files"
    ),
    "debug" : ({
        "NBExtensionApp" : {
            "verbose" : 2,
        }}, "Extra output"
    ),
    "quiet" : ({
        "NBExtensionApp" : {
            "verbose" : 0,
        }}, "Minimal output"
    ),
    "symlink" : ({
        "NBExtensionApp" : {
            "symlink" : True,
        }}, "Create symlink instead of copying files"
    ),
    "user" : ({
        "NBExtensionApp" : {
            "user" : True,
        }}, "Install to the user's IPython directory"
    ),
}
flags['s'] = flags['symlink']

aliases = {
    "ipython-dir" : "NBExtensionApp.ipython_dir",
    "prefix" : "NBExtensionApp.prefix",
    "nbextensions" : "NBExtensionApp.nbextensions_dir",
    "destination" : "NBExtensionApp.destination",
}

class NBExtensionApp(BaseIPythonApplication):
    """Entry point for installing notebook extensions"""

    description = """Install IPython notebook extensions

    Usage

        ipython install-nbextension path/url

    This copies a file or a folder into the IPython nbextensions directory.
    If a URL is given, it will be downloaded.
    If an archive is given, it will be extracted into nbextensions.
    If the requested files are already up to date, no action is taken
    unless --overwrite is specified.
    """

    examples = """
    ipython install-nbextension /path/to/myextension
    """
    aliases = aliases
    flags = flags

    overwrite = Bool(False, config=True, help="Force overwrite of existing files")
    symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
    user = Bool(False, config=True, help="Whether to do a user install")
    prefix = Unicode('', config=True, help="Installation prefix")
    nbextensions_dir = Unicode('', config=True, help="Full path to nbextensions dir (probably use prefix or user)")
    destination = Unicode('', config=True, help="Destination for the copy or symlink")
    verbose = Enum((0,1,2), default_value=1, config=True,
        help="Verbosity level"
    )

    def install_extensions(self):
        if len(self.extra_args)>1:
            raise ValueError("only one nbextension allowed at a time.  Call multiple times to install multiple extensions.")
        install_nbextension(self.extra_args[0],
            overwrite=self.overwrite,
            symlink=self.symlink,
            verbose=self.verbose,
            user=self.user,
            prefix=self.prefix,
            destination=self.destination,
            nbextensions_dir=self.nbextensions_dir,
        )

    def start(self):
        if not self.extra_args:
            for nbext in [pjoin(self.ipython_dir, u'nbextensions')] + SYSTEM_NBEXTENSIONS_DIRS:
                if os.path.exists(nbext):
                    print("Notebook extensions in %s:" % nbext)
                    for ext in os.listdir(nbext):
                        print(u"    %s" % ext)
        else:
            try:
                self.install_extensions()
            except ArgumentConflict as e:
                print(str(e), file=sys.stderr)
                self.exit(1)

if __name__ == '__main__':
    NBExtensionApp.launch_instance()

<|endoftext|>


<commit_before>#!/usr/bin/python
#coding: utf-8 -*-

# (c) 2013, Benno Joy <benno@ansible.com>
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.

try:
    import shade
    HAS_SHADE = True
except ImportError:
    HAS_SHADE = False

DOCUMENTATION = '''
---
module: os_subnet
short_description: Add/Remove subnet to an OpenStack network
extends_documentation_fragment: openstack
version_added: "2.0"
author: "Monty Taylor (@emonty)"
description:
   - Add or Remove a subnet to an OpenStack network
options:
   state:
     description:
        - Indicate desired state of the resource
     choices: ['present', 'absent']
     required: false
     default: present
   network_name:
     description:
        - Name of the network to which the subnet should be attached
     required: true when state is 'present'
   name:
     description:
       - The name of the subnet that should be created. Although Neutron
         allows for non-unique subnet names, this module enforces subnet
         name uniqueness.
     required: true
   cidr:
     description:
        - The CIDR representation of the subnet that should be assigned to
          the subnet.
     required: true when state is 'present'
     default: None
   ip_version:
     description:
        - The IP version of the subnet 4 or 6
     required: false
     default: 4
   enable_dhcp:
     description:
        - Whether DHCP should be enabled for this subnet.
     required: false
     default: true
   gateway_ip:
     description:
        - The ip that would be assigned to the gateway for this subnet
     required: false
     default: None
   dns_nameservers:
     description:
        - List of DNS nameservers for this subnet.
     required: false
     default: None
   allocation_pool_start:
     description:
        - From the subnet pool the starting address from which the IP should
          be allocated.
     required: false
     default: None
   allocation_pool_end:
     description:
        - From the subnet pool the last IP that should be assigned to the
          virtual machines.
     required: false
     default: None
   host_routes:
     description:
        - A list of host route dictionaries for the subnet.
     required: false
     default: None
requirements:
    - "python >= 2.6"
    - "shade"
'''

EXAMPLES = '''
# Create a new (or update an existing) subnet on the specified network
- os_subnet:
    state=present
    network_name=network1
    name=net1subnet
    cidr=192.168.0.0/24
    dns_nameservers:
       - 8.8.8.7
       - 8.8.8.8
    host_routes:
       - destination: 0.0.0.0/0
         nexthop: 123.456.78.9
       - destination: 192.168.0.0/24
         nexthop: 192.168.0.1

# Delete a subnet
- os_subnet:
    state=absent
    name=net1subnet
'''

def _needs_update(subnet, module):
    """Check for differences in the updatable values."""
    enable_dhcp = module.params['enable_dhcp']
    subnet_name = module.params['name']
    pool_start = module.params['allocation_pool_start']
    pool_end = module.params['allocation_pool_end']
    gateway_ip = module.params['gateway_ip']
    dns = module.params['dns_nameservers']
    host_routes = module.params['host_routes']
    curr_pool = subnet['allocation_pools'][0]

    if subnet['enable_dhcp'] != enable_dhcp:
        return True
    if subnet_name and subnet['name'] != subnet_name:
        return True
    if pool_start and curr_pool['start'] != pool_start:
        return True
    if pool_end and curr_pool['end'] != pool_end:
        return True
    if gateway_ip and subnet['gateway_ip'] != gateway_ip:
        return True
    if dns and sorted(subnet['dns_nameservers']) != sorted(dns):
        return True
    if host_routes:
        curr_hr = sorted(subnet['host_routes'], key=lambda t: t.keys())
        new_hr = sorted(host_routes, key=lambda t: t.keys())
        if sorted(curr_hr) != sorted(new_hr):
            return True
    return False

def _system_state_change(module, subnet):
    state = module.params['state']
    if state == 'present':
        if not subnet:
            return True
        return _needs_update(subnet, module)
    if state == 'absent' and subnet:
        return True
    return False

def main():
    argument_spec = openstack_full_argument_spec(
        name=dict(required=True),
        network_name=dict(default=None),
        cidr=dict(default=None),
        ip_version=dict(default='4', choices=['4', '6']),
        enable_dhcp=dict(default='true', type='bool'),
        gateway_ip=dict(default=None),
        dns_nameservers=dict(default=None, type='list'),
        allocation_pool_start=dict(default=None),
        allocation_pool_end=dict(default=None),
        host_routes=dict(default=None, type='list'),
        state=dict(default='present', choices=['absent', 'present']),
    )

    module_kwargs = openstack_module_kwargs()
    module = AnsibleModule(argument_spec,
                           supports_check_mode=True,
                           **module_kwargs)

    if not HAS_SHADE:
        module.fail_json(msg='shade is required for this module')

    state = module.params['state']
    network_name = module.params['network_name']
    cidr = module.params['cidr']
    ip_version = module.params['ip_version']
    enable_dhcp = module.params['enable_dhcp']
    subnet_name = module.params['name']
    gateway_ip = module.params['gateway_ip']
    dns = module.params['dns_nameservers']
    pool_start = module.params['allocation_pool_start']
    pool_end = module.params['allocation_pool_end']
    host_routes = module.params['host_routes']

    # Check for required parameters when state == 'present'
    if state == 'present':
        for p in ['network_name', 'cidr']:
            if not module.params[p]:
                module.fail_json(msg='%s required with present state' % p)

    if pool_start and pool_end:
        pool = [dict(start=pool_start, end=pool_end)]
    elif pool_start or pool_end:
        module.fail_json(msg='allocation pool requires start and end values')
    else:
        pool = None

    try:
        cloud = shade.openstack_cloud(**module.params)
        subnet = cloud.get_subnet(subnet_name)

        if module.check_mode:
            module.exit_json(changed=_system_state_change(module, subnet))

        if state == 'present':
            if not subnet:
                subnet = cloud.create_subnet(network_name, cidr,
                                             ip_version=ip_version,
                                             enable_dhcp=enable_dhcp,
                                             subnet_name=subnet_name,
                                             gateway_ip=gateway_ip,
                                             dns_nameservers=dns,
                                             allocation_pools=pool,
                                             host_routes=host_routes)
                changed = True
            else:
                if _needs_update(subnet, module):
                    cloud.update_subnet(subnet['id'],
                                        subnet_name=subnet_name,
                                        enable_dhcp=enable_dhcp,
                                        gateway_ip=gateway_ip,
                                        dns_nameservers=dns,
                                        allocation_pools=pool,
                                        host_routes=host_routes)
                    changed = True
                else:
                    changed = False
            module.exit_json(changed=changed)

        elif state == 'absent':
            if not subnet:
                changed = False
            else:
                changed = True
                cloud.delete_subnet(subnet_name)
            module.exit_json(changed=changed)

    except shade.OpenStackCloudException as e:
        module.fail_json(msg=e.message)

# this is magic, see lib/ansible/module_common.py
from ansible.module_utils.basic import *
from ansible.module_utils.openstack import *
if __name__ == '__main__':
    main()
<commit_msg>Plumb ipv6 modes into os_subnet<commit_after>#!/usr/bin/python
#coding: utf-8 -*-

# (c) 2013, Benno Joy <benno@ansible.com>
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.

try:
    import shade
    HAS_SHADE = True
except ImportError:
    HAS_SHADE = False

DOCUMENTATION = '''
---
module: os_subnet
short_description: Add/Remove subnet to an OpenStack network
extends_documentation_fragment: openstack
version_added: "2.0"
author: "Monty Taylor (@emonty)"
description:
   - Add or Remove a subnet to an OpenStack network
options:
   state:
     description:
        - Indicate desired state of the resource
     choices: ['present', 'absent']
     required: false
     default: present
   network_name:
     description:
        - Name of the network to which the subnet should be attached
     required: true when state is 'present'
   name:
     description:
       - The name of the subnet that should be created. Although Neutron
         allows for non-unique subnet names, this module enforces subnet
         name uniqueness.
     required: true
   cidr:
     description:
        - The CIDR representation of the subnet that should be assigned to
          the subnet.
     required: true when state is 'present'
     default: None
   ip_version:
     description:
        - The IP version of the subnet 4 or 6
     required: false
     default: 4
   enable_dhcp:
     description:
        - Whether DHCP should be enabled for this subnet.
     required: false
     default: true
   gateway_ip:
     description:
        - The ip that would be assigned to the gateway for this subnet
     required: false
     default: None
   dns_nameservers:
     description:
        - List of DNS nameservers for this subnet.
     required: false
     default: None
   allocation_pool_start:
     description:
        - From the subnet pool the starting address from which the IP should
          be allocated.
     required: false
     default: None
   allocation_pool_end:
     description:
        - From the subnet pool the last IP that should be assigned to the
          virtual machines.
     required: false
     default: None
   host_routes:
     description:
        - A list of host route dictionaries for the subnet.
     required: false
     default: None
   ipv6_ra_mode:
     description:
        - IPv6 router advertisement mode
     choices: ['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac']
     required: false
     default: None
   ipv6_address_mode:
     description:
        - IPv6 address mode
     choices: ['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac']
     required: false
     default: None
requirements:
    - "python >= 2.6"
    - "shade"
'''

EXAMPLES = '''
# Create a new (or update an existing) subnet on the specified network
- os_subnet:
    state=present
    network_name=network1
    name=net1subnet
    cidr=192.168.0.0/24
    dns_nameservers:
       - 8.8.8.7
       - 8.8.8.8
    host_routes:
       - destination: 0.0.0.0/0
         nexthop: 123.456.78.9
       - destination: 192.168.0.0/24
         nexthop: 192.168.0.1

# Delete a subnet
- os_subnet:
    state=absent
    name=net1subnet

# Create an ipv6 stateless subnet
- os_subnet:
    state: present
    name: intv6
    network_name: internal
    ip_version: 6
    cidr: 2db8:1::/64
    dns_nameservers:
        - 2001:4860:4860::8888
        - 2001:4860:4860::8844
    ipv6_ra_mode: dhcpv6-stateless
    ipv6_address_mode: dhcpv6-stateless
'''

def _needs_update(subnet, module):
    """Check for differences in the updatable values."""
    enable_dhcp = module.params['enable_dhcp']
    subnet_name = module.params['name']
    pool_start = module.params['allocation_pool_start']
    pool_end = module.params['allocation_pool_end']
    gateway_ip = module.params['gateway_ip']
    dns = module.params['dns_nameservers']
    host_routes = module.params['host_routes']
    curr_pool = subnet['allocation_pools'][0]

    if subnet['enable_dhcp'] != enable_dhcp:
        return True
    if subnet_name and subnet['name'] != subnet_name:
        return True
    if pool_start and curr_pool['start'] != pool_start:
        return True
    if pool_end and curr_pool['end'] != pool_end:
        return True
    if gateway_ip and subnet['gateway_ip'] != gateway_ip:
        return True
    if dns and sorted(subnet['dns_nameservers']) != sorted(dns):
        return True
    if host_routes:
        curr_hr = sorted(subnet['host_routes'], key=lambda t: t.keys())
        new_hr = sorted(host_routes, key=lambda t: t.keys())
        if sorted(curr_hr) != sorted(new_hr):
            return True
    return False

def _system_state_change(module, subnet):
    state = module.params['state']
    if state == 'present':
        if not subnet:
            return True
        return _needs_update(subnet, module)
    if state == 'absent' and subnet:
        return True
    return False

def main():
    ipv6_mode_choices = ['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac']
    argument_spec = openstack_full_argument_spec(
        name=dict(required=True),
        network_name=dict(default=None),
        cidr=dict(default=None),
        ip_version=dict(default='4', choices=['4', '6']),
        enable_dhcp=dict(default='true', type='bool'),
        gateway_ip=dict(default=None),
        dns_nameservers=dict(default=None, type='list'),
        allocation_pool_start=dict(default=None),
        allocation_pool_end=dict(default=None),
        host_routes=dict(default=None, type='list'),
        ipv6_ra_mode=dict(default=None, choice=ipv6_mode_choices),
        ipv6_address_mode=dict(default=None, choice=ipv6_mode_choices),
        state=dict(default='present', choices=['absent', 'present']),
    )

    module_kwargs = openstack_module_kwargs()
    module = AnsibleModule(argument_spec,
                           supports_check_mode=True,
                           **module_kwargs)

    if not HAS_SHADE:
        module.fail_json(msg='shade is required for this module')

    state = module.params['state']
    network_name = module.params['network_name']
    cidr = module.params['cidr']
    ip_version = module.params['ip_version']
    enable_dhcp = module.params['enable_dhcp']
    subnet_name = module.params['name']
    gateway_ip = module.params['gateway_ip']
    dns = module.params['dns_nameservers']
    pool_start = module.params['allocation_pool_start']
    pool_end = module.params['allocation_pool_end']
    host_routes = module.params['host_routes']
    ipv6_ra_mode = module.params['ipv6_ra_mode']
    ipv6_a_mode = module.params['ipv6_address_mode']

    # Check for required parameters when state == 'present'
    if state == 'present':
        for p in ['network_name', 'cidr']:
            if not module.params[p]:
                module.fail_json(msg='%s required with present state' % p)

    if pool_start and pool_end:
        pool = [dict(start=pool_start, end=pool_end)]
    elif pool_start or pool_end:
        module.fail_json(msg='allocation pool requires start and end values')
    else:
        pool = None

    try:
        cloud = shade.openstack_cloud(**module.params)
        subnet = cloud.get_subnet(subnet_name)

        if module.check_mode:
            module.exit_json(changed=_system_state_change(module, subnet))

        if state == 'present':
            if not subnet:
                subnet = cloud.create_subnet(network_name, cidr,
                                             ip_version=ip_version,
                                             enable_dhcp=enable_dhcp,
                                             subnet_name=subnet_name,
                                             gateway_ip=gateway_ip,
                                             dns_nameservers=dns,
                                             allocation_pools=pool,
                                             host_routes=host_routes,
                                             ipv6_ra_mode=ipv6_ra_mode,
                                             ipv6_address_mode=ipv6_a_mode)
                changed = True
            else:
                if _needs_update(subnet, module):
                    cloud.update_subnet(subnet['id'],
                                        subnet_name=subnet_name,
                                        enable_dhcp=enable_dhcp,
                                        gateway_ip=gateway_ip,
                                        dns_nameservers=dns,
                                        allocation_pools=pool,
                                        host_routes=host_routes,
                                        ipv6_ra_mode=ipv6_ra_mode,
                                        ipv6_address_mode=ipv6_a_mode)
                    changed = True
                else:
                    changed = False
            module.exit_json(changed=changed)

        elif state == 'absent':
            if not subnet:
                changed = False
            else:
                changed = True
                cloud.delete_subnet(subnet_name)
            module.exit_json(changed=changed)

    except shade.OpenStackCloudException as e:
        module.fail_json(msg=e.message)

# this is magic, see lib/ansible/module_common.py
from ansible.module_utils.basic import *
from ansible.module_utils.openstack import *
if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>import socket
import os, sys, time, threading, shutil, time
import Image, ImageDraw
import io
from packages.rmmedia import mediaplayer
from constants import *

def readInt(data):
    intBytes = data[:4]
    if len(data) > 4:
        remainingData = data[4:]
    else:
        remainingData = []
    num = (intBytes[0] << 24) + (intBytes[1] << 16) + (intBytes[2] << 8) + intBytes[3]
    return num, remainingData

# added size!
def readString(data, size):
    #size, data = readInt(data)
    strBytes = data[:size]
    if len(data) > size:
        remainingData = data[size:]
    else:
        remainingData = []
    inStr = str(strBytes)
    return inStr, remainingData

def interpret(tmpFilePath):
    # open temp file in binary mode
    with open(tmpFilePath, 'rb') as f:
        data = bytearray(f.read(4))
        numFiles, data = readInt(data)

        # check thumbnails path
        thumbsPath = os.getcwd() + '/media/thumbs/'
        if not os.path.isdir(thumbsPath):
            os.mkdir(thumbsPath)

        print "READING %d FILES" % numFiles
        for i in range(numFiles):
            # read file name
            data = bytearray(f.read(4))
            size, data = readInt(data)
            data = bytearray(f.read(size))
            name, data = readString(data, size)
            openPath = os.getcwd() + '/media/' + name
            data = bytearray(f.read(4))
            fileSize, data = readInt(data)
            if not os.path.isdir(openPath):
                #f = open(openPath, 'w+') #open in binary
                l = bytearray(f.read(fileSize))
                stream = io.BytesIO(l)
                img = Image.open(stream)
                draw = ImageDraw.Draw(img)
                img.save(openPath)
                #l = data[:fileSize]
                #data = data[fileSize:]
                #f.write(l)
                #f.close()

                # save thumbnail
                img = Image.open(openPath)
                w = img.size[0]
                h = img.size[1]
                newW = 200
                newH = newW * h / w
                img.thumbnail((newW,newH))
                img.save(thumbsPath + name)

def _openSocket():
    # create temp directory for received data
    if os.path.isdir(TCP_TEMP):
        shutil.rmtree(TCP_TEMP)
    os.mkdir(TCP_TEMP)

    global s
    s = socket.socket()
    s.bind(('',60020))
    s.listen(10) # Accept max. 10 connections
    print "File socket ready, listening for incoming file connections..."
    while True:
        sc, address = s.accept()
        print "TCP CLIENT CONNECTED: ", address

        dataSizeBytes = sc.recv(4)
        dataSize, remaining = readInt(bytearray(dataSizeBytes))
        print "Receiving %d Bytes" % (dataSize)
        buff = ''
        bytesRead = 0
        tmpFile = "tmp_" + str(int(round(time.time())))
        with open(TCP_TEMP + "/" + tmpFile, 'w') as tmp:
            while bytesRead < dataSize:
                data = sc.recv(1024)
                bytesRead += len(data)
                tmp.write(data)
                #buff += sc.recv(1024)

        print "Closing TCP Client connection..."
        sc.close()

        print "Data read to buffer, processing..."
        interpret(TCP_TEMP + "/" + tmpFile)

        data = bytearray(buff)
        # read number of files
        #numFilesBytes = sc.recv(4)
        numFiles, data = readInt(data)
        '''
        # check thumbnails path
        thumbsPath = os.getcwd() + '/media/thumbs/'
        if not os.path.isdir(thumbsPath):
            os.mkdir(thumbsPath)

        print "READING %d FILES" % numFiles
        for i in range(numFiles):
            # read file name
            name, data = readString(data)
            openPath = os.getcwd() + '/media/' + name
            fileSize, data = readInt(data)
            if not os.path.isdir(openPath):
                f = open(openPath, 'w+') #open in binary
                l = data[:fileSize]
                data = data[fileSize:]
                f.write(l)
                f.close()

                # save thumbnail
                img = Image.open(openPath)
                w = img.size[0]
                h = img.size[1]
                newW = 200
                newH = newW * h / w
                img.thumbnail((newW,newH))
                img.save(thumbsPath + name)
        '''
        print "FILES SAVED!"

        if mediaplayer.playerState == PLAYER_STARTED:
            mediaplayer.stop()
            time.sleep(3)
            mediaplayer.play()

def openFileSocket():
    global server_thread
    # Start a thread with the server
    server_thread = threading.Thread(target=_openSocket)
    # Exit the server thread when the main thread terminates
    server_thread.daemon = True
    server_thread.start()
    print "File socket loop running in thread:", server_thread.name

def closeFileSocket():
    global s
    s.close()

server_thread = None
s = None
<commit_msg>TCP Message handing through temp file should work now, added deletion of temp file after processing, further testing.<commit_after>import socket
import os, sys, time, threading, shutil, time
import Image, ImageDraw
import io
from packages.rmmedia import mediaplayer
from constants import *

def readInt(data):
    intBytes = data[:4]
    if len(data) > 4:
        remainingData = data[4:]
    else:
        remainingData = []
    num = (intBytes[0] << 24) + (intBytes[1] << 16) + (intBytes[2] << 8) + intBytes[3]
    return num, remainingData

# added size!
def readString(data, size):
    #size, data = readInt(data)
    strBytes = data[:size]
    if len(data) > size:
        remainingData = data[size:]
    else:
        remainingData = []
    inStr = str(strBytes)
    return inStr, remainingData

def interpret(tmpFilePath):
    # open temp file in binary mode
    with open(tmpFilePath, 'rb') as f:
        data = bytearray(f.read(4))
        numFiles, data = readInt(data)

        # check thumbnails path
        thumbsPath = os.getcwd() + '/media/thumbs/'
        if not os.path.isdir(thumbsPath):
            os.mkdir(thumbsPath)

        print "READING %d FILES" % numFiles
        for i in range(numFiles):
            # read file name
            data = bytearray(f.read(4))
            size, data = readInt(data)
            data = bytearray(f.read(size))
            name, data = readString(data, size)
            openPath = os.getcwd() + '/media/' + name
            data = bytearray(f.read(4))
            fileSize, data = readInt(data)
            if not os.path.isdir(openPath):
                #f = open(openPath, 'w+') #open in binary
                l = bytearray(f.read(fileSize))
                stream = io.BytesIO(l)
                img = Image.open(stream)
                draw = ImageDraw.Draw(img)
                img.save(openPath)
                #l = data[:fileSize]
                #data = data[fileSize:]
                #f.write(l)
                #f.close()

                # save thumbnail
                img = Image.open(openPath)
                w = img.size[0]
                h = img.size[1]
                newW = 200
                newH = newW * h / w
                img.thumbnail((newW,newH))
                img.save(thumbsPath + name)
    # remove temp file
    os.remove(tmpFilePath)

def _openSocket():
    # create temp directory for received data
    if os.path.isdir(TCP_TEMP):
        shutil.rmtree(TCP_TEMP)
    os.mkdir(TCP_TEMP)

    global s
    s = socket.socket()
    s.bind(('',60020))
    s.listen(10) # Accept max. 10 connections
    print "File socket ready, listening for incoming file connections..."
    while True:
        sc, address = s.accept()
        print "TCP CLIENT CONNECTED: ", address

        dataSizeBytes = sc.recv(4)
        dataSize, remaining = readInt(bytearray(dataSizeBytes))
        print "Receiving %d Bytes" % (dataSize)
        buff = ''
        bytesRead = 0
        tmpFile = "tmp_" + str(int(round(time.time())))
        with open(TCP_TEMP + "/" + tmpFile, 'w') as tmp:
            while bytesRead < dataSize:
                data = sc.recv(1024)
                bytesRead += len(data)
                tmp.write(data)
                #buff += sc.recv(1024)

        print "Closing TCP Client connection..."
        sc.close()

        print "Data read to buffer, processing..."
        interpret(TCP_TEMP + "/" + tmpFile)

        data = bytearray(buff)
        print "FILES SAVED!"

        if mediaplayer.playerState == PLAYER_STARTED:
            mediaplayer.stop()
            time.sleep(5)
            mediaplayer.play()

def openFileSocket():
    global server_thread
    # Start a thread with the server
    server_thread = threading.Thread(target=_openSocket)
    # Exit the server thread when the main thread terminates
    server_thread.daemon = True
    server_thread.start()
    print "File socket loop running in thread:", server_thread.name

def closeFileSocket():
    global s
    s.close()

server_thread = None
s = None
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2017, Thierry Sallé (@tsalle)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

ANSIBLE_METADATA = {
    'status': ['preview'],
    'supported_by': 'community',
    'metadata_version': '1.1'
}

DOCUMENTATION = '''
---
module: grafana_plugin
author:
  - "Thierry Sallé (@tsalle)"
version_added: "2.5"
short_description: Manage Grafana plugins via grafana-cli
description:
  - Install and remove Grafana plugins.
options:
  name:
    required: true
    description:
      - Name of the plugin.
  version:
    description:
      - Version of the plugin to install.
      - Default to latest
  grafana_plugins_dir:
    description:
      - Directory where Grafana plugin will be installed.
  grafana_repo:
    description:
      - Grafana repository. If not set, gafana-cli will use the default value C(https://grafana.net/api/plugins).
  grafana_plugin_url:
    description:
      - Custom Grafana plugin URL
      - Require grafana 4.6.x or later
  state:
    default: present
    choices: [present, absent]
    description:
      - Status of the Grafana plugin.
      - If latest is set, the version parameter will be ignored.
'''

EXAMPLES = '''
---
- name: install - update Grafana piechart panel plugin
  grafana_plugin:
    name: grafana-piechart-panel
    version: latest
    state: present
'''

RETURN = '''
---
version:
  description: version of the installed / removed plugin.
  type: string
  returned: allways
'''

import base64
import json
import os
from ansible.module_utils.basic import AnsibleModule

__metaclass__ = type

class GrafanaCliException(Exception):
    pass

def grafana_cli_bin(params):
    '''
    Get the grafana-cli binary path with global options.
    Raise a GrafanaCliException if the grafana-cli is not present or not in PATH

    :param params: ansible module params. Used to fill grafana-cli global params.
    '''
    program = 'grafana-cli'
    grafana_cli = None

    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            grafana_cli = program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                grafana_cli = exe_file
                break

    if grafana_cli is None:
        raise GrafanaCliException('grafana-cli binary is not present or not in PATH')
    else:
        if 'grafana_plugin_url' in params and params['grafana_plugin_url']:
            grafana_cli = '{} {} {}'.format(grafana_cli, '--pluginUrl', params['grafana_plugin_url'])
        if 'grafana_plugins_dir' in params and params['grafana_plugins_dir']:
            grafana_cli = '{} {} {}'.format(grafana_cli, '--pluginsDir', params['grafana_plugins_dir'])
        if 'grafana_repo' in params and params['grafana_repo']:
            grafana_cli = '{} {} {}'.format(grafana_cli, '--repo', params['grafana_repo'])
        if 'validate_certs' in params and params['validate_certs'] is False:
            grafana_cli = '{} {}'.format(grafana_cli, '--insecure')

        return '{} {}'.format(grafana_cli, 'plugins')

def get_grafana_plugin_version(module, params):
    '''
    Fetch grafana installed plugin version. Return None if plugin is not installed.

    :param module: ansible module object. used to run system commands.
    :param params: ansible module params.
    '''
    grafana_cli = grafana_cli_bin(params)
    rc, stdout, stderr = module.run_command('{} ls'.format(grafana_cli))
    stdout_lines = stdout.split("\n")
    for line in stdout_lines:
        if line.find(' @ ') != -1:
            line = line.rstrip()
            plugin_name, plugin_version = line.split(' @ ')
            if plugin_name == params['name']:
                return plugin_version
    return None

def grafana_plugin(module, params):
    '''
    Install update or remove grafana plugin

    :param module: ansible module object. used to run system commands.
    :param params: ansible module params.
    '''
    grafana_cli = grafana_cli_bin(params)

    if params['state'] == 'present':
        grafana_plugin_version = get_grafana_plugin_version(module, params)
        if grafana_plugin_version is not None:
            if 'version' in params and params['version']:
                if params['version'] == grafana_plugin_version:
                    return {'msg': 'Grafana plugin already installed',
                            'changed': False,
                            'version': grafana_plugin_version}
                else:
                    if params['version'] == 'latest':
                        cmd = '{} update {}'.format(grafana_cli, params['name'])
                    else:
                        cmd = '{} install {} {}'.format(grafana_cli, params['name'], params['version'])
            else:
                return {'msg': 'Grafana plugin already installed',
                        'changed': False,
                        'version': grafana_plugin_version}
        else:
            if 'version' in params:
                if params['version'] == 'latest':
                    cmd = '{} install {}'.format(grafana_cli, params['name'])
                else:
                    cmd = '{} install {} {}'.format(grafana_cli, params['name'], params['version'])
            else:
                cmd = '{} install {}'.format(grafana_cli, params['name'])
    else:
        cmd = '{} uninstall {}'.format(grafana_cli, params['name'])

    rc, stdout, stderr = module.run_command(cmd)
    if rc == 0:
        stdout_lines = stdout.split("\n")
        for line in stdout_lines:
            if line.find(params['name']):
                plugin_name, plugin_version = line.split(' @ ')
                return {'msg': 'Grafana plugin {} installed : {}'.format(params['name'], cmd),
                        'changed': True,
                        'version': plugin_version}
    else:
        raise GrafanaCliException("'{}' execution returned an error : [{}] {} {}".format(cmd, rc, stdout, stderr))

def main():
    module = AnsibleModule(
        argument_spec=dict(
            name=dict(required=True,
                      type='str'),
            version=dict(type='str'),
            grafana_plugins_dir=dict(type='str'),
            grafana_repo=dict(type='str'),
            grafana_plugin_url=dict(type='str'),
            state=dict(choices=['present', 'absent'],
                       default='present')
        ),
        supports_check_mode=False
    )

    try:
        result = grafana_plugin(module, module.params)
    except GrafanaCliException as e:
        module.fail_json(
            failed=True,
            msg="{}".format(e)
        )
        return
    except Exception as e:
        module.fail_json(
            failed=True,
            msg="{} : {} ".format(type(e), e)
        )
        return

    module.exit_json(
        failed=False,
        **result
    )
    return

if __name__ == '__main__':
    main()
<commit_msg>expect default value of `version` param as None (#35790)<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2017, Thierry Sallé (@tsalle)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

ANSIBLE_METADATA = {
    'status': ['preview'],
    'supported_by': 'community',
    'metadata_version': '1.1'
}

DOCUMENTATION = '''
---
module: grafana_plugin
author:
  - "Thierry Sallé (@tsalle)"
version_added: "2.5"
short_description: Manage Grafana plugins via grafana-cli
description:
  - Install and remove Grafana plugins.
options:
  name:
    required: true
    description:
      - Name of the plugin.
  version:
    description:
      - Version of the plugin to install.
      - Default to latest
  grafana_plugins_dir:
    description:
      - Directory where Grafana plugin will be installed.
  grafana_repo:
    description:
      - Grafana repository. If not set, gafana-cli will use the default value C(https://grafana.net/api/plugins).
  grafana_plugin_url:
    description:
      - Custom Grafana plugin URL
      - Require grafana 4.6.x or later
  state:
    default: present
    choices: [present, absent]
    description:
      - Status of the Grafana plugin.
      - If latest is set, the version parameter will be ignored.
'''

EXAMPLES = '''
---
- name: install - update Grafana piechart panel plugin
  grafana_plugin:
    name: grafana-piechart-panel
    version: latest
    state: present
'''

RETURN = '''
---
version:
  description: version of the installed / removed plugin.
  type: string
  returned: allways
'''

import base64
import json
import os
from ansible.module_utils.basic import AnsibleModule

__metaclass__ = type

class GrafanaCliException(Exception):
    pass

def grafana_cli_bin(params):
    '''
    Get the grafana-cli binary path with global options.
    Raise a GrafanaCliException if the grafana-cli is not present or not in PATH

    :param params: ansible module params. Used to fill grafana-cli global params.
    '''
    program = 'grafana-cli'
    grafana_cli = None

    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            grafana_cli = program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                grafana_cli = exe_file
                break

    if grafana_cli is None:
        raise GrafanaCliException('grafana-cli binary is not present or not in PATH')
    else:
        if 'grafana_plugin_url' in params and params['grafana_plugin_url']:
            grafana_cli = '{} {} {}'.format(grafana_cli, '--pluginUrl', params['grafana_plugin_url'])
        if 'grafana_plugins_dir' in params and params['grafana_plugins_dir']:
            grafana_cli = '{} {} {}'.format(grafana_cli, '--pluginsDir', params['grafana_plugins_dir'])
        if 'grafana_repo' in params and params['grafana_repo']:
            grafana_cli = '{} {} {}'.format(grafana_cli, '--repo', params['grafana_repo'])
        if 'validate_certs' in params and params['validate_certs'] is False:
            grafana_cli = '{} {}'.format(grafana_cli, '--insecure')

        return '{} {}'.format(grafana_cli, 'plugins')

def get_grafana_plugin_version(module, params):
    '''
    Fetch grafana installed plugin version. Return None if plugin is not installed.

    :param module: ansible module object. used to run system commands.
    :param params: ansible module params.
    '''
    grafana_cli = grafana_cli_bin(params)
    rc, stdout, stderr = module.run_command('{} ls'.format(grafana_cli))
    stdout_lines = stdout.split("\n")
    for line in stdout_lines:
        if line.find(' @ ') != -1:
            line = line.rstrip()
            plugin_name, plugin_version = line.split(' @ ')
            if plugin_name == params['name']:
                return plugin_version
    return None

def grafana_plugin(module, params):
    '''
    Install update or remove grafana plugin

    :param module: ansible module object. used to run system commands.
    :param params: ansible module params.
    '''
    grafana_cli = grafana_cli_bin(params)

    if params['state'] == 'present':
        grafana_plugin_version = get_grafana_plugin_version(module, params)
        if grafana_plugin_version is not None:
            if 'version' in params and params['version']:
                if params['version'] == grafana_plugin_version:
                    return {'msg': 'Grafana plugin already installed',
                            'changed': False,
                            'version': grafana_plugin_version}
                else:
                    if params['version'] == 'latest' or params['version'] is None:
                        cmd = '{} update {}'.format(grafana_cli, params['name'])
                    else:
                        cmd = '{} install {} {}'.format(grafana_cli, params['name'], params['version'])
            else:
                return {'msg': 'Grafana plugin already installed',
                        'changed': False,
                        'version': grafana_plugin_version}
        else:
            if 'version' in params:
                if params['version'] == 'latest' or params['version'] is None:
                    cmd = '{} install {}'.format(grafana_cli, params['name'])
                else:
                    cmd = '{} install {} {}'.format(grafana_cli, params['name'], params['version'])
            else:
                cmd = '{} install {}'.format(grafana_cli, params['name'])
    else:
        cmd = '{} uninstall {}'.format(grafana_cli, params['name'])

    rc, stdout, stderr = module.run_command(cmd)
    if rc == 0:
        stdout_lines = stdout.split("\n")
        for line in stdout_lines:
            if line.find(params['name']):
                plugin_name, plugin_version = line.split(' @ ')
                return {'msg': 'Grafana plugin {} installed : {}'.format(params['name'], cmd),
                        'changed': True,
                        'version': plugin_version}
    else:
        raise GrafanaCliException("'{}' execution returned an error : [{}] {} {}".format(cmd, rc, stdout, stderr))

def main():
    module = AnsibleModule(
        argument_spec=dict(
            name=dict(required=True,
                      type='str'),
            version=dict(type='str'),
            grafana_plugins_dir=dict(type='str'),
            grafana_repo=dict(type='str'),
            grafana_plugin_url=dict(type='str'),
            state=dict(choices=['present', 'absent'],
                       default='present')
        ),
        supports_check_mode=False
    )

    try:
        result = grafana_plugin(module, module.params)
    except GrafanaCliException as e:
        module.fail_json(
            failed=True,
            msg="{}".format(e)
        )
        return
    except Exception as e:
        module.fail_json(
            failed=True,
            msg="{} : {} ".format(type(e), e)
        )
        return

    module.exit_json(
        failed=False,
        **result
    )
    return

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
#

DOCUMENTATION = """
---
module: junos_facts
version_added: "2.1"
author: "Peter Sprygada (@privateip)"
short_description: Collect facts from remove device running Junos
description:
  - Collects fact information from a remote device running the Junos
    operating system.  By default, the module will collect basic fact
    information from the device to be included with the hostvars.
    Additional fact information can be collected based on the
    configured set of arguments.
extends_documentation_fragment: junos
options:
  config:
    description:
      - The C(config) argument instructs the fact module to collect
        the configuration from the remote device.  The configuration
        is then included in return facts.  By default, the configuration
        is returned as text.  The C(config_format) can be used to return
        different Junos configuration formats.
    required: true
    default: false
  config_format:
    description:
      - The C(config_format) argument is used to specify the desired
        format of the configuration file.  Devices support three
        configuration file formats.  By default, the configuration
        from the device is returned as text.  The other options include
        set and xml.  If the xml option is choosen, the configuration file
        is returned as both xml and json.
    required: false
    default: text
    choices: ['xml', 'text', 'set']
requirements:
  - junos-eznc
notes:
  - This module requires the netconf system service be enabled on
    the remote device being managed
"""

EXAMPLES = """
# the required set of connection arguments have been purposely left off
# the examples for brevity

- name: collect default set of facts
  junos_facts:

- name: collect default set of facts and configuration
  junos_facts:
    config: yes

- name: collect default set of facts and configuration in set format
  junos_facts:
    config: yes
    config_format: set

- name: collect default set of facts and configuration in XML and JSON format
  junos_facts:
    config: yes
    config_format: xml
"""

RETURN = """
ansible_facts:
  descrption: Returns the facts collect from the device
  returned: always
  type: dict
"""

def main():
    """ Main entry point for AnsibleModule
    """
    spec = dict(
        config=dict(required=True, type='bool'),
        config_format=dict(default='text', choices=['xml', 'set', 'text']),
        transport=dict(default='netconf', choices=['netconf'])
    )

    module = get_module(argument_spec=spec,
                        supports_check_mode=True)

    result = dict(changed=False)

    facts = module.get_facts()

    if '2RE' in facts:
        facts['has_2RE'] = facts['2RE']
        del facts['2RE']

    facts['version_info'] = dict(facts['version_info'])

    if module.params['config']:
        config_format = module.params['config_format']
        resp_config = module.get_config( config_format=config_format)

        if config_format in ['text', 'set']:
           facts['config'] = resp_config
        elif config_format == "xml":
           facts['config'] = xml_to_string(resp_config)
           facts['config_json'] = xml_to_json(resp_config)

    result['ansible_facts'] = facts
    module.exit_json(**result)

from ansible.module_utils.basic import *
from ansible.module_utils.junos import *

if __name__ == '__main__':
    main()
<commit_msg>Update junos_facts.py (#3759)<commit_after>#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
#

DOCUMENTATION = """
---
module: junos_facts
version_added: "2.1"
author: "Peter Sprygada (@privateip)"
short_description: Collect facts from remote device running Junos
description:
  - Collects fact information from a remote device running the Junos
    operating system.  By default, the module will collect basic fact
    information from the device to be included with the hostvars.
    Additional fact information can be collected based on the
    configured set of arguments.
extends_documentation_fragment: junos
options:
  config:
    description:
      - The C(config) argument instructs the fact module to collect
        the configuration from the remote device.  The configuration
        is then included in return facts.  By default, the configuration
        is returned as text.  The C(config_format) can be used to return
        different Junos configuration formats.
    required: true
    default: false
  config_format:
    description:
      - The C(config_format) argument is used to specify the desired
        format of the configuration file.  Devices support three
        configuration file formats.  By default, the configuration
        from the device is returned as text.  The other options include
        set and xml.  If the xml option is choosen, the configuration file
        is returned as both xml and json.
    required: false
    default: text
    choices: ['xml', 'text', 'set']
requirements:
  - junos-eznc
notes:
  - This module requires the netconf system service be enabled on
    the remote device being managed
"""

EXAMPLES = """
# the required set of connection arguments have been purposely left off
# the examples for brevity

- name: collect default set of facts
  junos_facts:

- name: collect default set of facts and configuration
  junos_facts:
    config: yes

- name: collect default set of facts and configuration in set format
  junos_facts:
    config: yes
    config_format: set

- name: collect default set of facts and configuration in XML and JSON format
  junos_facts:
    config: yes
    config_format: xml
"""

RETURN = """
ansible_facts:
  descrption: Returns the facts collect from the device
  returned: always
  type: dict
"""

def main():
    """ Main entry point for AnsibleModule
    """
    spec = dict(
        config=dict(required=True, type='bool'),
        config_format=dict(default='text', choices=['xml', 'set', 'text']),
        transport=dict(default='netconf', choices=['netconf'])
    )

    module = get_module(argument_spec=spec,
                        supports_check_mode=True)

    result = dict(changed=False)

    facts = module.get_facts()

    if '2RE' in facts:
        facts['has_2RE'] = facts['2RE']
        del facts['2RE']

    facts['version_info'] = dict(facts['version_info'])

    if module.params['config']:
        config_format = module.params['config_format']
        resp_config = module.get_config( config_format=config_format)

        if config_format in ['text', 'set']:
           facts['config'] = resp_config
        elif config_format == "xml":
           facts['config'] = xml_to_string(resp_config)
           facts['config_json'] = xml_to_json(resp_config)

    result['ansible_facts'] = facts
    module.exit_json(**result)

from ansible.module_utils.basic import *
from ansible.module_utils.junos import *

if __name__ == '__main__':
    main()
<|endoftext|>