<commit_before># -*- coding: utf-8 -*-

# Focus
# Copyright (C) 2010-2012 Grid Dynamics Consulting Services, Inc
# All Rights Reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program. If not, see
# <http://www.gnu.org/licenses/>.


"""Provides factory of blueprints for image management.

Currently we have only one blueprint for image management - for admins.
Later on we'll need image management for members of tenants, Essex allows it.
"""

import datetime
import json
import os
import time

import flask
from flask import blueprints
from flaskext import principal

import focus
from focus import clients
from focus import utils
from focus.views import environments
from focus.views import forms
from focus.views import pagination


class ProgressRecorder(object):
    """
    This is used to measure progress of a process.

    Instance works as a callback for a process to call.
    It records progress and ETA to application cache under given key.
    """
    def __init__(self, http_client, key, total):
        self.http_client = http_client
        self.key = key
        self.total = total

    def __enter__(self):
        self.start = time.time()
        self.counter = 0
        self.http_client.callback = self
        return self

    def __exit__(self, type, value, traceback):
        flask.current_app.cache.delete(self.key)
        self.http_client.callback = None

    def __call__(self, progress):
        self.counter += progress
        now = time.time()
        seconds_spent = now - self.start
        percent = self.counter * 100.0 / self.total
        if seconds_spent:
            speed = self.counter / seconds_spent
            remainder = self.total - self.counter
            eta = now + remainder / speed
            eta = (datetime.datetime.utcfromtimestamp(eta).
                   isoformat().split('.')[0].replace('T', ' '))
        else:
            speed = 0.0
            eta = u'∞'
            seconds_spent = 0.0

        flask.current_app.cache.set(
            self.key, {
                'transferred': self.counter,
                'total': self.total,
                'time_spent': '%0.2f' % seconds_spent,
                'percent': percent,
                'speed': '%0.2f' % speed,
                'eta': eta,
            })


def get_images_list():
    """Return list of images visible for given condigions.

    If g.tenant_id is not set it is admin blueprint.
    We have to show only images owned by systenant, which are also
    public (can be protected as well (we set it so, but other tools can
    change this attribute or set it to a wrong value from the beginning).

    If g.tenant_id is set it is project blueprint.
    We have to show the same images as for admin blueprint _and_
    images owned by current project (attribute "owner" must be equal to
    g.tenant_id) no matter if image is public/protected.
    NOTE(apugachev):
    Currently for some reason Glance does not return list of images owned
    by tenant with id '1' even if they are public - if they are requested
    through token issued for some other project then '1'.

    That's why we combine image lists here in case if list is for project.
    """
    admin_id = clients.get_systenant_id()
    is_global = lambda x: x.owner == admin_id and x.is_public
    result = filter(
        is_global,
        clients.admin_clients().glance.images.list())
    if getattr(flask.g, 'tenant_id', None):
        result.extend(filter(
            lambda x: x.owner == flask.g.tenant_id and x not in result,
            clients.user_clients(flask.g.tenant_id).glance.images.list()))
    result = sorted(result, key=lambda x: x.name)
    return result


@focus.app.route('/fast-upload-response/', methods=['POST'])
def fast_upload_response():
    """Respond to request from nginx-upload.

    Return path temp location.
    """
    src = os.path.abspath(flask.request.form['file.path'])
    if src.startswith(flask.current_app.config['UPLOADS_DEFAULT_DEST']):
        return flask.make_response(os.path.basename(src))
    else:
        flask.current_app.logger.error('Tried to trigger upload finish for %s',
                                       str(flask.request.form))
        return flask.jsonify({
                'status': 'error',
                'message': 'Invalid path of upload.'
                })


def get_bp(name):
    """Return blueprint for Image management."""
    bp = blueprints.Blueprint(name, __name__)

    @bp.route('<image_id>/', methods=['GET'])
    def show(image_id):
        """Present details page for single image object.

        Tries using both Glance and Nova services because they provide
        different info set about an image.
        """
        glance_image = clients.admin_clients().glance.images.get(image_id)
        nova_image = clients.admin_clients().nova.images.get(image_id)
        return {'glance_image': glance_image,
                'nova_image': nova_image}

    def get_tenant_id():
        return getattr(flask.g, 'tenant_id', clients.get_systenant_id())

    @bp.route('')
    def index():
        """List images.

        Admin (global visibility level) should see only images from
        systenant. Project members should see images for projects only.
        """
        images = get_images_list()
        if not hasattr(flask.g, 'tenant_id'):
            images = filter(
                lambda x: getattr(x, 'is_public', False),
                images)
        p = pagination.Pagination(images)
        data = p.slice(images)
        return {
            'images': data,
            'pagination': p,
            'delete_form': forms.DeleteForm()
        }

    @bp.route('new/', methods=['GET'])
    def new():
        """Present image upload form.

        TODO(apugachev): remove from templ location images older then X hours
        """
        images = get_images_list()
        check = lambda f: lambda x: getattr(x, 'container_format') == f
        kernels = filter(check('aki'), images)
        initrds = filter(check('ari'), images)
        dump = lambda d: json.dumps([x.properties for x in d])
        return {
            'kernel_list': dump(kernels),
            'initrd_list': dump(initrds)
        }

    @bp.route('upload/', methods=['POST'])
    def upload():
        """Save uploaded file.

        Handles AJAX call, saves file in temp location, returns filename.

        TODO(apugachev): remove from templ location images older then X hours
        """
        storage = flask.request.files['file']
        filename = focus.files_uploads.save(storage)
        flask.current_app.cache.set(<commit_msg>image uploadit for existing initrd and kernel fix<commit_after># -*- coding: utf-8 -*-

# Focus
# Copyright (C) 2010-2012 Grid Dynamics Consulting Services, Inc
# All Rights Reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program. If not, see
# <http://www.gnu.org/licenses/>.


"""Provides factory of blueprints for image management.

Currently we have only one blueprint for image management - for admins.
Later on we'll need image management for members of tenants, Essex allows it.
"""

import datetime
import json
import os
import time

import flask
from flask import blueprints
from flaskext import principal

import focus
from focus import clients
from focus import utils
from focus.views import environments
from focus.views import forms
from focus.views import pagination


class ProgressRecorder(object):
    """
    This is used to measure progress of a process.

    Instance works as a callback for a process to call.
    It records progress and ETA to application cache under given key.
    """
    def __init__(self, http_client, key, total):
        self.http_client = http_client
        self.key = key
        self.total = total

    def __enter__(self):
        self.start = time.time()
        self.counter = 0
        self.http_client.callback = self
        return self

    def __exit__(self, type, value, traceback):
        flask.current_app.cache.delete(self.key)
        self.http_client.callback = None

    def __call__(self, progress):
        self.counter += progress
        now = time.time()
        seconds_spent = now - self.start
        percent = self.counter * 100.0 / self.total
        if seconds_spent:
            speed = self.counter / seconds_spent
            remainder = self.total - self.counter
            eta = now + remainder / speed
            eta = (datetime.datetime.utcfromtimestamp(eta).
                   isoformat().split('.')[0].replace('T', ' '))
        else:
            speed = 0.0
            eta = u'∞'
            seconds_spent = 0.0

        flask.current_app.cache.set(
            self.key, {
                'transferred': self.counter,
                'total': self.total,
                'time_spent': '%0.2f' % seconds_spent,
                'percent': percent,
                'speed': '%0.2f' % speed,
                'eta': eta,
            })


def get_images_list():
    """Return list of images visible for given condigions.

    If g.tenant_id is not set it is admin blueprint.
    We have to show only images owned by systenant, which are also
    public (can be protected as well (we set it so, but other tools can
    change this attribute or set it to a wrong value from the beginning).

    If g.tenant_id is set it is project blueprint.
    We have to show the same images as for admin blueprint _and_
    images owned by current project (attribute "owner" must be equal to
    g.tenant_id) no matter if image is public/protected.
    NOTE(apugachev):
    Currently for some reason Glance does not return list of images owned
    by tenant with id '1' even if they are public - if they are requested
    through token issued for some other project then '1'.

    That's why we combine image lists here in case if list is for project.
    """
    admin_id = clients.get_systenant_id()
    is_global = lambda x: x.owner == admin_id and x.is_public
    result = filter(
        is_global,
        clients.admin_clients().glance.images.list())
    if getattr(flask.g, 'tenant_id', None):
        result.extend(filter(
            lambda x: x.owner == flask.g.tenant_id and x not in result,
            clients.user_clients(flask.g.tenant_id).glance.images.list()))
    result = sorted(result, key=lambda x: x.name)
    return result


@focus.app.route('/fast-upload-response/', methods=['POST'])
def fast_upload_response():
    """Respond to request from nginx-upload.

    Return path temp location.
    """
    src = os.path.abspath(flask.request.form['file.path'])
    if src.startswith(flask.current_app.config['UPLOADS_DEFAULT_DEST']):
        return flask.make_response(os.path.basename(src))
    else:
        flask.current_app.logger.error('Tried to trigger upload finish for %s',
                                       str(flask.request.form))
        return flask.jsonify({
                'status': 'error',
                'message': 'Invalid path of upload.'
                })


def get_bp(name):
    """Return blueprint for Image management."""
    bp = blueprints.Blueprint(name, __name__)

    @bp.route('<image_id>/', methods=['GET'])
    def show(image_id):
        """Present details page for single image object.

        Tries using both Glance and Nova services because they provide
        different info set about an image.
        """
        glance_image = clients.admin_clients().glance.images.get(image_id)
        nova_image = clients.admin_clients().nova.images.get(image_id)
        return {'glance_image': glance_image,
                'nova_image': nova_image}

    def get_tenant_id():
        return getattr(flask.g, 'tenant_id', clients.get_systenant_id())

    @bp.route('')
    def index():
        """List images.

        Admin (global visibility level) should see only images from
        systenant. Project members should see images for projects only.
        """
        images = get_images_list()
        if not hasattr(flask.g, 'tenant_id'):
            images = filter(
                lambda x: getattr(x, 'is_public', False),
                images)
        p = pagination.Pagination(images)
        data = p.slice(images)
        return {
            'images': data,
            'pagination': p,
            'delete_form': forms.DeleteForm()
        }

    @bp.route('new/', methods=['GET'])
    def new():
        """Present image upload form.

        TODO(apugachev): remove from templ location images older then X hours
        """
        images = get_images_list()
        check = lambda f: lambda x: getattr(x, 'container_format') == f
        kernels = filter(check('aki'), images)
        initrds = filter(check('ari'), images)
        dump = lambda d: json.dumps([{'name': x.name, 
                                      'container_format' :x.container_format, 
                                      'id': x.id} for x in d])
        return {
            'kernel_list': dump(kernels),
            'initrd_list': dump(initrds)
        }

    @bp.route('upload/', methods=['POST'])
    def upload():
        """Save uploaded file.

        Handles AJAX call, saves file in temp location, returns filename.

        TODO(apugachev): remove from templ location images older then X hours
        """
        storage = flask.request.files['file']
        filename = focus.files_uploads.save(storage)
        flask.current_app.cache.set(
{'fraction_non_alphanumeric': 0.09589135530125466, 'fraction_numerical': 0.0037225975458430993, 'mean_word_length': 2.3966284242566145, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4916455', 'n_tokens_mistral': 4111, 'n_tokens_neox': 3999, 'n_words': 1407}