<commit_before># -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# async.py - Async callback handling (InProgress)
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# kaa.notifier - Mainloop and callbacks
# Copyright (C) 2006 Dirk Meyer, Jason Tackaberry, et al.
#
# First Version: Dirk Meyer <dmeyer@tzi.de>
# Maintainer:    Dirk Meyer <dmeyer@tzi.de>
#
# Please see the file AUTHORS for a complete list of authors.
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version
# 2.1 as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# -----------------------------------------------------------------------------

__all__ = [ 'InProgress' ]

# python imports
import logging
import traceback

# kaa.notifier imports
from callback import Signal

# get logging object
log = logging.getLogger('notifier.async')

class InProgress(Signal):
    """
    An InProgress class used to return from function calls
    that need more time to continue. It is possible to connect
    to an object of this class like Signals. The memeber 'exception_handler'
    is a second signal to get notification of an exception raised later.
    """
    def __init__(self):
        Signal.__init__(self)
        self.exception_handler = Signal()
        self.is_finished = False
        self.status = None

    def set_status(self, s):
        """
        Connect a status object to the InProgress object. The status object
        has to be updated by the creator of that object. The status should
        be a Signal so the monitoring function can connect to it to get
        notified on updates.
        """
        self.status = s

    def get_status(self):
        """
        Return status object if connected or return True if the function is
        still in progress or False if not.
        """
        if self.status is not None:
            return self.status
        return not is_finished

    def finished(self, result):
        """
        This function should be called when the creating function is
        done and no longer in progress.
        """
        if isinstance(result, InProgress):
            # we are still not finished, register to this result
            result.connect(self.finished)
            result.exception_handler.connect(self.exception)
            return
        # store result
        self.is_finished = True
        self._result = result
        self._exception = None
        # emit signal
        self.emit(result)
        # cleanup
        self._callbacks = []
        self.exception_handler = None

    def exception(self, e):
        """
        This function should be called when the creating function is
        done because it raised an exception.
        """
        if self.exception_handler.count() == 0:
            if hasattr(e, '_exc_info'):
                # Exception info set internally, so display traceback from that.
                trace = ''.join(traceback.format_exception(*e._exc_info))
            else:
                # No traceback set explicitly
                trace = '%s [no traceback available]' % repr(e)

            log.error('*** InProgress exception not handled ***\n%s', trace)
        # store result
        self.is_finished = True
        self._exception = e
        # emit signal
        self.exception_handler.emit(e)
        # cleanup
        self._callbacks = []
        self.exception_handler = None

    def __call__(self, *args, **kwargs):
        """
        You can call the InProgress object to get the results when finished.
        The function will either return the result or raise the exception
        provided to the exception function.
        """
        if not self.is_finished:
            raise RuntimeError('operation not finished')
        if self._exception:
            raise self._exception
        return self._result

    def get_result(self):
        """
        Get the results when finished.
        The function will either return the result or raise the exception
        provided to the exception function.
        """
        return self()

    def _connect(self, callback, args = (), kwargs = {}, once = False,
                 weak = False, pos = -1):
        """
        Internal connect function. Always set once to True because InProgress
        will be emited only once.
        """
        return Signal._connect(self, callback, args, kwargs, True, weak, pos)
<commit_msg>typo<commit_after># -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# async.py - Async callback handling (InProgress)
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# kaa.notifier - Mainloop and callbacks
# Copyright (C) 2006 Dirk Meyer, Jason Tackaberry, et al.
#
# First Version: Dirk Meyer <dmeyer@tzi.de>
# Maintainer:    Dirk Meyer <dmeyer@tzi.de>
#
# Please see the file AUTHORS for a complete list of authors.
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version
# 2.1 as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# -----------------------------------------------------------------------------

__all__ = [ 'InProgress' ]

# python imports
import logging
import traceback

# kaa.notifier imports
from callback import Signal

# get logging object
log = logging.getLogger('notifier.async')

class InProgress(Signal):
    """
    An InProgress class used to return from function calls
    that need more time to continue. It is possible to connect
    to an object of this class like Signals. The memeber 'exception_handler'
    is a second signal to get notification of an exception raised later.
    """
    def __init__(self):
        Signal.__init__(self)
        self.exception_handler = Signal()
        self.is_finished = False
        self.status = None

    def set_status(self, s):
        """
        Connect a status object to the InProgress object. The status object
        has to be updated by the creator of that object. The status should
        be a Signal so the monitoring function can connect to it to get
        notified on updates.
        """
        self.status = s

    def get_status(self):
        """
        Return status object if connected or return True if the function is
        still in progress or False if not.
        """
        if self.status is not None:
            return self.status
        return not self.is_finished

    def finished(self, result):
        """
        This function should be called when the creating function is
        done and no longer in progress.
        """
        if isinstance(result, InProgress):
            # we are still not finished, register to this result
            result.connect(self.finished)
            result.exception_handler.connect(self.exception)
            return
        # store result
        self.is_finished = True
        self._result = result
        self._exception = None
        # emit signal
        self.emit(result)
        # cleanup
        self._callbacks = []
        self.exception_handler = None

    def exception(self, e):
        """
        This function should be called when the creating function is
        done because it raised an exception.
        """
        if self.exception_handler.count() == 0:
            if hasattr(e, '_exc_info'):
                # Exception info set internally, so display traceback from that.
                trace = ''.join(traceback.format_exception(*e._exc_info))
            else:
                # No traceback set explicitly
                trace = '%s [no traceback available]' % repr(e)

            log.error('*** InProgress exception not handled ***\n%s', trace)
        # store result
        self.is_finished = True
        self._exception = e
        # emit signal
        self.exception_handler.emit(e)
        # cleanup
        self._callbacks = []
        self.exception_handler = None

    def __call__(self, *args, **kwargs):
        """
        You can call the InProgress object to get the results when finished.
        The function will either return the result or raise the exception
        provided to the exception function.
        """
        if not self.is_finished:
            raise RuntimeError('operation not finished')
        if self._exception:
            raise self._exception
        return self._result

    def get_result(self):
        """
        Get the results when finished.
        The function will either return the result or raise the exception
        provided to the exception function.
        """
        return self()

    def _connect(self, callback, args = (), kwargs = {}, once = False,
                 weak = False, pos = -1):
        """
        Internal connect function. Always set once to True because InProgress
        will be emited only once.
        """
        return Signal._connect(self, callback, args, kwargs, True, weak, pos)
<|endoftext|>


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

# Copyright: (c) 2014, 2015 YAEGASHI Takeshi <yaegashi@debian.org>
# Copyright: (c) 2017, Ansible Project
# 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
__metaclass__ = type

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

DOCUMENTATION = """
---
module: blockinfile
author:
    - YAEGASHI Takeshi (@yaegashi)
extends_documentation_fragment:
    - files
    - validate
short_description: Insert/update/remove a text block surrounded by marker lines
version_added: '2.0'
description:
  - This module will insert/update/remove a block of multi-line text
    surrounded by customizable marker lines.
options:
  path:
    description:
      - The file to modify.
      - Before 2.3 this option was only usable as I(dest), I(destfile) and I(name).
    aliases: [ dest, destfile, name ]
    required: true
  state:
    description:
      - Whether the block should be there or not.
    choices: [ absent, present ]
    default: present
  marker:
    description:
      - The marker line template.
        "{mark}" will be replaced with the values in marker_begin
        (default="BEGIN") and marker_end (default="END").
    default: '# {mark} ANSIBLE MANAGED BLOCK'
  block:
    description:
      - The text to insert inside the marker lines.
        If it's missing or an empty string,
        the block will be removed as if C(state) were specified to C(absent).
    aliases: [ content ]
    default: ''
  insertafter:
    description:
      - If specified, the block will be inserted after the last match of
        specified regular expression. A special value is available; C(EOF) for
        inserting the block at the end of the file.  If specified regular
        expression has no matches, C(EOF) will be used instead.
    default: EOF
    choices: [ EOF, '*regex*' ]
  insertbefore:
    description:
      - If specified, the block will be inserted before the last match of
        specified regular expression. A special value is available; C(BOF) for
        inserting the block at the beginning of the file.  If specified regular
        expression has no matches, the block will be inserted at the end of the
        file.
    choices: [ BOF, '*regex*' ]
  create:
    description:
      - Create a new file if it doesn't exist.
    type: bool
    default: 'no'
  backup:
    description:
      - Create a backup file including the timestamp information so you can
        get the original file back if you somehow clobbered it incorrectly.
    type: bool
    default: 'no'
  marker_begin:
    description:
      - This will be inserted at {mark} in the opening ansible block marker.
    default: 'BEGIN'
    version_added: "2.5"
  marker_end:
    required: false
    description:
      - This will be inserted at {mark} in the closing ansible block marker.
    default: 'END'
    version_added: "2.5"

notes:
  - This module supports check mode.
  - When using 'with_*' loops be aware that if you do not set a unique mark the block will be overwritten on each iteration.
  - As of Ansible 2.3, the I(dest) option has been changed to I(path) as default, but I(dest) still works as well.
  - Option I(follow) has been removed in version 2.5, because this module modifies the contents of the file so I(follow=no) doesn't make sense.
"""

EXAMPLES = r"""
# Before 2.3, option 'dest' or 'name' was used instead of 'path'
- name: insert/update "Match User" configuration block in /etc/ssh/sshd_config
  blockinfile:
    path: /etc/ssh/sshd_config
    block: |
      Match User ansible-agent
      PasswordAuthentication no

- name: insert/update eth0 configuration stanza in /etc/network/interfaces
        (it might be better to copy files into /etc/network/interfaces.d/)
  blockinfile:
    path: /etc/network/interfaces
    block: |
      iface eth0 inet static
          address 192.0.2.23
          netmask 255.255.255.0

- name: insert/update configuration using a local file and validate it
  blockinfile:
    block: "{{ lookup('file', './local/ssh_config') }}"
    dest: "/etc/ssh/ssh_config"
    backup: yes
    validate: "/usr/sbin/sshd -T -f %s"

- name: insert/update HTML surrounded by custom markers after  line
  blockinfile:
    path: /var/www/html/index.html
    marker: ""
    insertafter: ""
    content: |

Welcome to {{ ansible_hostname }}

Last updated on {{ ansible_date_time.iso8601 }}

- name: remove HTML as well as surrounding markers
  blockinfile:
    path: /var/www/html/index.html
    marker: ""
    content: ""

- name: Add mappings to /etc/hosts
  blockinfile:
    path: /etc/hosts
    block: |
      {{ item.ip }} {{ item.name }}
    marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.name }}"
  with_items:
    - { name: host1, ip: 10.10.1.10 }
    - { name: host2, ip: 10.10.1.11 }
    - { name: host3, ip: 10.10.1.12 }
"""

import re
import os
import tempfile
from ansible.module_utils.six import b
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_bytes

def write_changes(module, contents, path):

    tmpfd, tmpfile = tempfile.mkstemp(dir=getattr(module, 'tmpdir', None))
    f = os.fdopen(tmpfd, 'wb')
    f.write(contents)
    f.close()

    validate = module.params.get('validate', None)
    valid = not validate
    if validate:
        if "%s" not in validate:
            module.fail_json(msg="validate must contain %%s: %s" % (validate))
        (rc, out, err) = module.run_command(validate % tmpfile)
        valid = rc == 0
        if rc != 0:
            module.fail_json(msg='failed to validate: '
                                 'rc:%s error:%s' % (rc, err))
    if valid:
        module.atomic_move(tmpfile, path, unsafe_writes=module.params['unsafe_writes'])

def check_file_attrs(module, changed, message, diff):

    file_args = module.load_file_common_arguments(module.params)
    if module.set_file_attributes_if_different(file_args, False, diff=diff):

        if changed:
            message += " and "
        changed = True
        message += "ownership, perms or SE linux context changed"

    return message, changed

def main():
    module = AnsibleModule(
        argument_spec=dict(
            path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
            state=dict(type='str', default='present', choices=['absent', 'present']),
            marker=dict(type='str', default='# {mark} ANSIBLE MANAGED BLOCK'),
            block=dict(type='str', default='', aliases=['content']),
            insertafter=dict(type='str'),
            insertbefore=dict(type='str'),
            create=dict(type='bool', default=False),
            backup=dict(type='bool', default=False),
            validate=dict(type='str'),
            marker_begin=dict(type='str', default='BEGIN'),
            marker_end=dict(type='str', default='END'),
        ),
        mutually_exclusive=[['insertbefore', 'insertafter']],
        add_file_common_args=True,
        supports_check_mode=True
    )

    params = module.params
    path = params['path']

    if os.path.isdir(path):
        module.fail_json(rc=256,
                         msg='Path %s is a directory !' % path)

    path_exists = os.path.exists(path)
    if not path_exists:
        if not module.boolean(params['create']):
            module.fail_json(rc=257,
                             msg='Path %s does not exist !' % path)
        destpath = os.path.dirname(path)
        if not os.path.exists(destpath) and not module.check_mode:
            try:
                os.makedirs(destpath)
            except Exception as e:
                module.fail_json(msg='Error creating %s Error code: %s Error description: %s' % (destpath, e[0], e[1]))
        original = None
        lines = []
    else:
        f = open(path, 'rb')
        original = f.read()
        f.close()
        lines = original.splitlines()

    diff = {'before': '',
            'after': '',
            'before_header': '%s (content)' % path,
            'after_header': '%s (content)' % path}

    if module._diff and original:
        diff['before'] = original

    insertbefore = params['insertbefore']
    insertafter = params['insertafter']
    block = to_bytes(params['block'])
    marker = to_bytes(params['marker'])
    present = params['state'] == 'present'

    if not present and not path_exists:
        module.exit_json(changed=False, msg="File %s not present" % path)

    if insertbefore is None and insertafter is None:
        insertafter = 'EOF'

    if insertafter not in (None, 'EOF'):
        insertre = re.compile(to_bytes(insertafter, errors='surrogate_or_strict'))
    elif insertbefore not in (None, 'BOF'):
        insertre = re.compile(to_bytes(insertbefore, errors='surrogate_or_strict'))
    else:
        insertre = None

    marker0 = re.sub(b(r'{mark}'), b(params['marker_begin']), marker)
    marker1 = re.sub(b(r'{mark}'), b(params['marker_end']), marker)
    if present and block:
        # Escape seqeuences like '\n' need to be handled in Ansible 1.x
        if module.ansible_version.startswith('1.'):
            block = re.sub('', block, '')
        blocklines = [marker0] + block.splitlines() + [marker1]
    else:
        blocklines = []

    n0 = n1 = None
    for i, line in enumerate(lines):
        if line == marker0:
            n0 = i
        if line == marker1:
            n1 = i

    if None in (n0, n1):
        n0 = None
        if insertre is not None:
            for i, line in enumerate(lines):
                if insertre.search(line):
                    n0 = i
            if n0 is None:
                n0 = len(lines)
            elif insertafter is not None:
                n0 += 1
        elif insertbefore is not None:
            n0 = 0  # insertbefore=BOF
        else:
            n0 = len(lines)  # insertafter=EOF
    elif n0 < n1:
        lines[n0:n1 + 1] = []
    else:
        lines[n1:n0 + 1] = []
        n0 = n1

    lines[n0:n0] = blocklines

    if lines:
        result = b('\n').join(lines)
        if original is None or original.endswith(b('\n')):
            result += b('\n')
    else:
        result = ''

    if module._diff:
        diff['after'] = result

    if original == result:
        msg = ''
        changed = False
    elif original is None:
        msg = 'File created'
        changed = True
    elif not blocklines:
        msg = 'Block removed'
        changed = True
    else:
        msg = 'Block inserted'
        changed = True

    if changed and not module.check_mode:
        if module.boolean(params['backup']) and path_exists:
            module.backup_local(path)
        # We should always follow symlinks so that we change the real file
        real_path = os.path.realpath(params['path'])
        write_changes(module, result, real_path)

    if module.check_mode and not path_exists:
        module.exit_json(changed=changed, msg=msg, diff=diff)

    attr_diff = {}
    msg, changed = check_file_attrs(module, changed, msg, attr_diff)

    attr_diff['before_header'] = '%s (file attributes)' % path
    attr_diff['after_header'] = '%s (file attributes)' % path

    difflist = [diff, attr_diff]
    module.exit_json(changed=changed, msg=msg, diff=difflist)

if __name__ == '__main__':
    main()
<commit_msg>Add a note for multible changes in one file (#37090)<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2014, 2015 YAEGASHI Takeshi <yaegashi@debian.org>
# Copyright: (c) 2017, Ansible Project
# 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
__metaclass__ = type

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

DOCUMENTATION = """
---
module: blockinfile
author:
    - YAEGASHI Takeshi (@yaegashi)
extends_documentation_fragment:
    - files
    - validate
short_description: Insert/update/remove a text block surrounded by marker lines
version_added: '2.0'
description:
  - This module will insert/update/remove a block of multi-line text
    surrounded by customizable marker lines.
options:
  path:
    description:
      - The file to modify.
      - Before 2.3 this option was only usable as I(dest), I(destfile) and I(name).
    aliases: [ dest, destfile, name ]
    required: true
  state:
    description:
      - Whether the block should be there or not.
    choices: [ absent, present ]
    default: present
  marker:
    description:
      - The marker line template.
        "{mark}" will be replaced with the values in marker_begin
        (default="BEGIN") and marker_end (default="END").
    default: '# {mark} ANSIBLE MANAGED BLOCK'
  block:
    description:
      - The text to insert inside the marker lines.
        If it's missing or an empty string,
        the block will be removed as if C(state) were specified to C(absent).
    aliases: [ content ]
    default: ''
  insertafter:
    description:
      - If specified, the block will be inserted after the last match of
        specified regular expression. A special value is available; C(EOF) for
        inserting the block at the end of the file.  If specified regular
        expression has no matches, C(EOF) will be used instead.
    default: EOF
    choices: [ EOF, '*regex*' ]
  insertbefore:
    description:
      - If specified, the block will be inserted before the last match of
        specified regular expression. A special value is available; C(BOF) for
        inserting the block at the beginning of the file.  If specified regular
        expression has no matches, the block will be inserted at the end of the
        file.
    choices: [ BOF, '*regex*' ]
  create:
    description:
      - Create a new file if it doesn't exist.
    type: bool
    default: 'no'
  backup:
    description:
      - Create a backup file including the timestamp information so you can
        get the original file back if you somehow clobbered it incorrectly.
    type: bool
    default: 'no'
  marker_begin:
    description:
      - This will be inserted at {mark} in the opening ansible block marker.
    default: 'BEGIN'
    version_added: "2.5"
  marker_end:
    required: false
    description:
      - This will be inserted at {mark} in the closing ansible block marker.
    default: 'END'
    version_added: "2.5"

notes:
  - This module supports check mode.
  - When using 'with_*' loops be aware that if you do not set a unique mark the block will be overwritten on each iteration.
  - As of Ansible 2.3, the I(dest) option has been changed to I(path) as default, but I(dest) still works as well.
  - Option I(follow) has been removed in version 2.5, because this module modifies the contents of the file so I(follow=no) doesn't make sense.
  - When more then one block should be handled in **one** file you **must** change the I(marker) per task
"""

EXAMPLES = r"""
# Before 2.3, option 'dest' or 'name' was used instead of 'path'
- name: insert/update "Match User" configuration block in /etc/ssh/sshd_config
  blockinfile:
    path: /etc/ssh/sshd_config
    block: |
      Match User ansible-agent
      PasswordAuthentication no

- name: insert/update eth0 configuration stanza in /etc/network/interfaces
        (it might be better to copy files into /etc/network/interfaces.d/)
  blockinfile:
    path: /etc/network/interfaces
    block: |
      iface eth0 inet static
          address 192.0.2.23
          netmask 255.255.255.0

- name: insert/update configuration using a local file and validate it
  blockinfile:
    block: "{{ lookup('file', './local/ssh_config') }}"
    dest: "/etc/ssh/ssh_config"
    backup: yes
    validate: "/usr/sbin/sshd -T -f %s"

- name: insert/update HTML surrounded by custom markers after  line
  blockinfile:
    path: /var/www/html/index.html
    marker: ""
    insertafter: ""
    content: |

Welcome to {{ ansible_hostname }}

Last updated on {{ ansible_date_time.iso8601 }}

- name: remove HTML as well as surrounding markers
  blockinfile:
    path: /var/www/html/index.html
    marker: ""
    content: ""

- name: Add mappings to /etc/hosts
  blockinfile:
    path: /etc/hosts
    block: |
      {{ item.ip }} {{ item.name }}
    marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.name }}"
  with_items:
    - { name: host1, ip: 10.10.1.10 }
    - { name: host2, ip: 10.10.1.11 }
    - { name: host3, ip: 10.10.1.12 }
"""

import re
import os
import tempfile
from ansible.module_utils.six import b
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_bytes

def write_changes(module, contents, path):

    tmpfd, tmpfile = tempfile.mkstemp(dir=getattr(module, 'tmpdir', None))
    f = os.fdopen(tmpfd, 'wb')
    f.write(contents)
    f.close()

    validate = module.params.get('validate', None)
    valid = not validate
    if validate:
        if "%s" not in validate:
            module.fail_json(msg="validate must contain %%s: %s" % (validate))
        (rc, out, err) = module.run_command(validate % tmpfile)
        valid = rc == 0
        if rc != 0:
            module.fail_json(msg='failed to validate: '
                                 'rc:%s error:%s' % (rc, err))
    if valid:
        module.atomic_move(tmpfile, path, unsafe_writes=module.params['unsafe_writes'])

def check_file_attrs(module, changed, message, diff):

    file_args = module.load_file_common_arguments(module.params)
    if module.set_file_attributes_if_different(file_args, False, diff=diff):

        if changed:
            message += " and "
        changed = True
        message += "ownership, perms or SE linux context changed"

    return message, changed

def main():
    module = AnsibleModule(
        argument_spec=dict(
            path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
            state=dict(type='str', default='present', choices=['absent', 'present']),
            marker=dict(type='str', default='# {mark} ANSIBLE MANAGED BLOCK'),
            block=dict(type='str', default='', aliases=['content']),
            insertafter=dict(type='str'),
            insertbefore=dict(type='str'),
            create=dict(type='bool', default=False),
            backup=dict(type='bool', default=False),
            validate=dict(type='str'),
            marker_begin=dict(type='str', default='BEGIN'),
            marker_end=dict(type='str', default='END'),
        ),
        mutually_exclusive=[['insertbefore', 'insertafter']],
        add_file_common_args=True,
        supports_check_mode=True
    )

    params = module.params
    path = params['path']

    if os.path.isdir(path):
        module.fail_json(rc=256,
                         msg='Path %s is a directory !' % path)

    path_exists = os.path.exists(path)
    if not path_exists:
        if not module.boolean(params['create']):
            module.fail_json(rc=257,
                             msg='Path %s does not exist !' % path)
        destpath = os.path.dirname(path)
        if not os.path.exists(destpath) and not module.check_mode:
            try:
                os.makedirs(destpath)
            except Exception as e:
                module.fail_json(msg='Error creating %s Error code: %s Error description: %s' % (destpath, e[0], e[1]))
        original = None
        lines = []
    else:
        f = open(path, 'rb')
        original = f.read()
        f.close()
        lines = original.splitlines()

    diff = {'before': '',
            'after': '',
            'before_header': '%s (content)' % path,
            'after_header': '%s (content)' % path}

    if module._diff and original:
        diff['before'] = original

    insertbefore = params['insertbefore']
    insertafter = params['insertafter']
    block = to_bytes(params['block'])
    marker = to_bytes(params['marker'])
    present = params['state'] == 'present'

    if not present and not path_exists:
        module.exit_json(changed=False, msg="File %s not present" % path)

    if insertbefore is None and insertafter is None:
        insertafter = 'EOF'

    if insertafter not in (None, 'EOF'):
        insertre = re.compile(to_bytes(insertafter, errors='surrogate_or_strict'))
    elif insertbefore not in (None, 'BOF'):
        insertre = re.compile(to_bytes(insertbefore, errors='surrogate_or_strict'))
    else:
        insertre = None

    marker0 = re.sub(b(r'{mark}'), b(params['marker_begin']), marker)
    marker1 = re.sub(b(r'{mark}'), b(params['marker_end']), marker)
    if present and block:
        # Escape seqeuences like '\n' need to be handled in Ansible 1.x
        if module.ansible_version.startswith('1.'):
            block = re.sub('', block, '')
        blocklines = [marker0] + block.splitlines() + [marker1]
    else:
        blocklines = []

    n0 = n1 = None
    for i, line in enumerate(lines):
        if line == marker0:
            n0 = i
        if line == marker1:
            n1 = i

    if None in (n0, n1):
        n0 = None
        if insertre is not None:
            for i, line in enumerate(lines):
                if insertre.search(line):
                    n0 = i
            if n0 is None:
                n0 = len(lines)
            elif insertafter is not None:
                n0 += 1
        elif insertbefore is not None:
            n0 = 0  # insertbefore=BOF
        else:
            n0 = len(lines)  # insertafter=EOF
    elif n0 < n1:
        lines[n0:n1 + 1] = []
    else:
        lines[n1:n0 + 1] = []
        n0 = n1

    lines[n0:n0] = blocklines

    if lines:
        result = b('\n').join(lines)
        if original is None or original.endswith(b('\n')):
            result += b('\n')
    else:
        result = ''

    if module._diff:
        diff['after'] = result

    if original == result:
        msg = ''
        changed = False
    elif original is None:
        msg = 'File created'
        changed = True
    elif not blocklines:
        msg = 'Block removed'
        changed = True
    else:
        msg = 'Block inserted'
        changed = True

    if changed and not module.check_mode:
        if module.boolean(params['backup']) and path_exists:
            module.backup_local(path)
        # We should always follow symlinks so that we change the real file
        real_path = os.path.realpath(params['path'])
        write_changes(module, result, real_path)

    if module.check_mode and not path_exists:
        module.exit_json(changed=changed, msg=msg, diff=diff)

    attr_diff = {}
    msg, changed = check_file_attrs(module, changed, msg, attr_diff)

    attr_diff['before_header'] = '%s (file attributes)' % path
    attr_diff['after_header'] = '%s (file attributes)' % path

    difflist = [diff, attr_diff]
    module.exit_json(changed=changed, msg=msg, diff=difflist)

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


<commit_before>#!/usr/bin/python

# Copyright: (c) 2016, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
__metaclass__ = type

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

DOCUMENTATION = r'''
---
module: sefcontext
short_description: Manages SELinux file context mapping definitions
description:
- Manages SELinux file context mapping definitions.
- Similar to the C(semanage fcontext) command.
version_added: '2.2'
options:
  target:
    description:
    - Target path (expression).
    type: str
    required: yes
    aliases: [ path ]
  ftype:
    description:
    - File type.
    - The following file type options can be passed;
      C(a) for all files,
      C(b) for block devices,
      C(c) for character devices,
      C(d) for directories,
      C(f) for regular files,
      C(l) for symbolic links,
      C(p) for named pipes,
      C(s) for socket files.
    type: str
    default: a
  setype:
    description:
    - SELinux type for the specified target.
    required: yes
  seuser:
    description:
    - SELinux user for the specified target.
    type: str
  selevel:
    description:
    - SELinux range for the specified target.
    type: str
    aliases: [ serange ]
  state:
    description:
    - Whether the SELinux file context must be C(absent) or C(present).
    type: str
    choices: [ absent, present ]
    default: present
  reload:
    description:
    - Reload SELinux policy after commit.
    - Note that this does not apply SELinux file contexts to existing files.
    type: bool
    default: 'yes'
notes:
- The changes are persistent across reboots.
- The M(sefcontext) module does not modify existing files to the new
  SELinux context(s), so it is advisable to first create the SELinux
  file contexts before creating files, or run C(restorecon) manually
  for the existing files that require the new SELinux file contexts.
- Not applying SELinux fcontexts to existing files is a deliberate
  decision as it would be unclear what reported changes would entail
  to, and there's no guarantee that applying SELinux fcontext does
  not pick up other unrelated prior changes.
requirements:
- libselinux-python
- policycoreutils-python
author:
- Dag Wieers (@dagwieers)
'''

EXAMPLES = r'''
- name: Allow apache to modify files in /srv/git_repos
  sefcontext:
    target: '/srv/git_repos(/.*)?'
    setype: httpd_git_rw_content_t
    state: present

- name: Apply new SELinux file context to filesystem
  command: restorecon -irv /srv/git_repos
'''

RETURN = r'''
# Default return values
'''

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.pycompat24 import get_exception
from ansible.module_utils._text import to_native

try:
    import selinux
    HAVE_SELINUX = True
except ImportError:
    HAVE_SELINUX = False

try:
    import seobject
    HAVE_SEOBJECT = True
except ImportError:
    HAVE_SEOBJECT = False

# Add missing entries (backward compatible)
if HAVE_SEOBJECT:
    seobject.file_types.update(dict(
        a=seobject.SEMANAGE_FCONTEXT_ALL,
        b=seobject.SEMANAGE_FCONTEXT_BLOCK,
        c=seobject.SEMANAGE_FCONTEXT_CHAR,
        d=seobject.SEMANAGE_FCONTEXT_DIR,
        f=seobject.SEMANAGE_FCONTEXT_REG,
        l=seobject.SEMANAGE_FCONTEXT_LINK,
        p=seobject.SEMANAGE_FCONTEXT_PIPE,
        s=seobject.SEMANAGE_FCONTEXT_SOCK,
    ))

# Make backward compatible
option_to_file_type_str = dict(
    a='all files',
    b='block device',
    c='character device',
    d='directory',
    f='regular file',
    l='symbolic link',
    p='named pipe',
    s='socket file',
)

def semanage_fcontext_exists(sefcontext, target, ftype):
    ''' Get the SELinux file context mapping definition from policy. Return None if it does not exist. '''

    # Beware that records comprise of a string representation of the file_type
    record = (target, option_to_file_type_str[ftype])
    records = sefcontext.get_all()
    try:
        return records[record]
    except KeyError:
        return None

def semanage_fcontext_modify(module, result, target, ftype, setype, do_reload, serange, seuser, sestore=''):
    ''' Add or modify SELinux file context mapping definition to the policy. '''

    changed = False
    prepared_diff = ''

    try:
        sefcontext = seobject.fcontextRecords(sestore)
        sefcontext.set_reload(do_reload)
        exists = semanage_fcontext_exists(sefcontext, target, ftype)
        if exists:
            # Modify existing entry
            orig_seuser, orig_serole, orig_setype, orig_serange = exists

            if seuser is None:
                seuser = orig_seuser
            if serange is None:
                serange = orig_serange

            if setype != orig_setype or seuser != orig_seuser or serange != orig_serange:
                if not module.check_mode:
                    sefcontext.modify(target, setype, ftype, serange, seuser)
                changed = True

                if module._diff:
                    prepared_diff += '# Change to semanage file context mappings\n'
                    prepared_diff += '-%s      %s      %s:%s:%s:%s\n' % (target, ftype, orig_seuser, orig_serole, orig_setype, orig_serange)
                    prepared_diff += '+%s      %s      %s:%s:%s:%s\n' % (target, ftype, seuser, orig_serole, setype, serange)
        else:
            # Add missing entry
            if seuser is None:
                seuser = 'system_u'
            if serange is None:
                serange = 's0'

            if not module.check_mode:
                sefcontext.add(target, setype, ftype, serange, seuser)
            changed = True

            if module._diff:
                prepared_diff += '# Addition to semanage file context mappings\n'
                prepared_diff += '+%s      %s      %s:%s:%s:%s\n' % (target, ftype, seuser, 'object_r', setype, serange)

    except Exception:
        e = get_exception()
        module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, to_native(e)))

    if module._diff and prepared_diff:
        result['diff'] = dict(prepared=prepared_diff)

    module.exit_json(changed=changed, seuser=seuser, serange=serange, **result)

def semanage_fcontext_delete(module, result, target, ftype, do_reload, sestore=''):
    ''' Delete SELinux file context mapping definition from the policy. '''

    changed = False
    prepared_diff = ''

    try:
        sefcontext = seobject.fcontextRecords(sestore)
        sefcontext.set_reload(do_reload)
        exists = semanage_fcontext_exists(sefcontext, target, ftype)
        if exists:
            # Remove existing entry
            orig_seuser, orig_serole, orig_setype, orig_serange = exists

            if not module.check_mode:
                sefcontext.delete(target, ftype)
            changed = True

            if module._diff:
                prepared_diff += '# Deletion to semanage file context mappings\n'
                prepared_diff += '-%s      %s      %s:%s:%s:%s\n' % (target, ftype, exists[0], exists[1], exists[2], exists[3])

    except Exception:
        e = get_exception()
        module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, to_native(e)))

    if module._diff and prepared_diff:
        result['diff'] = dict(prepared=prepared_diff)

    module.exit_json(changed=changed, **result)

def main():
    module = AnsibleModule(
        argument_spec=dict(
            target=dict(required=True, aliases=['path']),
            ftype=dict(type='str', default='a', choices=option_to_file_type_str.keys()),
            setype=dict(type='str', required=True),
            seuser=dict(type='str'),
            selevel=dict(type='str', aliases=['serange']),
            state=dict(type='str', default='present', choices=['absent', 'present']),
            reload=dict(type='bool', default=True),
        ),
        supports_check_mode=True,
    )
    if not HAVE_SELINUX:
        module.fail_json(msg="This module requires libselinux-python")

    if not HAVE_SEOBJECT:
        module.fail_json(msg="This module requires policycoreutils-python")

    if not selinux.is_selinux_enabled():
        module.fail_json(msg="SELinux is disabled on this host.")

    target = module.params['target']
    ftype = module.params['ftype']
    setype = module.params['setype']
    seuser = module.params['seuser']
    serange = module.params['selevel']
    state = module.params['state']
    do_reload = module.params['reload']

    result = dict(target=target, ftype=ftype, setype=setype, state=state)

    if state == 'present':
        semanage_fcontext_modify(module, result, target, ftype, setype, do_reload, serange, seuser)
    elif state == 'absent':
        semanage_fcontext_delete(module, result, target, ftype, do_reload)
    else:
        module.fail_json(msg='Invalid value of argument "state": {0}'.format(state))

if __name__ == '__main__':
    main()
<commit_msg>Docs: Fix ftype list in sefcontext module (#46364)<commit_after>#!/usr/bin/python

# Copyright: (c) 2016, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
__metaclass__ = type

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

DOCUMENTATION = r'''
---
module: sefcontext
short_description: Manages SELinux file context mapping definitions
description:
- Manages SELinux file context mapping definitions.
- Similar to the C(semanage fcontext) command.
version_added: '2.2'
options:
  target:
    description:
    - Target path (expression).
    type: str
    required: yes
    aliases: [ path ]
  ftype:
    description:
    - The file type that should have SELinux contexts applied.
    - "The following file type options are available:"
    - C(a) for all files,
    - C(b) for block devices,
    - C(c) for character devices,
    - C(d) for directories,
    - C(f) for regular files,
    - C(l) for symbolic links,
    - C(p) for named pipes,
    - C(s) for socket files.
    type: str
    default: a
  setype:
    description:
    - SELinux type for the specified target.
    required: yes
  seuser:
    description:
    - SELinux user for the specified target.
    type: str
  selevel:
    description:
    - SELinux range for the specified target.
    type: str
    aliases: [ serange ]
  state:
    description:
    - Whether the SELinux file context must be C(absent) or C(present).
    type: str
    choices: [ absent, present ]
    default: present
  reload:
    description:
    - Reload SELinux policy after commit.
    - Note that this does not apply SELinux file contexts to existing files.
    type: bool
    default: 'yes'
notes:
- The changes are persistent across reboots.
- The M(sefcontext) module does not modify existing files to the new
  SELinux context(s), so it is advisable to first create the SELinux
  file contexts before creating files, or run C(restorecon) manually
  for the existing files that require the new SELinux file contexts.
- Not applying SELinux fcontexts to existing files is a deliberate
  decision as it would be unclear what reported changes would entail
  to, and there's no guarantee that applying SELinux fcontext does
  not pick up other unrelated prior changes.
requirements:
- libselinux-python
- policycoreutils-python
author:
- Dag Wieers (@dagwieers)
'''

EXAMPLES = r'''
- name: Allow apache to modify files in /srv/git_repos
  sefcontext:
    target: '/srv/git_repos(/.*)?'
    setype: httpd_git_rw_content_t
    state: present

- name: Apply new SELinux file context to filesystem
  command: restorecon -irv /srv/git_repos
'''

RETURN = r'''
# Default return values
'''

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.pycompat24 import get_exception
from ansible.module_utils._text import to_native

try:
    import selinux
    HAVE_SELINUX = True
except ImportError:
    HAVE_SELINUX = False

try:
    import seobject
    HAVE_SEOBJECT = True
except ImportError:
    HAVE_SEOBJECT = False

# Add missing entries (backward compatible)
if HAVE_SEOBJECT:
    seobject.file_types.update(dict(
        a=seobject.SEMANAGE_FCONTEXT_ALL,
        b=seobject.SEMANAGE_FCONTEXT_BLOCK,
        c=seobject.SEMANAGE_FCONTEXT_CHAR,
        d=seobject.SEMANAGE_FCONTEXT_DIR,
        f=seobject.SEMANAGE_FCONTEXT_REG,
        l=seobject.SEMANAGE_FCONTEXT_LINK,
        p=seobject.SEMANAGE_FCONTEXT_PIPE,
        s=seobject.SEMANAGE_FCONTEXT_SOCK,
    ))

# Make backward compatible
option_to_file_type_str = dict(
    a='all files',
    b='block device',
    c='character device',
    d='directory',
    f='regular file',
    l='symbolic link',
    p='named pipe',
    s='socket file',
)

def semanage_fcontext_exists(sefcontext, target, ftype):
    ''' Get the SELinux file context mapping definition from policy. Return None if it does not exist. '''

    # Beware that records comprise of a string representation of the file_type
    record = (target, option_to_file_type_str[ftype])
    records = sefcontext.get_all()
    try:
        return records[record]
    except KeyError:
        return None

def semanage_fcontext_modify(module, result, target, ftype, setype, do_reload, serange, seuser, sestore=''):
    ''' Add or modify SELinux file context mapping definition to the policy. '''

    changed = False
    prepared_diff = ''

    try:
        sefcontext = seobject.fcontextRecords(sestore)
        sefcontext.set_reload(do_reload)
        exists = semanage_fcontext_exists(sefcontext, target, ftype)
        if exists:
            # Modify existing entry
            orig_seuser, orig_serole, orig_setype, orig_serange = exists

            if seuser is None:
                seuser = orig_seuser
            if serange is None:
                serange = orig_serange

            if setype != orig_setype or seuser != orig_seuser or serange != orig_serange:
                if not module.check_mode:
                    sefcontext.modify(target, setype, ftype, serange, seuser)
                changed = True

                if module._diff:
                    prepared_diff += '# Change to semanage file context mappings\n'
                    prepared_diff += '-%s      %s      %s:%s:%s:%s\n' % (target, ftype, orig_seuser, orig_serole, orig_setype, orig_serange)
                    prepared_diff += '+%s      %s      %s:%s:%s:%s\n' % (target, ftype, seuser, orig_serole, setype, serange)
        else:
            # Add missing entry
            if seuser is None:
                seuser = 'system_u'
            if serange is None:
                serange = 's0'

            if not module.check_mode:
                sefcontext.add(target, setype, ftype, serange, seuser)
            changed = True

            if module._diff:
                prepared_diff += '# Addition to semanage file context mappings\n'
                prepared_diff += '+%s      %s      %s:%s:%s:%s\n' % (target, ftype, seuser, 'object_r', setype, serange)

    except Exception:
        e = get_exception()
        module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, to_native(e)))

    if module._diff and prepared_diff:
        result['diff'] = dict(prepared=prepared_diff)

    module.exit_json(changed=changed, seuser=seuser, serange=serange, **result)

def semanage_fcontext_delete(module, result, target, ftype, do_reload, sestore=''):
    ''' Delete SELinux file context mapping definition from the policy. '''

    changed = False
    prepared_diff = ''

    try:
        sefcontext = seobject.fcontextRecords(sestore)
        sefcontext.set_reload(do_reload)
        exists = semanage_fcontext_exists(sefcontext, target, ftype)
        if exists:
            # Remove existing entry
            orig_seuser, orig_serole, orig_setype, orig_serange = exists

            if not module.check_mode:
                sefcontext.delete(target, ftype)
            changed = True

            if module._diff:
                prepared_diff += '# Deletion to semanage file context mappings\n'
                prepared_diff += '-%s      %s      %s:%s:%s:%s\n' % (target, ftype, exists[0], exists[1], exists[2], exists[3])

    except Exception:
        e = get_exception()
        module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, to_native(e)))

    if module._diff and prepared_diff:
        result['diff'] = dict(prepared=prepared_diff)

    module.exit_json(changed=changed, **result)

def main():
    module = AnsibleModule(
        argument_spec=dict(
            target=dict(required=True, aliases=['path']),
            ftype=dict(type='str', default='a', choices=option_to_file_type_str.keys()),
            setype=dict(type='str', required=True),
            seuser=dict(type='str'),
            selevel=dict(type='str', aliases=['serange']),
            state=dict(type='str', default='present', choices=['absent', 'present']),
            reload=dict(type='bool', default=True),
        ),
        supports_check_mode=True,
    )
    if not HAVE_SELINUX:
        module.fail_json(msg="This module requires libselinux-python")

    if not HAVE_SEOBJECT:
        module.fail_json(msg="This module requires policycoreutils-python")

    if not selinux.is_selinux_enabled():
        module.fail_json(msg="SELinux is disabled on this host.")

    target = module.params['target']
    ftype = module.params['ftype']
    setype = module.params['setype']
    seuser = module.params['seuser']
    serange = module.params['selevel']
    state = module.params['state']
    do_reload = module.params['reload']

    result = dict(target=target, ftype=ftype, setype=setype, state=state)

    if state == 'present':
        semanage_fcontext_modify(module, result, target, ftype, setype, do_reload, serange, seuser)
    elif state == 'absent':
        semanage_fcontext_delete(module, result, target, ftype, do_reload)
    else:
        module.fail_json(msg='Invalid value of argument "state": {0}'.format(state))

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


<commit_before>import os

# Use 'dev', 'beta', or 'final' as the 4th element to indicate release type.

VERSION = (1, 0, 1, 'machtfit', 30)

def get_short_version():
    return '%s.%s' % (VERSION[0], VERSION[1])

def get_version():
    return '{}.{}.{}-{}-{}'.format(*VERSION)

# Cheeky setting that allows each template to be accessible by two paths.
# Eg: the template 'oscar/templates/oscar/base.html' can be accessed via both
# 'base.html' and 'oscar/base.html'.  This allows Oscar's templates to be
# extended by templates with the same filename
OSCAR_MAIN_TEMPLATE_DIR = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), 'templates/oscar')

OSCAR_CORE_APPS = [
    'oscar',
    'oscar.apps.analytics',
    'oscar.apps.checkout',
    'oscar.apps.address',
    'oscar.apps.shipping',
    'oscar.apps.catalogue',
    'oscar.apps.catalogue.reviews',
    'oscar.apps.partner',
    'oscar.apps.basket',
    'oscar.apps.payment',
    'oscar.apps.offer',
    'oscar.apps.order',
    'oscar.apps.customer',
    'oscar.apps.promotions',
    'oscar.apps.voucher',
    'oscar.apps.wishlists',
    'oscar.apps.dashboard',
    'oscar.apps.dashboard.reports',
    'oscar.apps.dashboard.users',
    'oscar.apps.dashboard.orders',
    'oscar.apps.dashboard.promotions',
    'oscar.apps.dashboard.catalogue',
    'oscar.apps.dashboard.offers',
    'oscar.apps.dashboard.partners',
    'oscar.apps.dashboard.pages',
    'oscar.apps.dashboard.ranges',
    'oscar.apps.dashboard.reviews',
    'oscar.apps.dashboard.vouchers',
    'oscar.apps.dashboard.communications',
    # 3rd-party apps that oscar depends on
    'treebeard',
    'sorl.thumbnail',
    'django_tables2',
]

def get_core_apps(overrides=None):
    """
    Return a list of oscar's apps amended with any passed overrides
    """
    if not overrides:
        return OSCAR_CORE_APPS

    def get_app_label(app_label, overrides):
        pattern = app_label.replace('oscar.apps.', '')
        for override in overrides:
            if override.endswith(pattern):
                if 'dashboard' in override and 'dashboard' not in pattern:
                    continue
                return override
        return app_label

    apps = []
    for app_label in OSCAR_CORE_APPS:
        apps.append(get_app_label(app_label, overrides))
    return apps
<commit_msg>Bump version to 1.0.1-machtfit-31<commit_after>import os

# Use 'dev', 'beta', or 'final' as the 4th element to indicate release type.

VERSION = (1, 0, 1, 'machtfit', 31)

def get_short_version():
    return '%s.%s' % (VERSION[0], VERSION[1])

def get_version():
    return '{}.{}.{}-{}-{}'.format(*VERSION)

# Cheeky setting that allows each template to be accessible by two paths.
# Eg: the template 'oscar/templates/oscar/base.html' can be accessed via both
# 'base.html' and 'oscar/base.html'.  This allows Oscar's templates to be
# extended by templates with the same filename
OSCAR_MAIN_TEMPLATE_DIR = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), 'templates/oscar')

OSCAR_CORE_APPS = [
    'oscar',
    'oscar.apps.analytics',
    'oscar.apps.checkout',
    'oscar.apps.address',
    'oscar.apps.shipping',
    'oscar.apps.catalogue',
    'oscar.apps.catalogue.reviews',
    'oscar.apps.partner',
    'oscar.apps.basket',
    'oscar.apps.payment',
    'oscar.apps.offer',
    'oscar.apps.order',
    'oscar.apps.customer',
    'oscar.apps.promotions',
    'oscar.apps.voucher',
    'oscar.apps.wishlists',
    'oscar.apps.dashboard',
    'oscar.apps.dashboard.reports',
    'oscar.apps.dashboard.users',
    'oscar.apps.dashboard.orders',
    'oscar.apps.dashboard.promotions',
    'oscar.apps.dashboard.catalogue',
    'oscar.apps.dashboard.offers',
    'oscar.apps.dashboard.partners',
    'oscar.apps.dashboard.pages',
    'oscar.apps.dashboard.ranges',
    'oscar.apps.dashboard.reviews',
    'oscar.apps.dashboard.vouchers',
    'oscar.apps.dashboard.communications',
    # 3rd-party apps that oscar depends on
    'treebeard',
    'sorl.thumbnail',
    'django_tables2',
]

def get_core_apps(overrides=None):
    """
    Return a list of oscar's apps amended with any passed overrides
    """
    if not overrides:
        return OSCAR_CORE_APPS

    def get_app_label(app_label, overrides):
        pattern = app_label.replace('oscar.apps.', '')
        for override in overrides:
            if override.endswith(pattern):
                if 'dashboard' in override and 'dashboard' not in pattern:
                    continue
                return override
        return app_label

    apps = []
    for app_label in OSCAR_CORE_APPS:
        apps.append(get_app_label(app_label, overrides))
    return apps
<|endoftext|>


<commit_before>import time

from .worker import DNSResolver
from exaproxy.network.functions import isip

class ResolverManager(object):
	resolver_factory = DNSResolver()

	def __init__(self, poller, configuration):
		self.poller = poller
		self.configuration = configuration # needed when creating new resolver instances
		self.resolv = configuration.dns.resolver
		self.timeout = configuration.dns.timeout

		# The actual work is done in the worker
		self.worker = self.resolver_factory.createUDPClient(configuration, self.resolv)

		# All currently active clients (UDP and TCP)
		self.workers = {}
		self.workers[self.worker.socket] = self.worker
                self.poller.addReadSocket('read_resolver', self.worker.socket)

		# Track the clients currently expecting results
		self.clients = {}

		# Key should be the hostname rather than the request ID?
		self.resolving = {}

		# TCP workers that have not yet sent a complete request
		self.sending = {}

		# track the current queries and when they were started
		self.active = []

		self.cache = {}
		self.cached = []

	def cacheDestination(self, hostname, ip):
		if hostname not in self.cache:
			self.cache[hostname] = ip
			self.cached.append((time.time(), hostname))

	def expireCache(self):
		if not self.cached:
			return

		count = len(self.cached)
		stop = min(count, self.configuration.dns.expire)
		position = stop-1

		cutoff = time.time() - self.configuration.dns.ttl

		while position > 10:
			timestamp, hostname = self.cached[position]
			if timestamp > cutoff:
				break

			position = int(position/1.3)
		else:
			position = 0

		for timestamp, hostname in self.cached[position:stop]:
			if timestamp > cutoff:
				break

			position += 1

			self.cache.pop(hostname, None)

		if position:
			self.cached = self.cached[position:]

	def cleanup(self):
		cutoff = time.time() - self.timeout
		count = 0

		for timestamp, client_id in self.active:
			if timestamp > cutoff:
				break

			count += 1
			identifier = self.clients.get(client_id)

			if identifier is not None:
				data = self.resolving.pop(identifier, None)
				if not data:
					data = self.sending.pop(identifier, None)

				if data:
					client_id, original, hostname, command, decision = data
					yield client_id, 'rewrite', '\0'.join(('503', 'dns.html', '', '', '', hostname, 'peer'))

		if count:
			self.active = self.active[count:]

	def resolves(self, command, decision):
		if command in ('download', 'connect'):
			hostname = decision.split('\0')[0]
			if isip(hostname):
				res = False
			else:
				res = True
		else:
			res = False

		return res

	def extractHostname(self, command, decision):
		data = decision.split('\0')

		if command == 'download':
			hostname = data[0]

		elif command == 'connect':
			hostname = decision.split('\0')[0]

		else:
			hostname = None

		return hostname

	def resolveDecision(self, command, decision, ip):
		if command in ('download', 'connect'):
			hostname, args = decision.split('\0', 1)
			newdecision = '\0'.join((ip, args))
		else:
			newdecision = None

		return newdecision

	def startResolving(self, client_id, command, decision):
		hostname = self.extractHostname(command, decision)

		if hostname:
			if hostname in self.cache:
				identifier = None
				ip = self.cache[hostname]

				if ip is not None:
					resolved = self.resolveDecision(command, decision, ip)
					response = client_id, command, resolved

				else:
					newdecision = '\0'.join(('503', 'dns.html', 'http', '', hostname, 'peer'))
					response = client_id, 'rewrite', newdecision

			else:
				identifier, _ = self.worker.resolveHost(hostname)
				response = None
				self.resolving[identifier] = client_id, hostname, hostname, command, decision
				self.clients[client_id] = identifier
				self.active.append((time.time(), client_id))
		else:
			identifier = None
			response = None

		return identifier, response

	def startResolvingTCP(self, client_id, command, decision):
		hostname = self.extractHostname(command, decision)

		if hostname:
			worker = self.resolver_factory.createTCPClient(self.configuration, self.resolv)
			self.workers[worker.socket] = worker

			identifier, all_sent = worker.resolveHost(hostname)
			self.clients[client_id] = identifier
			self.active.append((time.time(), client_id))

			if all_sent:
				self.poller.addReadSocket('read_resolver', worker.socket)
				self.resolving[identifier] = client_id, hostname, hostname, command, decision
			else:
				self.poller.addWriteSocket('write_resolver', worker.socket)
				self.sending[worker.socket] = client_id, hostname, hostname, command, decision

		else:
			identifier = None

		return identifier

	def getResponse(self, sock):
		worker = self.workers.get(sock)

		if worker:
			result = worker.getResponse()

			if result:
				identifier, forhost, ip, completed, newidentifier, newhost, newcomplete = result
				data = self.resolving.pop(identifier, None)
			else:
				# most likely we have not read a full request over TCP
				data = None

			if data:
				client_id, original, hostname, command, decision = data
				self.clients.pop(client_id)

				# check to see if we received an incomplete response
				if not completed:
					worker = self.worker = self.resolver_factory.createTCPClient(self.configuration, self.resolv)
					# Note:	this will start with a request for an A record again even if
					#	the UDP client choked only once it asked for the AAAA
					newidentifier, all_sent = worker.resolveHost(hostname)
					newhost = hostname
					response = None

					if newidentifier:
						self.poller.addReadSocket('read_resolver', worker.socket)
					else:
						self.poller.addWriteSocket('write_resolver', worker.socket)
						self.sending[worker.socket] = client_id, original, hostname, command, decision

				# check to see if the worker started a new request
				if newidentifier:
					self.resolving[newidentifier] = client_id, original, newhost, command, decision
					self.clients[client_id] = newidentifier
					response = None

					if completed and newcomplete:
						self.poller.addReadSocket('read_resolver', worker.socket)
					elif completed and not newcomplete:
						self.poller.addWriteSocket('write_resolver', worker.socket)
						self.sending[worker.socket] = client_id, original, hostname, command, decision

				# we just started a new (TCP) request and have not yet completely sent it
				elif not completed:
					response = None

				# maybe we read the wrong response?
				elif forhost != hostname:
					self.resolving[identifier] = client_id, original, hostname, command, decision
					self.clients[client_id] = identifier
					self.active.append((time.time(), client_id))
					response = None

				# success
				elif ip is not None:
					resolved = self.resolveDecision(command, decision, ip)
					response = client_id, command, resolved
					self.cacheDestination(original, ip)

				# not found
				else:
					newdecision = '\0'.join(('503', 'dns.html', 'http', '', hostname, 'peer'))
					response = client_id, 'rewrite', newdecision
					#self.cacheDestination(original, ip)
			else:
				response = None

			if response:
				if worker.isClosed():
					self.poller.removeReadSocket('read_resolver', sock)
					self.workers.pop(sock)

		else:
			response = None

		return response

	def continueSending(self, sock):
		"""Continue sending data over the connected TCP socket"""
		data = self.sending.get(sock)
		if data:
			client_id, original, hostname, command, decision = data
			worker = self.workers[sock]
			identifier = self.clients[client_id]

			res = worker.continueSending()

			if res is False: # we've sent all we need to send
				tmp = self.sending.pop(sock)
				self.resolving[identifier] = tmp

				self.poller.removeWriteSocket('write_resolver', sock)
				self.poller.addReadSocket('read_resolver', sock)
<commit_msg>Fix missing parameter in rewrite message<commit_after>import time

from .worker import DNSResolver
from exaproxy.network.functions import isip

class ResolverManager(object):
	resolver_factory = DNSResolver()

	def __init__(self, poller, configuration):
		self.poller = poller
		self.configuration = configuration # needed when creating new resolver instances
		self.resolv = configuration.dns.resolver
		self.timeout = configuration.dns.timeout

		# The actual work is done in the worker
		self.worker = self.resolver_factory.createUDPClient(configuration, self.resolv)

		# All currently active clients (UDP and TCP)
		self.workers = {}
		self.workers[self.worker.socket] = self.worker
                self.poller.addReadSocket('read_resolver', self.worker.socket)

		# Track the clients currently expecting results
		self.clients = {}

		# Key should be the hostname rather than the request ID?
		self.resolving = {}

		# TCP workers that have not yet sent a complete request
		self.sending = {}

		# track the current queries and when they were started
		self.active = []

		self.cache = {}
		self.cached = []

	def cacheDestination(self, hostname, ip):
		if hostname not in self.cache:
			self.cache[hostname] = ip
			self.cached.append((time.time(), hostname))

	def expireCache(self):
		if not self.cached:
			return

		count = len(self.cached)
		stop = min(count, self.configuration.dns.expire)
		position = stop-1

		cutoff = time.time() - self.configuration.dns.ttl

		while position > 10:
			timestamp, hostname = self.cached[position]
			if timestamp > cutoff:
				break

			position = int(position/1.3)
		else:
			position = 0

		for timestamp, hostname in self.cached[position:stop]:
			if timestamp > cutoff:
				break

			position += 1

			self.cache.pop(hostname, None)

		if position:
			self.cached = self.cached[position:]

	def cleanup(self):
		cutoff = time.time() - self.timeout
		count = 0

		for timestamp, client_id in self.active:
			if timestamp > cutoff:
				break

			count += 1
			identifier = self.clients.get(client_id)

			if identifier is not None:
				data = self.resolving.pop(identifier, None)
				if not data:
					data = self.sending.pop(identifier, None)

				if data:
					client_id, original, hostname, command, decision = data
					yield client_id, 'rewrite', '\0'.join(('503', 'dns.html', '', '', '', hostname, 'peer'))

		if count:
			self.active = self.active[count:]

	def resolves(self, command, decision):
		if command in ('download', 'connect'):
			hostname = decision.split('\0')[0]
			if isip(hostname):
				res = False
			else:
				res = True
		else:
			res = False

		return res

	def extractHostname(self, command, decision):
		data = decision.split('\0')

		if command == 'download':
			hostname = data[0]

		elif command == 'connect':
			hostname = decision.split('\0')[0]

		else:
			hostname = None

		return hostname

	def resolveDecision(self, command, decision, ip):
		if command in ('download', 'connect'):
			hostname, args = decision.split('\0', 1)
			newdecision = '\0'.join((ip, args))
		else:
			newdecision = None

		return newdecision

	def startResolving(self, client_id, command, decision):
		hostname = self.extractHostname(command, decision)

		if hostname:
			if hostname in self.cache:
				identifier = None
				ip = self.cache[hostname]

				if ip is not None:
					resolved = self.resolveDecision(command, decision, ip)
					response = client_id, command, resolved

				else:
					newdecision = '\0'.join(('503', 'dns.html', 'http', '', '', hostname, 'peer'))
					response = client_id, 'rewrite', newdecision

			else:
				identifier, _ = self.worker.resolveHost(hostname)
				response = None
				self.resolving[identifier] = client_id, hostname, hostname, command, decision
				self.clients[client_id] = identifier
				self.active.append((time.time(), client_id))
		else:
			identifier = None
			response = None

		return identifier, response

	def startResolvingTCP(self, client_id, command, decision):
		hostname = self.extractHostname(command, decision)

		if hostname:
			worker = self.resolver_factory.createTCPClient(self.configuration, self.resolv)
			self.workers[worker.socket] = worker

			identifier, all_sent = worker.resolveHost(hostname)
			self.clients[client_id] = identifier
			self.active.append((time.time(), client_id))

			if all_sent:
				self.poller.addReadSocket('read_resolver', worker.socket)
				self.resolving[identifier] = client_id, hostname, hostname, command, decision
			else:
				self.poller.addWriteSocket('write_resolver', worker.socket)
				self.sending[worker.socket] = client_id, hostname, hostname, command, decision

		else:
			identifier = None

		return identifier

	def getResponse(self, sock):
		worker = self.workers.get(sock)

		if worker:
			result = worker.getResponse()

			if result:
				identifier, forhost, ip, completed, newidentifier, newhost, newcomplete = result
				data = self.resolving.pop(identifier, None)
			else:
				# most likely we have not read a full request over TCP
				data = None

			if data:
				client_id, original, hostname, command, decision = data
				self.clients.pop(client_id)

				# check to see if we received an incomplete response
				if not completed:
					worker = self.worker = self.resolver_factory.createTCPClient(self.configuration, self.resolv)
					# Note:	this will start with a request for an A record again even if
					#	the UDP client choked only once it asked for the AAAA
					newidentifier, all_sent = worker.resolveHost(hostname)
					newhost = hostname
					response = None

					if newidentifier:
						self.poller.addReadSocket('read_resolver', worker.socket)
					else:
						self.poller.addWriteSocket('write_resolver', worker.socket)
						self.sending[worker.socket] = client_id, original, hostname, command, decision

				# check to see if the worker started a new request
				if newidentifier:
					self.resolving[newidentifier] = client_id, original, newhost, command, decision
					self.clients[client_id] = newidentifier
					response = None

					if completed and newcomplete:
						self.poller.addReadSocket('read_resolver', worker.socket)
					elif completed and not newcomplete:
						self.poller.addWriteSocket('write_resolver', worker.socket)
						self.sending[worker.socket] = client_id, original, hostname, command, decision

				# we just started a new (TCP) request and have not yet completely sent it
				elif not completed:
					response = None

				# maybe we read the wrong response?
				elif forhost != hostname:
					self.resolving[identifier] = client_id, original, hostname, command, decision
					self.clients[client_id] = identifier
					self.active.append((time.time(), client_id))
					response = None

				# success
				elif ip is not None:
					resolved = self.resolveDecision(command, decision, ip)
					response = client_id, command, resolved
					self.cacheDestination(original, ip)

				# not found
				else:
					newdecision = '\0'.join(('503', 'dns.html', 'http', '', '', hostname, 'peer'))
					response = client_id, 'rewrite', newdecision
					#self.cacheDestination(original, ip)
			else:
				response = None

			if response:
				if worker.isClosed():
					self.poller.removeReadSocket('read_resolver', sock)
					self.workers.pop(sock)

		else:
			response = None

		return response

	def continueSending(self, sock):
		"""Continue sending data over the connected TCP socket"""
		data = self.sending.get(sock)
		if data:
			client_id, original, hostname, command, decision = data
			worker = self.workers[sock]
			identifier = self.clients[client_id]

			res = worker.continueSending()

			if res is False: # we've sent all we need to send
				tmp = self.sending.pop(sock)
				self.resolving[identifier] = tmp

				self.poller.removeWriteSocket('write_resolver', sock)
				self.poller.addReadSocket('read_resolver', sock)
<|endoftext|>


<commit_before>#! /usr/bin/python
''' Performs the hardware level functions of the page-turning mechanism
'''

# STANDARD PYTHON IMPORTS
import subprocess

# PYTHON LIBRARIES
import RPi.GPIO as GPIO

# USER LIBRARIES
from step_servo import ServoStepThread

# GLOBAL VARIABLES
DEFAULT_SETTLING_TIME = 1 # second
DEFAULT_WHEEL_DEGREES = 39 # degrees
DEFAULT_HOLDER_ARM_DEGREES = 100 # degrees

# CLASSES
class PageTurner():
	''' Implements the pre-defined page turner actions
	'''
	def __init__(self, state_machine=None):
		''' Initialises the page turner.

			'state_machine' should contain a reference to a PiStateMachine()
				object.
		'''
		self._state_machine = state_machine
		self.is_init = False
		self.wheel_degrees = DEFAULT_WHEEL_DEGREES
		self.holder_arm_degrees = DEFAULT_HOLDER_ARM_DEGREES
		self.init_all_servo_gpio()
		self.init_servo_positions()

	def __del__(self):
		''' Destructor for the page turner. Deinit all servo GPIOs
		'''
		self.deinit_all_servo_gpio()

	def _run_servos(self, pin_lookup):
		''' Runs every servo in the pin lookup table using the src/pi/step_servo.py
		bash script.
		'''
		pin_str = ""
		deg_str = ""
		tim_str = ""

		for pin, tup in pin_lookup.iteritems():
			pin_str = "%s%d" % (pin_str, pin)
			deg_str = "%s%d" % (deg_str, tup[0])
			tim_str = "%s%d" % (tim_str, tup[1])

		subprocess.call('src/pi/step_servo.py -p "%s" -d "%s" -t "%s"' % \
			(pin_str, deg_str, tim_str), shell=True)

	def _run_servos_non_bash(self, pin_lookup):
		''' Runs every servo in the pin_lookup table that looks like:
			{ pin : (degrees, settling_time) }
		'''
		threads = []
		for pin, tup in pin_lookup.iteritems():
			threads.append(ServoStepThread(pin, tup[0], tup[1]))

		# Run the servos
		for t in threads:
			t.start()

		# Wait for all servos to settle
		for t in threads:
			t.join()

	def init_all_servo_gpio(self):
		''' Initialises the GPIO module and the individual servo pins
		'''
		self._init_servo_gpio(9)
		self._init_servo_gpio(11)
		self._init_servo_gpio(10)
		self._init_servo_gpio(14)
		self._init_servo_gpio(7)
		self._init_servo_gpio(8)
		self._init_servo_gpio(15)
		print "Successfully init all servo GPIO"

	def _init_servo_gpio(self,pin):
		''' Initialises a single servo GPIO pin.
		'''
		GPIO.setmode(GPIO.BCM)
		GPIO.setup(pin, GPIO.OUT)

	def deinit_all_servo_gpio(self):
		''' Deinitialises all GPIOs
		'''
		GPIO.cleanup()
		print "Successfully deinit all servo GPIO"

	def set_wheel_degrees(self, wheel_degrees):
		''' Sets the wheel degrees value, usually passed from the server through
		the state machine
		'''
		self.wheel_degrees = wheel_degrees

	def get_wheel_degrees(self):
		''' Returns the value of wheel degrees currently in use.
		'''
		return self.wheel_degrees

	def set_holder_arm_degrees(self, holder_arm_degrees):
		''' Sets the holder arm degrees values.
		'''
		self.holder_arm_degrees = holder_arm_degrees

	def get_holder_arm_degrees(self):
		''' Returns the holder arm degrees currently in use
		'''
		return self.holder_arm_degrees

	def init_servo_positions(self):
		''' Takes the servos to their defined starting positions.
		'''
		pin_lookup = {9  : (90,  DEFAULT_SETTLING_TIME), \
					  11 : (120, DEFAULT_SETTLING_TIME), \
					  10 : (0,   DEFAULT_SETTLING_TIME), \
					  14 : (180, DEFAULT_SETTLING_TIME), \
					  7  : (100, DEFAULT_SETTLING_TIME), \
					  8  : (75,  DEFAULT_SETTLING_TIME), \
					  15 : (90,  DEFAULT_SETTLING_TIME)}

		self._run_servos(pin_lookup)
		self.is_init = True

	def queue_turn_page(self):
		''' Queues a page turn on a background thread. When the page turning is
		complete, the state_machine's handler_turned_page is called.

		TODO: Background thread
		'''

		# Signal to the state machine that we have finished turning the page
		self.turn_page()
		self._state_machine.handler_turned_page()

	def turn_page(self):
		''' Turns a page using the experimentally adjusted degree values. Calls
		the state_machine's handler_turned_page() function once done.
		'''
		if not self.is_init:
			self.init_servo_positions()
		self.is_init = False

		pin_lookup = {11 : (self.wheel_degrees, DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {9  : (0,   DEFAULT_SETTLING_TIME), \
					  15 : (75,  DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {15 : (100, DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {11 : (120, DEFAULT_SETTLING_TIME), \
					  10 : (130, DEFAULT_SETTLING_TIME), \
					  14 : (0,   DEFAULT_SETTLING_TIME), \
					  7  : (0,   DEFAULT_SETTLING_TIME), \
					  8  : (160, DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {7  : (100, DEFAULT_SETTLING_TIME), \
					  8  : (75,  DEFAULT_SETTLING_TIME), \
					  15 : (90,  DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {14 : (180, DEFAULT_SETTLING_TIME), \
					  10 : (0,   DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		self.deinit_all_servo_gpio()

# FUNCTIONS

# CODE
if __name__ == '__main__':
	print "Do not run page_turner.py from __main__."

# END OF FILE<commit_msg>Hotfix in pi page_turner calling step_servo<commit_after>#! /usr/bin/python
''' Performs the hardware level functions of the page-turning mechanism
'''

# STANDARD PYTHON IMPORTS
import subprocess

# PYTHON LIBRARIES
import RPi.GPIO as GPIO

# USER LIBRARIES
from step_servo import ServoStepThread

# GLOBAL VARIABLES
DEFAULT_SETTLING_TIME = 1 # second
DEFAULT_WHEEL_DEGREES = 39 # degrees
DEFAULT_HOLDER_ARM_DEGREES = 100 # degrees

# CLASSES
class PageTurner():
	''' Implements the pre-defined page turner actions
	'''
	def __init__(self, state_machine=None):
		''' Initialises the page turner.

			'state_machine' should contain a reference to a PiStateMachine()
				object.
		'''
		self._state_machine = state_machine
		self.is_init = False
		self.wheel_degrees = DEFAULT_WHEEL_DEGREES
		self.holder_arm_degrees = DEFAULT_HOLDER_ARM_DEGREES
		self.init_all_servo_gpio()
		self.init_servo_positions()

	def __del__(self):
		''' Destructor for the page turner. Deinit all servo GPIOs
		'''
		self.deinit_all_servo_gpio()

	def _run_servos(self, pin_lookup):
		''' Runs every servo in the pin lookup table using the src/pi/step_servo.py
		bash script.
		'''
		pin_str = ""
		deg_str = ""
		tim_str = ""

		for pin, tup in pin_lookup.iteritems():
			pin_str = "%s,%d" % (pin_str, pin)
			deg_str = "%s,%d" % (deg_str, tup[0])
			tim_str = "%s,%d" % (tim_str, tup[1])

		subprocess.call('src/pi/step_servo.py -p "%s" -d "%s" -t "%s"' % \
			(pin_str, deg_str, tim_str), shell=True)

	def _run_servos_non_bash(self, pin_lookup):
		''' Runs every servo in the pin_lookup table that looks like:
			{ pin : (degrees, settling_time) }
		'''
		threads = []
		for pin, tup in pin_lookup.iteritems():
			threads.append(ServoStepThread(pin, tup[0], tup[1]))

		# Run the servos
		for t in threads:
			t.start()

		# Wait for all servos to settle
		for t in threads:
			t.join()

	def init_all_servo_gpio(self):
		''' Initialises the GPIO module and the individual servo pins
		'''
		self._init_servo_gpio(9)
		self._init_servo_gpio(11)
		self._init_servo_gpio(10)
		self._init_servo_gpio(14)
		self._init_servo_gpio(7)
		self._init_servo_gpio(8)
		self._init_servo_gpio(15)
		print "Successfully init all servo GPIO"

	def _init_servo_gpio(self,pin):
		''' Initialises a single servo GPIO pin.
		'''
		GPIO.setmode(GPIO.BCM)
		GPIO.setup(pin, GPIO.OUT)

	def deinit_all_servo_gpio(self):
		''' Deinitialises all GPIOs
		'''
		GPIO.cleanup()
		print "Successfully deinit all servo GPIO"

	def set_wheel_degrees(self, wheel_degrees):
		''' Sets the wheel degrees value, usually passed from the server through
		the state machine
		'''
		self.wheel_degrees = wheel_degrees

	def get_wheel_degrees(self):
		''' Returns the value of wheel degrees currently in use.
		'''
		return self.wheel_degrees

	def set_holder_arm_degrees(self, holder_arm_degrees):
		''' Sets the holder arm degrees values.
		'''
		self.holder_arm_degrees = holder_arm_degrees

	def get_holder_arm_degrees(self):
		''' Returns the holder arm degrees currently in use
		'''
		return self.holder_arm_degrees

	def init_servo_positions(self):
		''' Takes the servos to their defined starting positions.
		'''
		pin_lookup = {9  : (90,  DEFAULT_SETTLING_TIME), \
					  11 : (120, DEFAULT_SETTLING_TIME), \
					  10 : (0,   DEFAULT_SETTLING_TIME), \
					  14 : (180, DEFAULT_SETTLING_TIME), \
					  7  : (100, DEFAULT_SETTLING_TIME), \
					  8  : (75,  DEFAULT_SETTLING_TIME), \
					  15 : (90,  DEFAULT_SETTLING_TIME)}

		self._run_servos(pin_lookup)
		self.is_init = True

	def queue_turn_page(self):
		''' Queues a page turn on a background thread. When the page turning is
		complete, the state_machine's handler_turned_page is called.

		TODO: Background thread
		'''

		# Signal to the state machine that we have finished turning the page
		self.turn_page()
		self._state_machine.handler_turned_page()

	def turn_page(self):
		''' Turns a page using the experimentally adjusted degree values. Calls
		the state_machine's handler_turned_page() function once done.
		'''
		if not self.is_init:
			self.init_servo_positions()
		self.is_init = False

		pin_lookup = {11 : (self.wheel_degrees, DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {9  : (0,   DEFAULT_SETTLING_TIME), \
					  15 : (75,  DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {15 : (100, DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {11 : (120, DEFAULT_SETTLING_TIME), \
					  10 : (130, DEFAULT_SETTLING_TIME), \
					  14 : (0,   DEFAULT_SETTLING_TIME), \
					  7  : (0,   DEFAULT_SETTLING_TIME), \
					  8  : (160, DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {7  : (100, DEFAULT_SETTLING_TIME), \
					  8  : (75,  DEFAULT_SETTLING_TIME), \
					  15 : (90,  DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		pin_lookup = {14 : (180, DEFAULT_SETTLING_TIME), \
					  10 : (0,   DEFAULT_SETTLING_TIME)}
		self._run_servos(pin_lookup)
		pin_lookup.clear()

		self.deinit_all_servo_gpio()

# FUNCTIONS

# CODE
if __name__ == '__main__':
	print "Do not run page_turner.py from __main__."

# END OF FILE<|endoftext|>


<commit_before># ex: set expandtab softtabstop=4 shiftwidth=4: -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
#
# Copyright (C) 2011  Contributor
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the EU DataGrid Software License.  You should
# have received a copy of the license with this program, and the
# license is published at
# http://eu-datagrid.web.cern.ch/eu-datagrid/license.html.
#
# THE FOLLOWING DISCLAIMER APPLIES TO ALL SOFTWARE CODE AND OTHER
# MATERIALS CONTRIBUTED IN CONNECTION WITH THIS PROGRAM.
#
# THIS SOFTWARE IS LICENSED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE AND ANY WARRANTY OF NON-INFRINGEMENT, ARE
# DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS
# SOFTWARE MAY BE REDISTRIBUTED TO OTHERS ONLY BY EFFECTIVELY USING
# THIS OR ANOTHER EQUIVALENT DISCLAIMER AS WELL AS ANY OTHER LICENSE
# TERMS THAT MAY APPLY.
""" Representation of Fully Qualified Domain Names """

from datetime import datetime

from sqlalchemy import (Integer, DateTime, Sequence, Column, ForeignKey,
                        UniqueConstraint)
from sqlalchemy.orm import relation, deferred, backref
from sqlalchemy.orm.session import Session

from aquilon.exceptions_ import InternalError, ArgumentError
from aquilon.aqdb.model import Base, DnsDomain, DnsEnvironment
from aquilon.aqdb.model.base import _raise_custom
from aquilon.aqdb.model.dns_domain import parse_fqdn
from aquilon.aqdb.column_types import AqStr

_TN = "fqdn"

class Fqdn(Base):
    __tablename__ = _TN
    _instance_label = 'fqdn'

    id = Column(Integer, Sequence('%s_id_seq' % _TN), primary_key=True)

    name = Column(AqStr(63), nullable=False)

    dns_domain_id = Column(Integer, ForeignKey('dns_domain.id',
                                               name='%s_dns_domain_fk' % _TN),
                           nullable=False)

    dns_environment_id = Column(Integer, ForeignKey('dns_environment.id',
                                                    name='%s_dns_env_fk' % _TN),
                                nullable=False)

    creation_date = deferred(Column(DateTime, default=datetime.now,
                                    nullable=False))

    dns_domain = relation(DnsDomain, innerjoin=True, backref=backref("fqdns"))

    dns_environment = relation(DnsEnvironment, innerjoin=True,
                               backref=backref("fqdns"))

    @property
    def fqdn(self):
        return self.name + '.' + self.dns_domain.name

    @classmethod
    def get_unique(cls, session, dns_environment=None, name=None,
                   dns_domain=None, fqdn=None, **kwargs):
        if fqdn:
            if name or dns_domain:  # pragma: no cover
                raise TypeError("fqdn and name/dns_domain should not be mixed")
            (name, dns_domain) = parse_fqdn(session, fqdn)

        if not isinstance(dns_environment, DnsEnvironment):
            dns_environment = DnsEnvironment.get_unique_or_default(session,
                                                                   dns_environment)
        return super(Fqdn, cls).get_unique(session, name=name,
                                           dns_domain=dns_domain,
                                           dns_environment=dns_environment,
                                           **kwargs)

    @classmethod
    def get_or_create(cls, session, dns_environment=None, preclude=False,
                      **kwargs):
        fqdn = cls.get_unique(session, dns_environment=dns_environment, **kwargs)
        if fqdn:
            if preclude:
                _raise_custom(preclude, ArgumentError, "{0} already exists.")
            return fqdn

        if not isinstance(dns_environment, DnsEnvironment):
            dns_environment = DnsEnvironment.get_unique_or_default(session,
                                                                   dns_environment)

        fqdn = cls(session=session, dns_environment=dns_environment, **kwargs)
        session.add(fqdn)
        session.flush()
        return fqdn

    @classmethod
    def check_name(cls, name, dns_domain):
        """ Validate the name parameter """

        if not isinstance(name, basestring):  # pragma: no cover
            raise TypeError("%s: name must be a string." % cls.name)
        if not isinstance(dns_domain, DnsDomain):  # pragma: no cover
            raise TypeError("%s: dns_domain must be a DnsDomain." % cls.name)

        DnsDomain.check_label(name)

        # The limit for DNS name length is 255, assuming wire format. This
        # translates to 253 for simple ASCII text; see:
        # http://www.ops.ietf.org/lists/namedroppers/namedroppers.2003/msg00964.html
        if len(name) + 1 + len(dns_domain.name) > 253:
            raise ArgumentError('The fully qualified domain name is too long.')

    def __init__(self, session=None, name=None, dns_domain=None, fqdn=None,
                 dns_environment=None, **kwargs):
        if not session or not isinstance(session, Session):  # pragma: no cover
            raise InternalError("%s needs a session." % self._get_class_label())

        if fqdn:
            if name or dns_domain:  # pragma: no cover
                raise TypeError("fqdn and name/dns_domain should not be mixed")
            (name, dns_domain) = parse_fqdn(session, fqdn)

        self.check_name(name, dns_domain)

        if not isinstance(dns_environment, DnsEnvironment):
            dns_environment = DnsEnvironment.get_unique_or_default(session,
                                                                   dns_environment)

        super(Fqdn, self).__init__(name=name, dns_domain=dns_domain,
                                   dns_environment=dns_environment, **kwargs)

fqdn = Fqdn.__table__  # pylint: disable-msg=C0103, E1101
fqdn.primary_key.name = '%s_pk' % _TN
fqdn.append_constraint(UniqueConstraint('name', 'dns_domain_id',
                                        'dns_environment_id',
                                        name='%s_name_domain_env_uk' % _TN))

fqdn.info['unique_fields'] = ['dns_environment', 'dns_domain', 'name']
<commit_msg>Change the order of parameters of Fqdn.get_unique()<commit_after># ex: set expandtab softtabstop=4 shiftwidth=4: -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
#
# Copyright (C) 2011  Contributor
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the EU DataGrid Software License.  You should
# have received a copy of the license with this program, and the
# license is published at
# http://eu-datagrid.web.cern.ch/eu-datagrid/license.html.
#
# THE FOLLOWING DISCLAIMER APPLIES TO ALL SOFTWARE CODE AND OTHER
# MATERIALS CONTRIBUTED IN CONNECTION WITH THIS PROGRAM.
#
# THIS SOFTWARE IS LICENSED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE AND ANY WARRANTY OF NON-INFRINGEMENT, ARE
# DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS
# SOFTWARE MAY BE REDISTRIBUTED TO OTHERS ONLY BY EFFECTIVELY USING
# THIS OR ANOTHER EQUIVALENT DISCLAIMER AS WELL AS ANY OTHER LICENSE
# TERMS THAT MAY APPLY.
""" Representation of Fully Qualified Domain Names """

from datetime import datetime

from sqlalchemy import (Integer, DateTime, Sequence, Column, ForeignKey,
                        UniqueConstraint)
from sqlalchemy.orm import relation, deferred, backref
from sqlalchemy.orm.session import Session

from aquilon.exceptions_ import InternalError, ArgumentError
from aquilon.aqdb.model import Base, DnsDomain, DnsEnvironment
from aquilon.aqdb.model.base import _raise_custom
from aquilon.aqdb.model.dns_domain import parse_fqdn
from aquilon.aqdb.column_types import AqStr

_TN = "fqdn"

class Fqdn(Base):
    __tablename__ = _TN
    _instance_label = 'fqdn'

    id = Column(Integer, Sequence('%s_id_seq' % _TN), primary_key=True)

    name = Column(AqStr(63), nullable=False)

    dns_domain_id = Column(Integer, ForeignKey('dns_domain.id',
                                               name='%s_dns_domain_fk' % _TN),
                           nullable=False)

    dns_environment_id = Column(Integer, ForeignKey('dns_environment.id',
                                                    name='%s_dns_env_fk' % _TN),
                                nullable=False)

    creation_date = deferred(Column(DateTime, default=datetime.now,
                                    nullable=False))

    dns_domain = relation(DnsDomain, innerjoin=True, backref=backref("fqdns"))

    dns_environment = relation(DnsEnvironment, innerjoin=True,
                               backref=backref("fqdns"))

    @property
    def fqdn(self):
        return self.name + '.' + self.dns_domain.name

    @classmethod
    def get_unique(cls, session, fqdn=None, dns_environment=None, name=None,
                   dns_domain=None, **kwargs):
        if fqdn:
            if name or dns_domain:  # pragma: no cover
                raise TypeError("fqdn and name/dns_domain should not be mixed")
            (name, dns_domain) = parse_fqdn(session, fqdn)

        if not isinstance(dns_environment, DnsEnvironment):
            dns_environment = DnsEnvironment.get_unique_or_default(session,
                                                                   dns_environment)
        return super(Fqdn, cls).get_unique(session, name=name,
                                           dns_domain=dns_domain,
                                           dns_environment=dns_environment,
                                           **kwargs)

    @classmethod
    def get_or_create(cls, session, dns_environment=None, preclude=False,
                      **kwargs):
        fqdn = cls.get_unique(session, dns_environment=dns_environment, **kwargs)
        if fqdn:
            if preclude:
                _raise_custom(preclude, ArgumentError, "{0} already exists.")
            return fqdn

        if not isinstance(dns_environment, DnsEnvironment):
            dns_environment = DnsEnvironment.get_unique_or_default(session,
                                                                   dns_environment)

        fqdn = cls(session=session, dns_environment=dns_environment, **kwargs)
        session.add(fqdn)
        session.flush()
        return fqdn

    @classmethod
    def check_name(cls, name, dns_domain):
        """ Validate the name parameter """

        if not isinstance(name, basestring):  # pragma: no cover
            raise TypeError("%s: name must be a string." % cls.name)
        if not isinstance(dns_domain, DnsDomain):  # pragma: no cover
            raise TypeError("%s: dns_domain must be a DnsDomain." % cls.name)

        DnsDomain.check_label(name)

        # The limit for DNS name length is 255, assuming wire format. This
        # translates to 253 for simple ASCII text; see:
        # http://www.ops.ietf.org/lists/namedroppers/namedroppers.2003/msg00964.html
        if len(name) + 1 + len(dns_domain.name) > 253:
            raise ArgumentError('The fully qualified domain name is too long.')

    def __init__(self, session=None, name=None, dns_domain=None, fqdn=None,
                 dns_environment=None, **kwargs):
        if not session or not isinstance(session, Session):  # pragma: no cover
            raise InternalError("%s needs a session." % self._get_class_label())

        if fqdn:
            if name or dns_domain:  # pragma: no cover
                raise TypeError("fqdn and name/dns_domain should not be mixed")
            (name, dns_domain) = parse_fqdn(session, fqdn)

        self.check_name(name, dns_domain)

        if not isinstance(dns_environment, DnsEnvironment):
            dns_environment = DnsEnvironment.get_unique_or_default(session,
                                                                   dns_environment)

        super(Fqdn, self).__init__(name=name, dns_domain=dns_domain,
                                   dns_environment=dns_environment, **kwargs)

fqdn = Fqdn.__table__  # pylint: disable-msg=C0103, E1101
fqdn.primary_key.name = '%s_pk' % _TN
fqdn.append_constraint(UniqueConstraint('name', 'dns_domain_id',
                                        'dns_environment_id',
                                        name='%s_name_domain_env_uk' % _TN))

fqdn.info['unique_fields'] = ['dns_environment', 'dns_domain', 'name']
<|endoftext|>