<commit_before># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"""
Starting point for routing EC2 requests.

"""

import webob
import webob.dec
import webob.exc

from nova import context
from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
from nova import wsgi
from nova.api.ec2 import apirequest
from nova.api.ec2 import cloud
from nova.auth import manager

FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api")
flags.DEFINE_boolean('use_forwarded_for', False,
                     'Treat X-Forwarded-For as the canonical remote address. '
                     'Only enable this if you have a sanitizing proxy.')
flags.DEFINE_integer('lockout_attempts', 5,
                     'Number of failed auths before lockout.')
flags.DEFINE_integer('lockout_minutes', 15,
                     'Number of minutes to lockout if triggered.')
flags.DEFINE_integer('lockout_window', 15,
                     'Number of minutes for lockout window.')
flags.DEFINE_list('lockout_memcached_servers', None,
                  'Memcached servers or None for in process cache.')

class RequestLogging(wsgi.Middleware):
    """Access-Log akin logging for all EC2 API requests."""

    @webob.dec.wsgify
    def __call__(self, req):
        start = utils.utcnow()
        rv = req.get_response(self.application)
        self.log_request_completion(rv, req, start)
        return rv

    def log_request_completion(self, response, request, start):
        controller = request.environ.get('ec2.controller', None)
        if controller:
            controller = controller.__class__.__name__
        action = request.environ.get('ec2.action', None)
        ctxt = request.environ.get('ec2.context', None)
        delta = utils.utcnow() - start
        seconds = delta.seconds
        microseconds = delta.microseconds
        LOG.info(
            "%s.%ss %s %s %s %s:%s %s [%s] %s %s",
            seconds,
            microseconds,
            request.remote_addr,
            request.method,
            request.path_info,
            controller,
            action,
            response.status_int,
            request.user_agent,
            request.content_type,
            response.content_type,
            context=ctxt)

class Lockout(wsgi.Middleware):
    """Lockout for x minutes on y failed auths in a z minute period.

    x = lockout_timeout flag
    y = lockout_window flag
    z = lockout_attempts flag

    Uses memcached if lockout_memcached_servers flag is set, otherwise it
    uses a very simple in-proccess cache. Due to the simplicity of
    the implementation, the timeout window is started with the first
    failed request, so it will block if there are x failed logins within
    that period.

    There is a possible race condition where simultaneous requests could
    sneak in before the lockout hits, but this is extremely rare and would
    only result in a couple of extra failed attempts."""

    def __init__(self, application):
        """middleware can use fake for testing."""
        if FLAGS.lockout_memcached_servers:
            import memcache
        else:
            from nova import fakememcache as memcache
        self.mc = memcache.Client(FLAGS.lockout_memcached_servers,
                                  debug=0)
        super(Lockout, self).__init__(application)

    @webob.dec.wsgify
    def __call__(self, req):
        access_key = str(req.params['AWSAccessKeyId'])
        failures_key = "authfailures-%s" % access_key
        failures = int(self.mc.get(failures_key) or 0)
        if failures >= FLAGS.lockout_attempts:
            detail = _("Too many failed authentications.")
            raise webob.exc.HTTPForbidden(detail=detail)
        res = req.get_response(self.application)
        if res.status_int == 403:
            failures = self.mc.incr(failures_key)
            if failures is None:
                # NOTE(vish): To use incr, failures has to be a string.
                self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60)
            elif failures >= FLAGS.lockout_attempts:
                lock_mins = FLAGS.lockout_minutes
                msg = _('Access key %(access_key)s has had %(failures)d'
                        ' failed authentications and will be locked out'
                        ' for %(lock_mins)d minutes.') % locals()
                LOG.warn(msg)
                self.mc.set(failures_key, str(failures),
                            time=FLAGS.lockout_minutes * 60)
        return res

class Authenticate(wsgi.Middleware):

    """Authenticate an EC2 request and add 'ec2.context' to WSGI environ."""

    @webob.dec.wsgify
    def __call__(self, req):
        # Read request signature and access id.
        try:
            signature = req.params['Signature']
            access = req.params['AWSAccessKeyId']
        except:
            raise webob.exc.HTTPBadRequest()

        # Make a copy of args for authentication and signature verification.
        auth_params = dict(req.params)
        # Not part of authentication args
        auth_params.pop('Signature')

        # Authenticate the request.
        try:
            (user, project) = manager.AuthManager().authenticate(
                    access,
                    signature,
                    auth_params,
                    req.method,
                    req.host,
                    req.path)
        # Be explicit for what exceptions are 403, the rest bubble as 500
        except (exception.NotFound, exception.NotAuthorized) as ex:
            LOG.audit(_("Authentication Failure: %s"), unicode(ex))
            raise webob.exc.HTTPForbidden()

        # Authenticated!
        remote_address = req.remote_addr
        if FLAGS.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For', remote_address)
        ctxt = context.RequestContext(user=user,
                                      project=project,
                                      remote_address=remote_address)
        req.environ['ec2.context'] = ctxt
        uname = user.name
        pname = project.name
        msg = _('Authenticated Request For %(uname)s:%(pname)s)') % locals()
        LOG.audit(msg, context=req.environ['ec2.context'])
        return self.application

class Requestify(wsgi.Middleware):

    def __init__(self, app, controller):
        super(Requestify, self).__init__(app)
        self.controller = utils.import_class(controller)()

    @webob.dec.wsgify
    def __call__(self, req):
        non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod',
                    'SignatureVersion', 'Version', 'Timestamp']
        args = dict(req.params)
        try:
            # Raise KeyError if omitted
            action = req.params['Action']
            for non_arg in non_args:
                # Remove, but raise KeyError if omitted
                args.pop(non_arg)
        except:
            raise webob.exc.HTTPBadRequest()

        LOG.debug(_('action: %s'), action)
        for key, value in args.items():
            LOG.debug(_('arg: %(key)s\t\tval: %(value)s') % locals())

        # Success!
        api_request = apirequest.APIRequest(self.controller, action,
                                            req.params['Version'], args)
        req.environ['ec2.request'] = api_request
        req.environ['ec2.action_args'] = args
        return self.application

class Authorizer(wsgi.Middleware):

    """Authorize an EC2 API request.

    Return a 401 if ec2.controller and ec2.action in WSGI environ may not be
    executed in ec2.context.
    """

    def __init__(self, application):
        super(Authorizer, self).__init__(application)
        self.action_roles = {
            'CloudController': {
                'DescribeAvailabilityZones': ['all'],
                'DescribeRegions': ['all'],
                'DescribeSnapshots': ['all'],
                'DescribeKeyPairs': ['all'],
                'CreateKeyPair': ['all'],
                'DeleteKeyPair': ['all'],
                'DescribeSecurityGroups': ['all'],
                'AuthorizeSecurityGroupIngress': ['netadmin'],
                'RevokeSecurityGroupIngress': ['netadmin'],
                'CreateSecurityGroup': ['netadmin'],
                'DeleteSecurityGroup': ['netadmin'],
                'GetConsoleOutput': ['projectmanager', 'sysadmin'],
                'DescribeVolumes': ['projectmanager', 'sysadmin'],
                'CreateVolume': ['projectmanager', 'sysadmin'],
                'AttachVolume': ['projectmanager', 'sysadmin'],
                'DetachVolume': ['projectmanager', 'sysadmin'],
                'DescribeInstances': ['all'],
                'DescribeAddresses': ['all'],
                'AllocateAddress': ['netadmin'],
                'ReleaseAddress': ['netadmin'],
                'AssociateAddress': ['netadmin'],
                'DisassociateAddress': ['netadmin'],
                'RunInstances': ['projectmanager', 'sysadmin'],
                'TerminateInstances': ['projectmanager', 'sysadmin'],
                'RebootInstances': ['projectmanager', 'sysadmin'],
                'UpdateInstance': ['projectmanager', 'sysadmin'],
                'DeleteVolume': ['projectmanager', 'sysadmin'],
                'DescribeImages': ['all'],
                'DeregisterImage': ['projectmanager', 'sysadmin'],
                'RegisterImage': ['projectmanager', 'sysadmin'],
                'DescribeImageAttribute': ['all'],
                'ModifyImageAttribute': ['projectmanager', 'sysadmin'],
                'UpdateImage': ['projectmanager', 'sysadmin'],
            },
            'AdminController': {
                # All actions have the same permission: ['none'] (the default)
                # superusers will be allowed to run them
                # all others will get HTTPUnauthorized.
            },
        }

    @webob.dec.wsgify
    def __call__(self, req):
        context = req.environ['ec2.context']
        controller = req.environ['ec2.request'].controller.__class__.__name__
        action = req.environ['ec2.request'].action
        allowed_roles = self.action_roles[controller].get(action, ['none'])
        if self._matches_any_role(context, allowed_roles):
            return self.application
        else:
            LOG.audit(_('Unauthorized request for controller=%(controller)s '
                        'and action=%(action)s') % locals(), context=context)
            raise webob.exc.HTTPUnauthorized()

    def _matches_any_role(self, context, roles):
        """Return True if any role in roles is allowed in context."""
        if context.user.is_superuser():
            return True
        if 'all' in roles:
            return True
        if 'none' in roles:
            return False
        return any(context.project.has_role(context.user_id, role)
                   for role in roles)

class Executor(wsgi.Application):

    """Execute an EC2 API request.

    Executes 'ec2.action' upon 'ec2.controller', passing 'ec2.context' and
    'ec2.action_args' (all variables in WSGI environ.)  Returns an XML
    response, or a 400 upon failure.
    """

    @webob.dec.wsgify
    def __call__(self, req):
        context = req.environ['ec2.context']
        api_request = req.environ['ec2.request']
        result = None
        try:
            result = api_request.invoke(context)
        except exception.InstanceNotFound as ex:
            LOG.info(_('InstanceNotFound raised: %s'), unicode(ex),
                     context=context)
            ec2_id = cloud.id_to_ec2_id(ex.instance_id)
            message = _('Instance %s not found') % ec2_id
            return self._error(req, context, type(ex).__name__, message)
        except exception.VolumeNotFound as ex:
            LOG.info(_('VolumeNotFound raised: %s'), unicode(ex),
                     context=context)
            ec2_id = cloud.id_to_ec2_id(ex.volume_id, 'vol-%08x')
            message = _('Volume %s not found') % ec2_id
            return self._error(req, context, type(ex).__name__, message)
        except exception.NotFound as ex:
            LOG.info(_('NotFound raised: %s'), unicode(ex), context=context)
            return self._error(req, context, type(ex).__name__, unicode(ex))
        except exception.ApiError as ex:
            LOG.exception(_('ApiError raised: %s'), unicode(ex),
                          context=context)
            if ex.code:
                return self._error(req, context, ex.code, unicode(ex))
            else:
                return self._error(req, context, type(ex).__name__,
                                   unicode(ex))
        except Exception as ex:
            extra = {'environment': req.environ}
            LOG.exception(_('Unexpected error raised: %s'), unicode(ex),
                          extra=extra, context=context)
            return self._error(req,
                               context,
                               'UnknownError',
                               _('An unknown error has occurred. '
                               'Please try your request again.'))
        else:
            resp = webob.Response()
            resp.status = 200
            resp.headers['Content-Type'] = 'text/xml'
            resp.body = str(result)
            return resp

    def _error(self, req, context, code, message):
        LOG.error("%s: %s", code, message, context=context)
        resp = webob.Response()
        resp.status = 400
        resp.headers['Content-Type'] = 'text/xml'
        resp.body = str('<?xml version="1.0"?>\n'
                         '<Response><Errors><Error><Code>%s</Code>'
                         '<Message>%s</Message></Error></Errors>'
                         '<RequestID>%s</RequestID></Response>' %
                         (utils.utf8(code), utils.utf8(message),
                         utils.utf8(context.request_id)))
        return resp

class Versions(wsgi.Application):

    @webob.dec.wsgify
    def __call__(self, req):
        """Respond to a request for all EC2 versions."""
        # available api versions
        versions = [
            '1.0',
            '2007-01-19',
            '2007-03-01',
            '2007-08-29',
            '2007-10-10',
            '2007-12-15',
            '2008-02-01',
            '2008-09-01',
            '2009-04-04',
        ]
        return ''.join('%s\n' % v for v in versions)
<commit_msg>Fix api logging to show proper path and controller:action.<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"""
Starting point for routing EC2 requests.

"""

import webob
import webob.dec
import webob.exc

from nova import context
from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
from nova import wsgi
from nova.api.ec2 import apirequest
from nova.api.ec2 import cloud
from nova.auth import manager

FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api")
flags.DEFINE_boolean('use_forwarded_for', False,
                     'Treat X-Forwarded-For as the canonical remote address. '
                     'Only enable this if you have a sanitizing proxy.')
flags.DEFINE_integer('lockout_attempts', 5,
                     'Number of failed auths before lockout.')
flags.DEFINE_integer('lockout_minutes', 15,
                     'Number of minutes to lockout if triggered.')
flags.DEFINE_integer('lockout_window', 15,
                     'Number of minutes for lockout window.')
flags.DEFINE_list('lockout_memcached_servers', None,
                  'Memcached servers or None for in process cache.')

class RequestLogging(wsgi.Middleware):
    """Access-Log akin logging for all EC2 API requests."""

    @webob.dec.wsgify
    def __call__(self, req):
        start = utils.utcnow()
        rv = req.get_response(self.application)
        self.log_request_completion(rv, req, start)
        return rv

    def log_request_completion(self, response, request, start):
        apireq = request.environ.get('ec2.request', None)
        if apirequest:
            controller = apireq.controller
            action = apireq.action
        else:
            controller = None
            action = None
        ctxt = request.environ.get('ec2.context', None)
        delta = utils.utcnow() - start
        seconds = delta.seconds
        microseconds = delta.microseconds
        LOG.info(
            "%s.%ss %s %s %s %s:%s %s [%s] %s %s",
            seconds,
            microseconds,
            request.remote_addr,
            request.method,
            "%s%s" % (request.script_name, request.path_info),
            controller,
            action,
            response.status_int,
            request.user_agent,
            request.content_type,
            response.content_type,
            context=ctxt)

class Lockout(wsgi.Middleware):
    """Lockout for x minutes on y failed auths in a z minute period.

    x = lockout_timeout flag
    y = lockout_window flag
    z = lockout_attempts flag

    Uses memcached if lockout_memcached_servers flag is set, otherwise it
    uses a very simple in-proccess cache. Due to the simplicity of
    the implementation, the timeout window is started with the first
    failed request, so it will block if there are x failed logins within
    that period.

    There is a possible race condition where simultaneous requests could
    sneak in before the lockout hits, but this is extremely rare and would
    only result in a couple of extra failed attempts."""

    def __init__(self, application):
        """middleware can use fake for testing."""
        if FLAGS.lockout_memcached_servers:
            import memcache
        else:
            from nova import fakememcache as memcache
        self.mc = memcache.Client(FLAGS.lockout_memcached_servers,
                                  debug=0)
        super(Lockout, self).__init__(application)

    @webob.dec.wsgify
    def __call__(self, req):
        access_key = str(req.params['AWSAccessKeyId'])
        failures_key = "authfailures-%s" % access_key
        failures = int(self.mc.get(failures_key) or 0)
        if failures >= FLAGS.lockout_attempts:
            detail = _("Too many failed authentications.")
            raise webob.exc.HTTPForbidden(detail=detail)
        res = req.get_response(self.application)
        if res.status_int == 403:
            failures = self.mc.incr(failures_key)
            if failures is None:
                # NOTE(vish): To use incr, failures has to be a string.
                self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60)
            elif failures >= FLAGS.lockout_attempts:
                lock_mins = FLAGS.lockout_minutes
                msg = _('Access key %(access_key)s has had %(failures)d'
                        ' failed authentications and will be locked out'
                        ' for %(lock_mins)d minutes.') % locals()
                LOG.warn(msg)
                self.mc.set(failures_key, str(failures),
                            time=FLAGS.lockout_minutes * 60)
        return res

class Authenticate(wsgi.Middleware):

    """Authenticate an EC2 request and add 'ec2.context' to WSGI environ."""

    @webob.dec.wsgify
    def __call__(self, req):
        # Read request signature and access id.
        try:
            signature = req.params['Signature']
            access = req.params['AWSAccessKeyId']
        except:
            raise webob.exc.HTTPBadRequest()

        # Make a copy of args for authentication and signature verification.
        auth_params = dict(req.params)
        # Not part of authentication args
        auth_params.pop('Signature')

        # Authenticate the request.
        try:
            (user, project) = manager.AuthManager().authenticate(
                    access,
                    signature,
                    auth_params,
                    req.method,
                    req.host,
                    req.path)
        # Be explicit for what exceptions are 403, the rest bubble as 500
        except (exception.NotFound, exception.NotAuthorized) as ex:
            LOG.audit(_("Authentication Failure: %s"), unicode(ex))
            raise webob.exc.HTTPForbidden()

        # Authenticated!
        remote_address = req.remote_addr
        if FLAGS.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For', remote_address)
        ctxt = context.RequestContext(user=user,
                                      project=project,
                                      remote_address=remote_address)
        req.environ['ec2.context'] = ctxt
        uname = user.name
        pname = project.name
        msg = _('Authenticated Request For %(uname)s:%(pname)s)') % locals()
        LOG.audit(msg, context=req.environ['ec2.context'])
        return self.application

class Requestify(wsgi.Middleware):

    def __init__(self, app, controller):
        super(Requestify, self).__init__(app)
        self.controller = utils.import_class(controller)()

    @webob.dec.wsgify
    def __call__(self, req):
        non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod',
                    'SignatureVersion', 'Version', 'Timestamp']
        args = dict(req.params)
        try:
            # Raise KeyError if omitted
            action = req.params['Action']
            for non_arg in non_args:
                # Remove, but raise KeyError if omitted
                args.pop(non_arg)
        except:
            raise webob.exc.HTTPBadRequest()

        LOG.debug(_('action: %s'), action)
        for key, value in args.items():
            LOG.debug(_('arg: %(key)s\t\tval: %(value)s') % locals())

        # Success!
        api_request = apirequest.APIRequest(self.controller, action,
                                            req.params['Version'], args)
        req.environ['ec2.request'] = api_request
        req.environ['ec2.action_args'] = args
        return self.application

class Authorizer(wsgi.Middleware):

    """Authorize an EC2 API request.

    Return a 401 if ec2.controller and ec2.action in WSGI environ may not be
    executed in ec2.context.
    """

    def __init__(self, application):
        super(Authorizer, self).__init__(application)
        self.action_roles = {
            'CloudController': {
                'DescribeAvailabilityZones': ['all'],
                'DescribeRegions': ['all'],
                'DescribeSnapshots': ['all'],
                'DescribeKeyPairs': ['all'],
                'CreateKeyPair': ['all'],
                'DeleteKeyPair': ['all'],
                'DescribeSecurityGroups': ['all'],
                'AuthorizeSecurityGroupIngress': ['netadmin'],
                'RevokeSecurityGroupIngress': ['netadmin'],
                'CreateSecurityGroup': ['netadmin'],
                'DeleteSecurityGroup': ['netadmin'],
                'GetConsoleOutput': ['projectmanager', 'sysadmin'],
                'DescribeVolumes': ['projectmanager', 'sysadmin'],
                'CreateVolume': ['projectmanager', 'sysadmin'],
                'AttachVolume': ['projectmanager', 'sysadmin'],
                'DetachVolume': ['projectmanager', 'sysadmin'],
                'DescribeInstances': ['all'],
                'DescribeAddresses': ['all'],
                'AllocateAddress': ['netadmin'],
                'ReleaseAddress': ['netadmin'],
                'AssociateAddress': ['netadmin'],
                'DisassociateAddress': ['netadmin'],
                'RunInstances': ['projectmanager', 'sysadmin'],
                'TerminateInstances': ['projectmanager', 'sysadmin'],
                'RebootInstances': ['projectmanager', 'sysadmin'],
                'UpdateInstance': ['projectmanager', 'sysadmin'],
                'DeleteVolume': ['projectmanager', 'sysadmin'],
                'DescribeImages': ['all'],
                'DeregisterImage': ['projectmanager', 'sysadmin'],
                'RegisterImage': ['projectmanager', 'sysadmin'],
                'DescribeImageAttribute': ['all'],
                'ModifyImageAttribute': ['projectmanager', 'sysadmin'],
                'UpdateImage': ['projectmanager', 'sysadmin'],
            },
            'AdminController': {
                # All actions have the same permission: ['none'] (the default)
                # superusers will be allowed to run them
                # all others will get HTTPUnauthorized.
            },
        }

    @webob.dec.wsgify
    def __call__(self, req):
        context = req.environ['ec2.context']
        controller = req.environ['ec2.request'].controller.__class__.__name__
        action = req.environ['ec2.request'].action
        allowed_roles = self.action_roles[controller].get(action, ['none'])
        if self._matches_any_role(context, allowed_roles):
            return self.application
        else:
            LOG.audit(_('Unauthorized request for controller=%(controller)s '
                        'and action=%(action)s') % locals(), context=context)
            raise webob.exc.HTTPUnauthorized()

    def _matches_any_role(self, context, roles):
        """Return True if any role in roles is allowed in context."""
        if context.user.is_superuser():
            return True
        if 'all' in roles:
            return True
        if 'none' in roles:
            return False
        return any(context.project.has_role(context.user_id, role)
                   for role in roles)

class Executor(wsgi.Application):

    """Execute an EC2 API request.

    Executes 'ec2.action' upon 'ec2.controller', passing 'ec2.context' and
    'ec2.action_args' (all variables in WSGI environ.)  Returns an XML
    response, or a 400 upon failure.
    """

    @webob.dec.wsgify
    def __call__(self, req):
        context = req.environ['ec2.context']
        api_request = req.environ['ec2.request']
        result = None
        try:
            result = api_request.invoke(context)
        except exception.InstanceNotFound as ex:
            LOG.info(_('InstanceNotFound raised: %s'), unicode(ex),
                     context=context)
            ec2_id = cloud.id_to_ec2_id(ex.instance_id)
            message = _('Instance %s not found') % ec2_id
            return self._error(req, context, type(ex).__name__, message)
        except exception.VolumeNotFound as ex:
            LOG.info(_('VolumeNotFound raised: %s'), unicode(ex),
                     context=context)
            ec2_id = cloud.id_to_ec2_id(ex.volume_id, 'vol-%08x')
            message = _('Volume %s not found') % ec2_id
            return self._error(req, context, type(ex).__name__, message)
        except exception.NotFound as ex:
            LOG.info(_('NotFound raised: %s'), unicode(ex), context=context)
            return self._error(req, context, type(ex).__name__, unicode(ex))
        except exception.ApiError as ex:
            LOG.exception(_('ApiError raised: %s'), unicode(ex),
                          context=context)
            if ex.code:
                return self._error(req, context, ex.code, unicode(ex))
            else:
                return self._error(req, context, type(ex).__name__,
                                   unicode(ex))
        except Exception as ex:
            extra = {'environment': req.environ}
            LOG.exception(_('Unexpected error raised: %s'), unicode(ex),
                          extra=extra, context=context)
            return self._error(req,
                               context,
                               'UnknownError',
                               _('An unknown error has occurred. '
                               'Please try your request again.'))
        else:
            resp = webob.Response()
            resp.status = 200
            resp.headers['Content-Type'] = 'text/xml'
            resp.body = str(result)
            return resp

    def _error(self, req, context, code, message):
        LOG.error("%s: %s", code, message, context=context)
        resp = webob.Response()
        resp.status = 400
        resp.headers['Content-Type'] = 'text/xml'
        resp.body = str('<?xml version="1.0"?>\n'
                         '<Response><Errors><Error><Code>%s</Code>'
                         '<Message>%s</Message></Error></Errors>'
                         '<RequestID>%s</RequestID></Response>' %
                         (utils.utf8(code), utils.utf8(message),
                         utils.utf8(context.request_id)))
        return resp

class Versions(wsgi.Application):

    @webob.dec.wsgify
    def __call__(self, req):
        """Respond to a request for all EC2 versions."""
        # available api versions
        versions = [
            '1.0',
            '2007-01-19',
            '2007-03-01',
            '2007-08-29',
            '2007-10-10',
            '2007-12-15',
            '2008-02-01',
            '2008-09-01',
            '2009-04-04',
        ]
        return ''.join('%s\n' % v for v in versions)
<|endoftext|>


<commit_before>from DashPykpi.kpistats import KpiStats
import os

def test_public_repo_access():
    if os.path.isfile('tinydb_for_KPI.json'):
        os.remove('tinydb_for_KPI.json')
    urls = ["https://github.com/benlaken/Comment_BadruddinAslam2014"]
    test = KpiStats(urls=urls)
    test.work()
    tmp = test.db.all()  # get the results from the DB object
    em1 = "Error, insufficent commits in public repo"
    assert tmp[0]['total_commits'] > 20, em1

def test_private_repo_access():
    if os.path.isfile('tinydb_for_KPI.json'):
        os.remove('tinydb_for_KPI.json')
    urls = ["https://github.com/UCL-RITS/RSD-Dashboard"]
    test = KpiStats(urls=urls)
    test.work()
    tmp = test.db.all()  # get the results from the DB object
    em1 = "Error, insufficent commits in privte repo"
    assert tmp[0]['total_commits'] > 1000, em1

def test_adds_rows_to_db():
    """Test the database file is created and populated appropriatley."""
    if os.path.isfile('tinydb_for_KPI.json'):
        os.remove('tinydb_for_KPI.json')
    urls = ["https://github.com/UCL-RITS/RSD-Dashboard",
            "https://github.com/benlaken/Comment_BadruddinAslam2014",
            "https://github.com/benlaken/fispy"]
    test = KpiStats(urls=urls)
    test.work()
    db_rows = test.db.all()
    assert len(db_rows) == 3, "Error, incorrect number of rows in DB"
<commit_msg>check the GitURLs development class returns a populated list<commit_after>from DashPykpi.kpistats import KpiStats, GitURLs
import os

def test_public_repo_access():
    if os.path.isfile('tinydb_for_KPI.json'):
        os.remove('tinydb_for_KPI.json')
    urls = ["https://github.com/benlaken/Comment_BadruddinAslam2014"]
    test = KpiStats(urls=urls)
    test.work()
    tmp = test.db.all()  # get the results from the DB object
    em1 = "Error, insufficent commits in public repo"
    assert tmp[0]['total_commits'] > 20, em1

def test_private_repo_access():
    if os.path.isfile('tinydb_for_KPI.json'):
        os.remove('tinydb_for_KPI.json')
    urls = ["https://github.com/UCL-RITS/RSD-Dashboard"]
    test = KpiStats(urls=urls)
    test.work()
    tmp = test.db.all()  # get the results from the DB object
    em1 = "Error, insufficent commits in privte repo"
    assert tmp[0]['total_commits'] > 1000, em1

def test_adds_rows_to_db():
    """Test the database file is created and populated appropriatley."""
    if os.path.isfile('tinydb_for_KPI.json'):
        os.remove('tinydb_for_KPI.json')
    urls = ["https://github.com/UCL-RITS/RSD-Dashboard",
            "https://github.com/benlaken/Comment_BadruddinAslam2014",
            "https://github.com/benlaken/fispy"]
    test = KpiStats(urls=urls)
    test.work()
    db_rows = test.db.all()
    assert len(db_rows) == 3, "Error, incorrect number of rows in DB"

def test_GitURLs_populates():
    """Test that the GitURLs class, meant for development, retrieves data."""
    url_list = GitURLs()
    assert len(url_list) > 0, "Error, GitURLs() class not retrieving urls."
<|endoftext|>


<commit_before>from django.conf.urls import patterns, url
from entities import views

from entities.views import EntityNoteCompoundPostView, EntityView, \
    EntityNoteView, EntityRangeView

urlpatterns = patterns('',
    url(r'^$', EntityView.as_view()),
    url(r'^(?P<radius>[0-9]{2})/$', 'entities.views.list_entities_inside_radius'),
    # url(r'^note/$', 'entities.views.list_notes'),
    url(r'^note/(?P<eid>[0-9]+)/$', 'entities.views.list_notes'),
    url(r'^compound/$', EntityNoteCompoundPostView.as_view()),

    # Get entities within a radius
    url(r'^radius/(?P<arg_lat>[0-9]\.[0-9]+)/(?P<arg_lng>[0-9]\.[0-9]+)/(?P<radius>[0-9]+)/$',
        EntityRangeView.as_view()),
)<commit_msg>cleaning up the url<commit_after>from django.conf.urls import patterns, url
from entities import views

from entities.views import EntityNoteCompoundPostView, EntityView, \
    EntityNoteView, EntityRangeView

urlpatterns = patterns('',
    url(r'^$', EntityView.as_view()),
    url(r'^(?P<radius>[0-9]{2})/$', 'entities.views.list_entities_inside_radius'),
    # url(r'^note/$', 'entities.views.list_notes'),
    url(r'^note/(?P<eid>[0-9]+)/$', 'entities.views.list_notes'),
    url(r'^compound/$', EntityNoteCompoundPostView.as_view()),

    # Get entities within a radius
    url(r'^radius/$', EntityRangeView.as_view()),
)<|endoftext|>


<commit_before># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2011 Openstack, LLC.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

from nova.rootwrap import filters

filterlist = [
    # nova/virt/disk/mount.py: 'kpartx', '-a', device
    # nova/virt/disk/mount.py: 'kpartx', '-d', device
    filters.CommandFilter("/sbin/kpartx", "root"),

    # nova/virt/disk/mount.py: 'tune2fs', '-c', 0, '-i', 0, mapped_device
    # nova/virt/xenapi/vm_utils.py: "tune2fs", "-O ^has_journal", part_path
    # nova/virt/xenapi/vm_utils.py: "tune2fs", "-j", partition_path
    filters.CommandFilter("/sbin/tune2fs", "root"),

    # nova/virt/disk/mount.py: 'mount', mapped_device, mount_dir
    # nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'..
    filters.CommandFilter("/bin/mount", "root"),

    # nova/virt/disk/mount.py: 'umount', mapped_device
    # nova/virt/xenapi/vm_utils.py: 'umount', dev_path
    filters.CommandFilter("/bin/umount", "root"),

    # nova/virt/disk/nbd.py: 'qemu-nbd', '-c', device, image
    # nova/virt/disk/nbd.py: 'qemu-nbd', '-d', device
    filters.CommandFilter("/usr/bin/qemu-nbd", "root"),

    # nova/virt/disk/loop.py: 'losetup', '--find', '--show', image
    # nova/virt/disk/loop.py: 'losetup', '--detach', device
    filters.CommandFilter("/sbin/losetup", "root"),

    # nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-i'
    # nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-m' dev
    filters.CommandFilter("/usr/bin/guestmount", "root"),

    # nova/virt/disk/guestfs.py: 'fusermount', 'u', mount_dir
    filters.CommandFilter("/bin/fusermount", "root"),
    filters.CommandFilter("/usr/bin/fusermount", "root"),

    # nova/virt/disk/api.py: 'tee', metadata_path
    # nova/virt/disk/api.py: 'tee', '-a', keyfile
    # nova/virt/disk/api.py: 'tee', netfile
    filters.CommandFilter("/usr/bin/tee", "root"),

    # nova/virt/disk/api.py: 'mkdir', '-p', sshdir
    # nova/virt/disk/api.py: 'mkdir', '-p', netdir
    filters.CommandFilter("/bin/mkdir", "root"),

    # nova/virt/disk/api.py: 'chown', 'root', sshdir
    # nova/virt/disk/api.py: 'chown', 'root:root', netdir
    # nova/virt/libvirt/connection.py: 'chown', os.getuid(), console_log
    # nova/virt/libvirt/connection.py: 'chown', os.getuid(), console_log
    # nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk')
    # nova/virt/xenapi/vm_utils.py: 'chown', os.getuid(), dev_path
    filters.CommandFilter("/bin/chown", "root"),

    # nova/virt/disk/api.py: 'chmod', '700', sshdir
    # nova/virt/disk/api.py: 'chmod', 755, netdir
    filters.CommandFilter("/bin/chmod", "root"),

    # nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
    # nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
    # nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
    # nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i..
    # nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'..
    # nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',..
    # nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',..
    # nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev)
    # nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1]
    # nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge
    # nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', ..
    # nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',..
    # nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ...
    # nova/network/linux_net.py: 'ip', 'link', 'set', interface, "address",..
    # nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up'
    # nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up'
    # nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, ..
    # nova/network/linux_net.py: 'ip', 'link', 'set', dev, "address", ..
    # nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up'
    filters.CommandFilter("/sbin/ip", "root"),

    # nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev
    filters.CommandFilter("/usr/sbin/tunctl", "root"),

    # nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
    # nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
    # nova/network/linux_net.py: 'ovs-vsctl', ....
    filters.CommandFilter("/usr/bin/ovs-vsctl", "root"),

    # nova/virt/libvirt/connection.py: 'dd', "if=%s" % virsh_output, ...
    filters.CommandFilter("/bin/dd", "root"),

    # nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ...
    filters.CommandFilter("/sbin/iscsiadm", "root"),

    # nova/virt/xenapi/vm_utils.py: "parted", "--script", ...
    # nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*.
    filters.CommandFilter("/sbin/parted", "root"),

    # nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s
    filters.CommandFilter("/sbin/fdisk", "root"),

    # nova/virt/xenapi/vm_utils.py: "e2fsck", "-f", "-p", partition_path
    filters.CommandFilter("/sbin/e2fsck", "root"),

    # nova/virt/xenapi/vm_utils.py: "resize2fs", partition_path
    filters.CommandFilter("/sbin/resize2fs", "root"),

    # nova/network/linux_net.py: 'ip[6]tables-save' % (cmd,), '-t', ...
    filters.CommandFilter("/sbin/iptables-save", "root"),
    filters.CommandFilter("/sbin/ip6tables-save", "root"),

    # nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
    filters.CommandFilter("/sbin/iptables-restore", "root"),
    filters.CommandFilter("/sbin/ip6tables-restore", "root"),

    # nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
    # nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
    filters.CommandFilter("/usr/bin/arping", "root"),

    # nova/network/linux_net.py: 'route', '-n'
    # nova/network/linux_net.py: 'route', 'del', 'default', 'gw'
    # nova/network/linux_net.py: 'route', 'add', 'default', 'gw'
    # nova/network/linux_net.py: 'route', '-n'
    # nova/network/linux_net.py: 'route', 'del', 'default', 'gw', old_gw, ..
    # nova/network/linux_net.py: 'route', 'add', 'default', 'gw', old_gateway
    filters.CommandFilter("/sbin/route", "root"),

    # nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
    filters.CommandFilter("/usr/bin/dhcp_release", "root"),

    # nova/network/linux_net.py: 'kill', '-9', pid
    # nova/network/linux_net.py: 'kill', '-HUP', pid
    filters.KillFilter("/bin/kill", "root",
                       ['-9', '-HUP'], ['/usr/sbin/dnsmasq']),

    # nova/network/linux_net.py: 'kill', pid
    filters.KillFilter("/bin/kill", "root", [''], ['/usr/sbin/radvd']),

    # nova/network/linux_net.py: dnsmasq call
    filters.DnsmasqFilter("/usr/sbin/dnsmasq", "root"),

    # nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'),..
    filters.CommandFilter("/usr/sbin/radvd", "root"),

    # nova/network/linux_net.py: 'brctl', 'addbr', bridge
    # nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
    # nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
    # nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
    filters.CommandFilter("/sbin/brctl", "root"),
    filters.CommandFilter("/usr/sbin/brctl", "root"),
    ]
<commit_msg>Add mkswap to rootwrap<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2011 Openstack, LLC.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

from nova.rootwrap import filters

filterlist = [
    # nova/virt/disk/mount.py: 'kpartx', '-a', device
    # nova/virt/disk/mount.py: 'kpartx', '-d', device
    filters.CommandFilter("/sbin/kpartx", "root"),

    # nova/virt/disk/mount.py: 'tune2fs', '-c', 0, '-i', 0, mapped_device
    # nova/virt/xenapi/vm_utils.py: "tune2fs", "-O ^has_journal", part_path
    # nova/virt/xenapi/vm_utils.py: "tune2fs", "-j", partition_path
    filters.CommandFilter("/sbin/tune2fs", "root"),

    # nova/virt/disk/mount.py: 'mount', mapped_device, mount_dir
    # nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'..
    filters.CommandFilter("/bin/mount", "root"),

    # nova/virt/disk/mount.py: 'umount', mapped_device
    # nova/virt/xenapi/vm_utils.py: 'umount', dev_path
    filters.CommandFilter("/bin/umount", "root"),

    # nova/virt/disk/nbd.py: 'qemu-nbd', '-c', device, image
    # nova/virt/disk/nbd.py: 'qemu-nbd', '-d', device
    filters.CommandFilter("/usr/bin/qemu-nbd", "root"),

    # nova/virt/disk/loop.py: 'losetup', '--find', '--show', image
    # nova/virt/disk/loop.py: 'losetup', '--detach', device
    filters.CommandFilter("/sbin/losetup", "root"),

    # nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-i'
    # nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-m' dev
    filters.CommandFilter("/usr/bin/guestmount", "root"),

    # nova/virt/disk/guestfs.py: 'fusermount', 'u', mount_dir
    filters.CommandFilter("/bin/fusermount", "root"),
    filters.CommandFilter("/usr/bin/fusermount", "root"),

    # nova/virt/disk/api.py: 'tee', metadata_path
    # nova/virt/disk/api.py: 'tee', '-a', keyfile
    # nova/virt/disk/api.py: 'tee', netfile
    filters.CommandFilter("/usr/bin/tee", "root"),

    # nova/virt/disk/api.py: 'mkdir', '-p', sshdir
    # nova/virt/disk/api.py: 'mkdir', '-p', netdir
    filters.CommandFilter("/bin/mkdir", "root"),

    # nova/virt/disk/api.py: 'chown', 'root', sshdir
    # nova/virt/disk/api.py: 'chown', 'root:root', netdir
    # nova/virt/libvirt/connection.py: 'chown', os.getuid(), console_log
    # nova/virt/libvirt/connection.py: 'chown', os.getuid(), console_log
    # nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk')
    # nova/virt/xenapi/vm_utils.py: 'chown', os.getuid(), dev_path
    filters.CommandFilter("/bin/chown", "root"),

    # nova/virt/disk/api.py: 'chmod', '700', sshdir
    # nova/virt/disk/api.py: 'chmod', 755, netdir
    filters.CommandFilter("/bin/chmod", "root"),

    # nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
    # nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
    # nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
    # nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i..
    # nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'..
    # nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',..
    # nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',..
    # nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev)
    # nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1]
    # nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge
    # nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', ..
    # nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',..
    # nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ...
    # nova/network/linux_net.py: 'ip', 'link', 'set', interface, "address",..
    # nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up'
    # nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up'
    # nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, ..
    # nova/network/linux_net.py: 'ip', 'link', 'set', dev, "address", ..
    # nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up'
    filters.CommandFilter("/sbin/ip", "root"),

    # nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev
    filters.CommandFilter("/usr/sbin/tunctl", "root"),

    # nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
    # nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
    # nova/network/linux_net.py: 'ovs-vsctl', ....
    filters.CommandFilter("/usr/bin/ovs-vsctl", "root"),

    # nova/virt/libvirt/connection.py: 'dd', "if=%s" % virsh_output, ...
    filters.CommandFilter("/bin/dd", "root"),

    # nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ...
    filters.CommandFilter("/sbin/iscsiadm", "root"),

    # nova/virt/xenapi/vm_utils.py: "parted", "--script", ...
    # nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*.
    filters.CommandFilter("/sbin/parted", "root"),

    # nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s
    filters.CommandFilter("/sbin/fdisk", "root"),

    # nova/virt/xenapi/vm_utils.py: "e2fsck", "-f", "-p", partition_path
    filters.CommandFilter("/sbin/e2fsck", "root"),

    # nova/virt/xenapi/vm_utils.py: "resize2fs", partition_path
    filters.CommandFilter("/sbin/resize2fs", "root"),

    # nova/network/linux_net.py: 'ip[6]tables-save' % (cmd,), '-t', ...
    filters.CommandFilter("/sbin/iptables-save", "root"),
    filters.CommandFilter("/sbin/ip6tables-save", "root"),

    # nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
    filters.CommandFilter("/sbin/iptables-restore", "root"),
    filters.CommandFilter("/sbin/ip6tables-restore", "root"),

    # nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
    # nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
    filters.CommandFilter("/usr/bin/arping", "root"),

    # nova/network/linux_net.py: 'route', '-n'
    # nova/network/linux_net.py: 'route', 'del', 'default', 'gw'
    # nova/network/linux_net.py: 'route', 'add', 'default', 'gw'
    # nova/network/linux_net.py: 'route', '-n'
    # nova/network/linux_net.py: 'route', 'del', 'default', 'gw', old_gw, ..
    # nova/network/linux_net.py: 'route', 'add', 'default', 'gw', old_gateway
    filters.CommandFilter("/sbin/route", "root"),

    # nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
    filters.CommandFilter("/usr/bin/dhcp_release", "root"),

    # nova/network/linux_net.py: 'kill', '-9', pid
    # nova/network/linux_net.py: 'kill', '-HUP', pid
    filters.KillFilter("/bin/kill", "root",
                       ['-9', '-HUP'], ['/usr/sbin/dnsmasq']),

    # nova/network/linux_net.py: 'kill', pid
    filters.KillFilter("/bin/kill", "root", [''], ['/usr/sbin/radvd']),

    # nova/network/linux_net.py: dnsmasq call
    filters.DnsmasqFilter("/usr/sbin/dnsmasq", "root"),

    # nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'),..
    filters.CommandFilter("/usr/sbin/radvd", "root"),

    # nova/network/linux_net.py: 'brctl', 'addbr', bridge
    # nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
    # nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
    # nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
    filters.CommandFilter("/sbin/brctl", "root"),
    filters.CommandFilter("/usr/sbin/brctl", "root"),

    # nova/virt/libvirt/utils.py: 'mkswap'
    # nova/virt/xenapi/vm_utils.py: 'mkswap'
    filters.CommandFilter("/sbin/mkswap", "root"),
    ]
<|endoftext|>


<commit_before>from tastypie.resources import ModelResource
from brubeck.multimedia.models import AudioClip,Slideshow,Video

class AudioResource(ModelResource):
	    tag = fields.ToManyField('brubeck.tagging.api.TagResource', 'tag')
        class Meta:
                queryset = AudioClip.objects.all()
                resource_name = 'audioclip'
                allowed_methods = ['get']

class SlideshowResource(ModelResource):
	    tag = fields.ToManyField('brubeck.tagging.api.TagResource', 'tag')
        class Meta:
                queryset = Slideshow.objects.all()
                resource_name = 'slideshow'
                allowed_methods = ['get']

class VideoResource(ModelResource):
		tag = fields.ToManyField('brubeck.tagging.api.TagResource', 'tag')
        class Meta:
                queryset = Video.objects.all()
                resource_name = 'video'
                allowed_methods = ['get']
<commit_msg>Multimedia API fixes<commit_after>from tastypie.resources import ModelResource
from tastypie import fields
from brubeck.multimedia.models import AudioClip,Slideshow,Video

class AudioResource(ModelResource):
		tag = fields.ToManyField('brubeck.tagging.api.TagResource', 'tag')
		class Meta:
			queryset = AudioClip.objects.all()
			resource_name = 'audioclip'
			allowed_methods = ['get']

class SlideshowResource(ModelResource):
		tag = fields.ToManyField('brubeck.tagging.api.TagResource', 'tag')
		class Meta:
			queryset = Slideshow.objects.all()
			resource_name = 'slideshow'
			allowed_methods = ['get']

class VideoResource(ModelResource):
		tag = fields.ToManyField('brubeck.tagging.api.TagResource', 'tag')
		class Meta:
			queryset = Video.objects.all()
			resource_name = 'video'
			allowed_methods = ['get']
<|endoftext|>


<commit_before>import os

from tempfile import NamedTemporaryFile

from twisted.trial.unittest import TestCase
from twisted.conch.manhole_ssh import ConchFactory
from twisted.conch.ssh import (
    transport, userauth, connection, channel, session)
from twisted.internet import defer, protocol, reactor
from twisted.internet.defer import inlineCallbacks

from vumi.middleware.manhole import ManholeMiddleware

# these are shipped along with Twisted
private_key = ConchFactory.privateKeys['ssh-rsa']
public_key = ConchFactory.publicKeys['ssh-rsa']

class DummyWorker(object):
    pass

class ClientTransport(transport.SSHClientTransport):

    def verifyHostKey(self, pub_key, fingerprint):
        return defer.succeed(1)

    def connectionSecure(self):
        return self.requestService(ClientUserAuth(
            os.getlogin(), ClientConnection(self.factory.channelConnected)))

class ClientUserAuth(userauth.SSHUserAuthClient):

    def getPassword(self, prompt=None):
        # Not doing password based auth
        return

    def getPublicKey(self):
        return public_key.blob()

    def getPrivateKey(self):
        return defer.succeed(private_key.keyObject)

class ClientConnection(connection.SSHConnection):

    def __init__(self, channel_connected):
        connection.SSHConnection.__init__(self)
        self._channel_connected = channel_connected

    def serviceStarted(self):
        channel = ClientChannel(self._channel_connected,
                                conn=self)
        self.openChannel(channel)

class ClientChannel(channel.SSHChannel):

    name = 'session'

    def __init__(self, channel_connected, *args, **kwargs):
        channel.SSHChannel.__init__(self, *args, **kwargs)
        self._channel_connected = channel_connected
        self.buffer = u''
        self.queue = defer.DeferredQueue()

    def channelOpen(self, data):
        self._channel_connected.callback(self)

    def dataReceived(self, data):
        self.buffer += data
        lines = self.buffer.split('\r\n')
        for line in lines[:-1]:
            self.queue.put(line)
        self.buffer = lines[-1]

class ManholeMiddlewareTestCase(TestCase):

    @inlineCallbacks
    def setUp(self):

        self.private_key_file = NamedTemporaryFile(mode='w')
        self.private_key_file.write(private_key.toString('OPENSSH'))
        self.private_key_file.flush()

        self.pub_key_file = NamedTemporaryFile(mode='w')
        self.pub_key_file.write(public_key.toString('OPENSSH'))
        self.pub_key_file.flush()

        self._middlewares = []

        mw = self.get_middleware({
            'authorized_keys': [self.pub_key_file.name]
        })
        host = mw.socket.getHost()

        factory = protocol.ClientFactory()
        factory.protocol = ClientTransport

        wait_for_channel = defer.Deferred()

        factory.channelConnected = wait_for_channel
        self.socket = reactor.connectTCP(host.host, host.port, factory)

        self.channel = yield self.open_shell(wait_for_channel)

    @inlineCallbacks
    def open_shell(self, wait_for_channel):
        channel = yield wait_for_channel
        conn = channel.conn
        term = session.packRequest_pty_req("xterm-mono", (0, 0, 0, 0), '')
        yield conn.sendRequest(channel, 'pty-req', term, wantReply=1)
        yield conn.sendRequest(channel, 'shell', '', wantReply=1)
        defer.returnValue(channel)

    def tearDown(self):
        for mw in self._middlewares:
            mw.teardown_middleware()
        self.socket.disconnect()

    def get_middleware(self, config={}):
        config = dict({
            'port': '0',
        }, **config)

        worker = DummyWorker()
        worker.transport_name = 'foo'

        mw = ManholeMiddleware("test_manhole_mw", config, worker)
        mw.setup_middleware()
        self._middlewares.append(mw)
        return mw

    @inlineCallbacks
    def test_mw(self):
        self.channel.write('print worker.transport_name\n')
        # read the echoed line we sent first
        yield self.channel.queue.get()
        # next is the server response
        received_line = yield self.channel.queue.get()
        self.assertEqual(received_line, 'foo')
<commit_msg>test cleanup<commit_after>import os

from tempfile import NamedTemporaryFile

from twisted.trial.unittest import TestCase
from twisted.conch.manhole_ssh import ConchFactory
from twisted.conch.ssh import (
    transport, userauth, connection, channel, session)
from twisted.internet import defer, protocol, reactor
from twisted.internet.defer import inlineCallbacks

from vumi.middleware.manhole import ManholeMiddleware

# these are shipped along with Twisted
private_key = ConchFactory.privateKeys['ssh-rsa']
public_key = ConchFactory.publicKeys['ssh-rsa']

class DummyWorker(object):
    pass

class ClientTransport(transport.SSHClientTransport):

    def verifyHostKey(self, pub_key, fingerprint):
        return defer.succeed(1)

    def connectionSecure(self):
        return self.requestService(ClientUserAuth(
            os.getlogin(), ClientConnection(self.factory.channelConnected)))

class ClientUserAuth(userauth.SSHUserAuthClient):

    def getPassword(self, prompt=None):
        # Not doing password based auth
        return

    def getPublicKey(self):
        return public_key.blob()

    def getPrivateKey(self):
        return defer.succeed(private_key.keyObject)

class ClientConnection(connection.SSHConnection):

    def __init__(self, channel_connected):
        connection.SSHConnection.__init__(self)
        self._channel_connected = channel_connected

    def serviceStarted(self):
        channel = ClientChannel(self._channel_connected,
                                conn=self)
        self.openChannel(channel)

class ClientChannel(channel.SSHChannel):

    name = 'session'

    def __init__(self, channel_connected, *args, **kwargs):
        channel.SSHChannel.__init__(self, *args, **kwargs)
        self._channel_connected = channel_connected
        self.buffer = u''
        self.queue = defer.DeferredQueue()

    def channelOpen(self, data):
        self._channel_connected.callback(self)

    def dataReceived(self, data):
        self.buffer += data
        lines = self.buffer.split('\r\n')
        for line in lines[:-1]:
            self.queue.put(line)
        self.buffer = lines[-1]

class ManholeMiddlewareTestCase(TestCase):

    def setUp(self):

        self.private_key_file = NamedTemporaryFile(mode='w')
        self.private_key_file.write(private_key.toString('OPENSSH'))
        self.private_key_file.flush()

        self.pub_key_file = NamedTemporaryFile(mode='w')
        self.pub_key_file.write(public_key.toString('OPENSSH'))
        self.pub_key_file.flush()

        self._middlewares = []
        self._client_sockets = []

        self.mw = self.get_middleware({
            'authorized_keys': [self.pub_key_file.name]
        })

    @inlineCallbacks
    def open_shell(self, middleware):
        host = middleware.socket.getHost()
        factory = protocol.ClientFactory()
        factory.protocol = ClientTransport
        factory.channelConnected = defer.Deferred()

        socket = reactor.connectTCP(host.host, host.port, factory)

        channel = yield factory.channelConnected
        conn = channel.conn
        term = session.packRequest_pty_req("xterm-mono", (0, 0, 0, 0), '')
        yield conn.sendRequest(channel, 'pty-req', term, wantReply=1)
        yield conn.sendRequest(channel, 'shell', '', wantReply=1)
        self._client_sockets.append(socket)
        defer.returnValue(channel)

    def tearDown(self):
        for socket in self._client_sockets:
            socket.disconnect()

        for mw in self._middlewares:
            mw.teardown_middleware()

    def get_middleware(self, config={}):
        config = dict({
            'port': '0',
        }, **config)

        worker = DummyWorker()
        worker.transport_name = 'foo'

        mw = ManholeMiddleware("test_manhole_mw", config, worker)
        mw.setup_middleware()
        self._middlewares.append(mw)
        return mw

    @inlineCallbacks
    def test_mw(self):
        shell = yield self.open_shell(self.mw)
        shell.write('print worker.transport_name\n')
        # read the echoed line we sent first
        yield shell.queue.get()
        # next is the server response
        received_line = yield shell.queue.get()
        self.assertEqual(received_line, 'foo')
<|endoftext|>


<commit_before>import asyncio, re, logging
import json

from hangups.ui.utils import get_conv_name
import plugins

def _initialise(bot):
    plugins.register_handler(_handle_autoreply, type="message")
    plugins.register_admin_command(["autoreply"])

@asyncio.coroutine
def _handle_autoreply(bot, event, command):
    autoreplies_enabled = bot.get_config_suboption(event.conv.id_, 'autoreplies_enabled')
    if not autoreplies_enabled:
        return

    """Handle autoreplies to keywords in messages"""

    logging.info("autoreply: {}".format(event.conv.id_))

    autoreplies_list = bot.get_config_suboption(event.conv_id, 'autoreplies')
    if autoreplies_list:
        for kwds, sentence in autoreplies_list:
            for kw in kwds:
                if _words_in_text(kw, event.text) or kw == "*":
                    bot.send_message_parsed(event.conv, sentence)
                    break

def _words_in_text(word, text):
    """Return True if word is in text"""

    if word.startswith("regex:"):
        word = word[6:]
    else:
        word = re.escape(word)

    regexword = "(?<!\w)" + word + "(?!\w)"

    return True if re.search(regexword, text, re.IGNORECASE) else False

def autoreply(bot, event, cmd=None, *args):
    """adds or removes an autoreply.
    Format:
    /bot autoreply add [["question1","question2"],"answer"] // add an autoreply
    /bot autoreply remove [["question"],"answer"] // remove an autoreply
    /bot autoreply // view all autoreplies
    """

    path = ["autoreplies"]
    argument = " ".join(args)
    html = ""
    value = bot.config.get_by_path(path)

    if cmd == 'add':
        if isinstance(value, list):
            value.append(json.loads(argument))
            bot.config.set_by_path(path, value)
            bot.config.save()
        else:
            html = "Append failed on non-list"
    elif cmd == 'remove':
        if isinstance(value, list):
            value.remove(json.loads(argument))
            bot.config.set_by_path(path, value)
            bot.config.save()
        else:
            html = "Remove failed on non-list"

    # Reload the config
    bot.config.load()

    if html == "":
        value = bot.config.get_by_path(path)
        html = "Autoreply config:
 {}".format(value)

    bot.send_html_to_conversation(event.conv_id, html)
<commit_msg>random selection from list of autoreplies, re: #239<commit_after>import asyncio, re, logging, json, random

import plugins

def _initialise(bot):
    plugins.register_handler(_handle_autoreply, type="message")
    plugins.register_admin_command(["autoreply"])

def _handle_autoreply(bot, event, command):
    autoreplies_enabled = bot.get_config_suboption(event.conv.id_, 'autoreplies_enabled')
    if not autoreplies_enabled:
        return

    """Handle autoreplies to keywords in messages"""

    logging.info("autoreply: {}".format(event.conv.id_))

    autoreplies_list = bot.get_config_suboption(event.conv_id, 'autoreplies')
    if autoreplies_list:
        for kwds, sentences in autoreplies_list:
            for kw in kwds:
                if _words_in_text(kw, event.text) or kw == "*":
                    if isinstance(sentences, list):
                        message = random.choice(sentences)
                    else:
                        message = sentences
                    bot.send_message_parsed(event.conv, message)
                    break

def _words_in_text(word, text):
    """Return True if word is in text"""

    if word.startswith("regex:"):
        word = word[6:]
    else:
        word = re.escape(word)

    regexword = "(?<!\w)" + word + "(?!\w)"

    return True if re.search(regexword, text, re.IGNORECASE) else False

def autoreply(bot, event, cmd=None, *args):
    """adds or removes an autoreply.
    Format:
    /bot autoreply add [["question1","question2"],"answer"] // add an autoreply
    /bot autoreply remove [["question"],"answer"] // remove an autoreply
    /bot autoreply // view all autoreplies
    """

    path = ["autoreplies"]
    argument = " ".join(args)
    html = ""
    value = bot.config.get_by_path(path)

    if cmd == 'add':
        if isinstance(value, list):
            value.append(json.loads(argument))
            bot.config.set_by_path(path, value)
            bot.config.save()
        else:
            html = "Append failed on non-list"
    elif cmd == 'remove':
        if isinstance(value, list):
            value.remove(json.loads(argument))
            bot.config.set_by_path(path, value)
            bot.config.save()
        else:
            html = "Remove failed on non-list"

    # Reload the config
    bot.config.load()

    if html == "":
        value = bot.config.get_by_path(path)
        html = "Autoreply config:
 {}".format(value)

    bot.send_html_to_conversation(event.conv_id, html)
<|endoftext|>


<commit_before>import optparse
import os
import signal
import subprocess
import sys

from .env import Env
from .version import __version__

# must have shell = True on Windows
is_windows = sys.platform == 'win32'

if is_windows:
    params = {'creationflags': subprocess.CREATE_NEW_PROCESS_GROUP}
else:
    params = {'preexec_fn': os.setsid}

class Response(Exception):
    def __init__(self, message='', status=0):
        self.message = message
        self.status = status

class Runner(object):
    envdir_usage = "usage: %prog [--help] [--version] dir child"
    envshell_usage = "usage: %prog [--help] [--version] dir"

    def __init__(self):
        self.parser = optparse.OptionParser(version=__version__)
        self.parser.disable_interspersed_args()
        self.parser.prog = 'envdir'
        signal.signal(signal.SIGTERM, self.terminate)

    def path(self, path):
        real_path = os.path.realpath(os.path.expanduser(path))
        if not os.path.exists(real_path):
            # use 111 error code to adher to envdir's standard
            raise Response("envdir %r does not exist" % path, 111)
        if not os.path.isdir(real_path):
            # use 111 error code to adher to envdir's standard
            raise Response("envdir %r not a directory" % path, 111)
        return real_path

    def open(self, path=None, stacklevel=1):
        if path is None:
            frame = sys._getframe()
            get_parent = lambda frame: frame.f_back
            for _ in range(stacklevel):
                frame = get_parent(frame)
            if frame is not None:
                callerdir = os.path.dirname(frame.f_code.co_filename)
                path = os.path.join(callerdir, 'envdir')
            else:
                # last holdout, assume cwd
                path = 'envdir'
        return Env(self.path(path))

    def shell(self, name, *args):
        self.parser.set_usage(self.envshell_usage)
        self.parser.prog = 'envshell'
        options, args = self.parser.parse_args(list(args))

        if len(args) == 0:
            raise Response("%s\nError: incorrect number of arguments" %
                           (self.parser.get_usage()), 2)

        sys.stdout.write("Launching envshell for %s. "
                         "Type 'exit' or 'Ctrl+D' to return.\n" %
                         self.path(args[0]))
        sys.stdout.flush()
        self.open(args[0], 2)

        shell = os.environ['SHELL']

        try:
            subprocess.check_call([shell],
                                  universal_newlines=True,
                                  bufsize=0,
                                  close_fds=not is_windows,
                                  **params)
        except OSError as err:
            if err.errno == 2:
                raise Response("Unable to find shell %s" % shell, err.errno)
            else:
                raise Response("An error occurred: %s" % err,
                               status=err.errno)

        raise Response()

    def run(self, name, *args):
        self.parser.set_usage(self.envdir_usage)
        self.parser.prog = 'envdir'
        options, args = self.parser.parse_args(list(args))

        if len(args) < 2:
            raise Response("%s\nError: incorrect number of arguments\n" %
                           (self.parser.get_usage()), 2)

        self.open(args[0], 2)

        # the args to call later
        args = args[1:]

        # in case someone passes in -- for any reason to separate the commands
        if args[0] == '--':
            args = args[1:]

        try:
            self.process = subprocess.Popen(args,
                                            universal_newlines=True,
                                            bufsize=0,
                                            close_fds=not is_windows,
                                            **params)
            self.process.wait()
        except OSError as err:
            if err.errno == 2:
                raise Response("Unable to find command %s" %
                               args[0], err.errno)
            else:
                raise Response(status=err.errno)
        except KeyboardInterrupt:
            self.terminate()
        raise Response(status=self.process.returncode)

    def terminate(self):
        # first send mellow signal
        self.quit(signal.SIGTERM)
        if self.process.poll() is None:
            # still running, kill it
            self.quit(signal.SIGKILL)

    def quit(self, signal):
        if self.process.poll() is None:
            proc_pgid = os.getpgid(self.process.pid)
            if os.getpgrp() == proc_pgid:
                # Just kill the proc, don't kill ourselves too
                os.kill(self.process.pid, signal)
            else:
                # Kill the whole process group
                os.killpg(proc_pgid, signal)
<commit_msg>Fixed parameter problem with signal handler.<commit_after>import optparse
import os
import signal
import subprocess
import sys

from .env import Env
from .version import __version__

# must have shell = True on Windows
is_windows = sys.platform == 'win32'

if is_windows:
    params = {'creationflags': subprocess.CREATE_NEW_PROCESS_GROUP}
else:
    params = {'preexec_fn': os.setsid}

class Response(Exception):
    def __init__(self, message='', status=0):
        self.message = message
        self.status = status

class Runner(object):
    envdir_usage = "usage: %prog [--help] [--version] dir child"
    envshell_usage = "usage: %prog [--help] [--version] dir"

    def __init__(self):
        self.parser = optparse.OptionParser(version=__version__)
        self.parser.disable_interspersed_args()
        self.parser.prog = 'envdir'
        signal.signal(signal.SIGTERM, self.terminate)

    def path(self, path):
        real_path = os.path.realpath(os.path.expanduser(path))
        if not os.path.exists(real_path):
            # use 111 error code to adher to envdir's standard
            raise Response("envdir %r does not exist" % path, 111)
        if not os.path.isdir(real_path):
            # use 111 error code to adher to envdir's standard
            raise Response("envdir %r not a directory" % path, 111)
        return real_path

    def open(self, path=None, stacklevel=1):
        if path is None:
            frame = sys._getframe()
            get_parent = lambda frame: frame.f_back
            for _ in range(stacklevel):
                frame = get_parent(frame)
            if frame is not None:
                callerdir = os.path.dirname(frame.f_code.co_filename)
                path = os.path.join(callerdir, 'envdir')
            else:
                # last holdout, assume cwd
                path = 'envdir'
        return Env(self.path(path))

    def shell(self, name, *args):
        self.parser.set_usage(self.envshell_usage)
        self.parser.prog = 'envshell'
        options, args = self.parser.parse_args(list(args))

        if len(args) == 0:
            raise Response("%s\nError: incorrect number of arguments" %
                           (self.parser.get_usage()), 2)

        sys.stdout.write("Launching envshell for %s. "
                         "Type 'exit' or 'Ctrl+D' to return.\n" %
                         self.path(args[0]))
        sys.stdout.flush()
        self.open(args[0], 2)

        shell = os.environ['SHELL']

        try:
            subprocess.check_call([shell],
                                  universal_newlines=True,
                                  bufsize=0,
                                  close_fds=not is_windows,
                                  **params)
        except OSError as err:
            if err.errno == 2:
                raise Response("Unable to find shell %s" % shell, err.errno)
            else:
                raise Response("An error occurred: %s" % err,
                               status=err.errno)

        raise Response()

    def run(self, name, *args):
        self.parser.set_usage(self.envdir_usage)
        self.parser.prog = 'envdir'
        options, args = self.parser.parse_args(list(args))

        if len(args) < 2:
            raise Response("%s\nError: incorrect number of arguments\n" %
                           (self.parser.get_usage()), 2)

        self.open(args[0], 2)

        # the args to call later
        args = args[1:]

        # in case someone passes in -- for any reason to separate the commands
        if args[0] == '--':
            args = args[1:]

        try:
            self.process = subprocess.Popen(args,
                                            universal_newlines=True,
                                            bufsize=0,
                                            close_fds=not is_windows,
                                            **params)
            self.process.wait()
        except OSError as err:
            if err.errno == 2:
                raise Response("Unable to find command %s" %
                               args[0], err.errno)
            else:
                raise Response(status=err.errno)
        except KeyboardInterrupt:
            self.terminate()
        raise Response(status=self.process.returncode)

    def terminate(self, *args, **kwargs):
        # first send mellow signal
        self.quit(signal.SIGTERM)
        if self.process.poll() is None:
            # still running, kill it
            self.quit(signal.SIGKILL)

    def quit(self, signal):
        if self.process.poll() is None:
            proc_pgid = os.getpgid(self.process.pid)
            if os.getpgrp() == proc_pgid:
                # Just kill the proc, don't kill ourselves too
                os.kill(self.process.pid, signal)
            else:
                # Kill the whole process group
                os.killpg(proc_pgid, signal)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from twisted.trial.unittest import SkipTest
from twisted.internet.defer import inlineCallbacks
from vumi.message import TransportUserMessage
from vumi.transports.gsm.tests.test_gsm_stubs import (
    FakeGSMTransport, FakeGammuPhone, FailingFakeGammuPhone)
from vumi.transports.tests.test_base import TransportTestCase
from vumi.transports.failures import FailureMessage
from datetime import datetime

class GSMTransportTestCase(TransportTestCase):

    transport_class = FakeGSMTransport
    transport_name = 'fake_gsm_transport'
    to_addr = '27761234567'

    @inlineCallbacks
    def setUp(self):
        yield super(GSMTransportTestCase, self).setUp()
        self.transport = yield self.get_transport({
            'transport_name': self.transport_name,
            'phone_number': self.to_addr,
            'country_code': 27,
            'poll_interval': 120,
            'gammu': {
                'UseGlobalDebugFile': 0,
                'DebugFile': '',
                'SyncTime': 0,
                'Connection': 'at',
                'LockDevice': 0,
                'DebugLevel': '',
                'Device': '/dev/path-to-modem',
                'StartInfo': 0,
                'Model': ''
            }
        })

    def tearDown(self):
        self.transport.r_server.teardown()

    def mk_msg(self, **kwargs):
        defaults = {
            'to_addr': '27761234567',
            'from_addr': '27761234567',
            'content': 'hello world',
            'transport_name': self.transport_name,
            'transport_type': 'sms',
            'transport_metadata': {},
        }
        defaults.update(kwargs)
        return TransportUserMessage(**defaults)

    @inlineCallbacks
    def test_receiving_sms(self):
        phone = FakeGammuPhone([{
            'Location': 0,
            'Text': 'hello world',
            'Number': '27761234567',
            'Type': 'Deliver',
            'DateTime': datetime.now(),
            'SMSCDateTime': datetime.now(),
        }])

        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual([], self.get_dispatched_failures())
        self.assertEqual([], self.get_dispatched_events())
        [msg] = self.get_dispatched_messages()
        self.assertEqual(msg['to_addr'], '+%s' % self.to_addr)
        self.assertEqual(msg['content'], 'hello world')
        self.assertEqual(phone.messages, [])

    @inlineCallbacks
    def test_sending_sms_success(self):
        phone = FakeGammuPhone()
        # generate a fake message
        msg = self.mk_msg()
        # dispatch as if being sent from an application
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        # the transport should store them in redis until the next
        # send & receive loop is triggered
        queue_name = self.transport.redis_outbound_queue
        key = self.transport.r_key(queue_name)
        self.assertEqual(self.transport.r_server.llen(key), 1)
        # trigger manually
        yield self.transport.receive_and_send_messages(phone)
        # all should be happy
        self.assertEqual(self.get_dispatched_failures(), [])
        # the phone's outbox should have the messages
        self.assertEqual(phone.outbox, [{
            'Type': 'Status_Report',
            'SMSC': {
                'Location': 1,
            },
            'Text': msg['content'],
            'Number': msg['to_addr'],
        }])
        # the redis list should be empty
        self.assertEqual(self.transport.r_server.llen(key), 0)

    @inlineCallbacks
    def test_sending_multipart_sms(self):
        phone = FakeGammuPhone()
        msg = self.mk_msg()
        msg['content'] = 'a' * 200  # doesn't fit in a single SMS
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual(len(phone.outbox), 2)
        self.assertEqual(phone.outbox[0]['Text'], 'a' * 153)
        self.assertEqual(phone.outbox[0]['UDH']['PartNumber'], 1)
        self.assertEqual(phone.outbox[0]['UDH']['AllParts'], 2)
        self.assertEqual(phone.outbox[1]['Text'], 'a' * 47)
        self.assertEqual(phone.outbox[1]['UDH']['PartNumber'], 2)
        self.assertEqual(phone.outbox[1]['UDH']['AllParts'], 2)

    @inlineCallbacks
    def test_receiving_multipart_sms(self):
        phone = FakeGammuPhone([{
            'MessageReference': u'9c103356fbf243b3805db555e4f9c2d3',
            'Number': u'27761234567',
            'DateTime': datetime.now(),
            'SMSCDateTime': datetime.now(),
            'Text': u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
                    u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
                    u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
                    u'aaaaaaaaaa',
            'Type': 'Deliver',
            'Location': 0,
            'UDH': {
                'AllParts': 2,
                'PartNumber': 1,
                'Text': '\x05\x00\x03\x19\x02\x01',
                'Type': 'ConcatenatedMessages'
            }
        }, {
            'MessageReference': u'9c103356fbf243b3805db555e4f9c2d3',
            'Number': u'27761234567',
            'DateTime': datetime.now(),
            'SMSCDateTime': datetime.now(),
            'Text': u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
            'Type': 'Deliver',
            'Location': 1,
            'UDH': {
                'AllParts': 2,
                'PartNumber': 2,
                'Text': '\x05\x00\x03\x19\x02\x02',
                'Type': 'ConcatenatedMessages'
            }
        }])
        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual([], self.get_dispatched_failures())
        self.assertEqual([], self.get_dispatched_events())
        [message] = self.get_dispatched_messages()
        self.assertEqual(message['content'], 'a' * 200)
        self.assertEqual(message['from_addr'], '+27761234567')

    @inlineCallbacks
    def test_sending_sms_failure(self):
        phone = FailingFakeGammuPhone()
        msg = self.mk_msg()
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual([], self.get_dispatched_messages())
        self.assertEqual([], self.get_dispatched_events())
        [failure] = self.get_dispatched_failures()
        self.assertEqual(failure['message'], msg.payload)
        self.assertEqual(failure['message_type'],
                            FailureMessage.MESSAGE_TYPE)
        self.assertEqual(failure['failure_code'],
                            FailureMessage.FC_UNSPECIFIED)

    @inlineCallbacks
    def test_delivery_reports(self):
        # fake an outbound delivery first
        phone = FakeGammuPhone()
        msg = self.mk_msg()
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        # have the phone handle it
        received, sent = yield self.transport.receive_and_send_messages(phone)
        # test correct behaviour
        self.assertEqual([], received)
        # expand the sent messages, should be a vumi message and a dict of
        # MessageReference -> GammuMessage mappings
        [(vumi_msg, gammu_messages)] = sent
        self.assertEqual(vumi_msg, msg)
        self.assertEqual(gammu_messages.keys(), [1]) # reference number given by modem
        self.assertEqual(gammu_messages[1]['Text'], msg['content'])

        # Now fake the delivery report for the given reference number
        phone = FakeGammuPhone([{
            'SMSCDateTime': datetime.now(),
            'Class': 0,
            'Text': u'Delivered',
            'Number': u'+27764493806',
            'DateTime': datetime.now(),
            'MessageReference': 1,
            'Length': 9,
            'Location': 0,
            'Type': 'Status_Report',
        }])

        # received & sent
        [delivery_report], [] = yield self.transport.receive_and_send_messages(phone)
        self.assertEqual(delivery_report['Text'], 'Delivered')
        self.assertEqual(delivery_report['MessageReference'], 1)

        [delivery_report] = self.get_dispatched_events()
        self.assertEqual(delivery_report['delivery_status'], 'delivered')
        self.assertEqual(delivery_report['user_message_id'], msg['message_id'])

    @inlineCallbacks
    def test_multipart_delivery_reports(self):
        # fake an outbound multipart delivery first
        phone = FakeGammuPhone()
        msg = self.mk_msg()
        msg['content'] = 'a' * 200
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        # have the phone handle it
        received, sent = yield self.transport.receive_and_send_messages(phone)
        # test correct behaviour
        self.assertEqual([], received)
        # expand the sent messages, should be a vumi message and a dict of
        # MessageReference -> GammuMessage mappings
        [(vumi_msg, gammu_messages)] = sent
        self.assertEqual(vumi_msg, msg)
        # reference numbers given by modem, 200 chars should be split over
        # two SMSs
        self.assertEqual(gammu_messages.keys(), [1,2])

        # Now fake the delivery report for the given reference number
        phone = FakeGammuPhone([{
            'SMSCDateTime': datetime.now(),
            'Class': 0,
            'Text': u'Delivered',
            'Number': u'+27764493806',
            'DateTime': datetime.now(),
            'MessageReference': 1,
            'Length': 9,
            'Location': 0,
            'Type': 'Status_Report',
        }, {
            'SMSCDateTime': datetime.now(),
            'Class': 0,
            'Text': u'Delivered',
            'Number': u'+27764493806',
            'DateTime': datetime.now(),
            'MessageReference': 2,
            'Length': 9,
            'Location': 1,
            'Type': 'Status_Report',
        }])
        gammu_reports, _ = yield self.transport.receive_and_send_messages(phone)
        [gd_report1, gd_report2] = gammu_reports
        [delivery_report] = self.get_dispatched_events()
        self.assertEqual(delivery_report['delivery_status'], 'delivered')
        self.assertEqual(delivery_report['user_message_id'], msg['message_id'])
<commit_msg>skip tests for untested bits that still need fixing<commit_after># -*- coding: utf-8 -*-
from twisted.trial.unittest import SkipTest
from twisted.internet.defer import inlineCallbacks
from vumi.message import TransportUserMessage
from vumi.transports.gsm.tests.test_gsm_stubs import (
    FakeGSMTransport, FakeGammuPhone, FailingFakeGammuPhone)
from vumi.transports.tests.test_base import TransportTestCase
from vumi.transports.failures import FailureMessage
from datetime import datetime

class GSMTransportTestCase(TransportTestCase):

    transport_class = FakeGSMTransport
    transport_name = 'fake_gsm_transport'
    to_addr = '27761234567'

    @inlineCallbacks
    def setUp(self):
        yield super(GSMTransportTestCase, self).setUp()
        self.transport = yield self.get_transport({
            'transport_name': self.transport_name,
            'phone_number': self.to_addr,
            'country_code': 27,
            'poll_interval': 120,
            'gammu': {
                'UseGlobalDebugFile': 0,
                'DebugFile': '',
                'SyncTime': 0,
                'Connection': 'at',
                'LockDevice': 0,
                'DebugLevel': '',
                'Device': '/dev/path-to-modem',
                'StartInfo': 0,
                'Model': ''
            }
        })

    def tearDown(self):
        self.transport.r_server.teardown()

    def mk_msg(self, **kwargs):
        defaults = {
            'to_addr': '27761234567',
            'from_addr': '27761234567',
            'content': 'hello world',
            'transport_name': self.transport_name,
            'transport_type': 'sms',
            'transport_metadata': {},
        }
        defaults.update(kwargs)
        return TransportUserMessage(**defaults)

    @inlineCallbacks
    def test_receiving_sms(self):
        phone = FakeGammuPhone([{
            'Location': 0,
            'Text': 'hello world',
            'Number': '27761234567',
            'Type': 'Deliver',
            'DateTime': datetime.now(),
            'SMSCDateTime': datetime.now(),
        }])

        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual([], self.get_dispatched_failures())
        self.assertEqual([], self.get_dispatched_events())
        [msg] = self.get_dispatched_messages()
        self.assertEqual(msg['to_addr'], '+%s' % self.to_addr)
        self.assertEqual(msg['content'], 'hello world')
        self.assertEqual(phone.messages, [])

    @inlineCallbacks
    def test_sending_sms_success(self):
        phone = FakeGammuPhone()
        # generate a fake message
        msg = self.mk_msg()
        # dispatch as if being sent from an application
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        # the transport should store them in redis until the next
        # send & receive loop is triggered
        queue_name = self.transport.redis_outbound_queue
        key = self.transport.r_key(queue_name)
        self.assertEqual(self.transport.r_server.llen(key), 1)
        # trigger manually
        yield self.transport.receive_and_send_messages(phone)
        # all should be happy
        self.assertEqual(self.get_dispatched_failures(), [])
        # the phone's outbox should have the messages
        self.assertEqual(phone.outbox, [{
            'Type': 'Status_Report',
            'SMSC': {
                'Location': 1,
            },
            'Text': msg['content'],
            'Number': msg['to_addr'],
        }])
        # the redis list should be empty
        self.assertEqual(self.transport.r_server.llen(key), 0)

    @inlineCallbacks
    def test_sending_multipart_sms(self):
        phone = FakeGammuPhone()
        msg = self.mk_msg()
        msg['content'] = 'a' * 200  # doesn't fit in a single SMS
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual(len(phone.outbox), 2)
        self.assertEqual(phone.outbox[0]['Text'], 'a' * 153)
        self.assertEqual(phone.outbox[0]['UDH']['PartNumber'], 1)
        self.assertEqual(phone.outbox[0]['UDH']['AllParts'], 2)
        self.assertEqual(phone.outbox[1]['Text'], 'a' * 47)
        self.assertEqual(phone.outbox[1]['UDH']['PartNumber'], 2)
        self.assertEqual(phone.outbox[1]['UDH']['AllParts'], 2)

    @inlineCallbacks
    def test_receiving_multipart_sms(self):
        phone = FakeGammuPhone([{
            'MessageReference': u'9c103356fbf243b3805db555e4f9c2d3',
            'Number': u'27761234567',
            'DateTime': datetime.now(),
            'SMSCDateTime': datetime.now(),
            'Text': u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
                    u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
                    u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
                    u'aaaaaaaaaa',
            'Type': 'Deliver',
            'Location': 0,
            'UDH': {
                'AllParts': 2,
                'PartNumber': 1,
                'Text': '\x05\x00\x03\x19\x02\x01',
                'Type': 'ConcatenatedMessages'
            }
        }, {
            'MessageReference': u'9c103356fbf243b3805db555e4f9c2d3',
            'Number': u'27761234567',
            'DateTime': datetime.now(),
            'SMSCDateTime': datetime.now(),
            'Text': u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
            'Type': 'Deliver',
            'Location': 1,
            'UDH': {
                'AllParts': 2,
                'PartNumber': 2,
                'Text': '\x05\x00\x03\x19\x02\x02',
                'Type': 'ConcatenatedMessages'
            }
        }])
        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual([], self.get_dispatched_failures())
        self.assertEqual([], self.get_dispatched_events())
        [message] = self.get_dispatched_messages()
        self.assertEqual(message['content'], 'a' * 200)
        self.assertEqual(message['from_addr'], '+27761234567')

    @inlineCallbacks
    def test_sending_sms_failure(self):
        phone = FailingFakeGammuPhone()
        msg = self.mk_msg()
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        yield self.transport.receive_and_send_messages(phone)
        self.assertEqual([], self.get_dispatched_messages())
        self.assertEqual([], self.get_dispatched_events())
        [failure] = self.get_dispatched_failures()
        self.assertEqual(failure['message'], msg.payload)
        self.assertEqual(failure['message_type'],
                            FailureMessage.MESSAGE_TYPE)
        self.assertEqual(failure['failure_code'],
                            FailureMessage.FC_UNSPECIFIED)

    @inlineCallbacks
    def test_delivery_reports_success(self):
        # fake an outbound delivery first
        phone = FakeGammuPhone()
        msg = self.mk_msg()
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        # have the phone handle it
        received, sent = yield self.transport.receive_and_send_messages(phone)
        # test correct behaviour
        self.assertEqual([], received)
        # expand the sent messages, should be a vumi message and a dict of
        # MessageReference -> GammuMessage mappings
        [(vumi_msg, gammu_messages)] = sent
        self.assertEqual(vumi_msg, msg)
        self.assertEqual(gammu_messages.keys(), [1]) # reference number given by modem
        self.assertEqual(gammu_messages[1]['Text'], msg['content'])

        # Now fake the delivery report for the given reference number
        phone = FakeGammuPhone([{
            'SMSCDateTime': datetime.now(),
            'Class': 0,
            'Text': u'Delivered',
            'Number': u'+27764493806',
            'DateTime': datetime.now(),
            'MessageReference': 1,
            'Length': 9,
            'Location': 0,
            'Type': 'Status_Report',
        }])

        # received & sent
        [delivery_report], [] = yield self.transport.receive_and_send_messages(phone)
        self.assertEqual(delivery_report['Text'], 'Delivered')
        self.assertEqual(delivery_report['MessageReference'], 1)

        [delivery_report] = self.get_dispatched_events()
        self.assertEqual(delivery_report['delivery_status'], 'delivered')
        self.assertEqual(delivery_report['user_message_id'], msg['message_id'])

    # @inlineCallbacks
    def test_delivery_reports_fail(self):
        raise SkipTest('Not implemented yet as I don\'t know what Gammu failed messages are returned as')

    @inlineCallbacks
    def test_multipart_delivery_reports_success(self):
        # fake an outbound multipart delivery first
        phone = FakeGammuPhone()
        msg = self.mk_msg()
        msg['content'] = 'a' * 200
        yield self.dispatch(msg, rkey='%s.outbound' % self.transport_name)
        # have the phone handle it
        received, sent = yield self.transport.receive_and_send_messages(phone)
        # test correct behaviour
        self.assertEqual([], received)
        # expand the sent messages, should be a vumi message and a dict of
        # MessageReference -> GammuMessage mappings
        [(vumi_msg, gammu_messages)] = sent
        self.assertEqual(vumi_msg, msg)
        # reference numbers given by modem, 200 chars should be split over
        # two SMSs
        self.assertEqual(gammu_messages.keys(), [1,2])

        # Now fake the delivery report for the given reference numbers
        phone = FakeGammuPhone([{
            'SMSCDateTime': datetime.now(),
            'Class': 0,
            'Text': u'Delivered',
            'Number': msg['to_addr'],
            'DateTime': datetime.now(),
            'MessageReference': 1,
            'Length': 9,
            'Location': 0,
            'Type': 'Status_Report',
        }, {
            'SMSCDateTime': datetime.now(),
            'Class': 0,
            'Text': u'Delivered',
            'Number': msg['to_addr'],
            'DateTime': datetime.now(),
            'MessageReference': 2,
            'Length': 9,
            'Location': 1,
            'Type': 'Status_Report',
        }])
        gammu_reports, _ = yield self.transport.receive_and_send_messages(phone)
        [gd_report1, gd_report2] = gammu_reports
        [delivery_report] = self.get_dispatched_events()
        self.assertEqual(delivery_report['delivery_status'], 'delivered')
        self.assertEqual(delivery_report['user_message_id'], msg['message_id'])

    # @inlineCallbacks
    def test_multipart_delivery_reports_fail(self):
        raise SkipTest('Not implemented yet as I don\'t know what Gammu failed messages are returned as')
<|endoftext|>