<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* 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.                                             */
/* -------------------------------------------------------------------------- */

#include "ImageManager.h"
#include "NebulaLog.h"
#include "ImagePool.h"
#include "SSLTools.h"
#include "SyncRequest.h"
#include "Template.h"
#include "Nebula.h"


/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

Image * ImageManager::acquire_image(int vm_id, int image_id, string& error)
{
    Image * img;
    int     rc;

    img = ipool->get(image_id,true);

    if ( img == 0 )
    {
        ostringstream oss;
        oss << "Image with ID: " << image_id  << " does not exists";

        error = oss.str();
        return 0;
    }

    rc = acquire_image(vm_id, img, error);

    if ( rc != 0 )
    {
        img->unlock();
        img = 0;
    }

    return img;
}

/* -------------------------------------------------------------------------- */

Image * ImageManager::acquire_image(int vm_id, const string& name, int uid, string& error)
{
    Image * img;
    int     rc;

    img = ipool->get(name,uid,true);

    if ( img == 0 )
    {
        ostringstream oss;
        oss << "Image " << name << " does not exists for user " << uid;

        error = oss.str();
        return 0;
    }

    rc = acquire_image(vm_id, img, error);

    if ( rc != 0 )
    {
        img->unlock();
        img = 0;
    }

    return img;
}

/* -------------------------------------------------------------------------- */

int ImageManager::acquire_image(int vm_id, Image *img, string& error)
{
    int rc = 0;

    switch (img->get_state())
    {
        case Image::READY:
            img->inc_running(vm_id);

            if ( img->isPersistent() )
            {
                img->set_state(Image::USED_PERS);
            }
            else
            {
                img->set_state(Image::USED);
            }

            ipool->update(img);
        break;

        case Image::USED_PERS:
            error = "Cannot acquire persistent image, it is already in use";
            rc    = -1;
        break;

        case Image::USED:
            img->inc_running(vm_id);
            ipool->update(img);
        break;

        case Image::INIT:
        case Image::DISABLED:
        case Image::LOCKED:
        case Image::ERROR:
        case Image::DELETE:
        case Image::CLONE:
           ostringstream oss;
           oss << "Cannot acquire image in state: "
               << Image::state_to_str(img->get_state());

           error = oss.str();
           rc    = -1;
        break;
    }

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void ImageManager::release_image(int vm_id, int iid, bool failed)
{
    Image * img;

    ostringstream disk_file;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return;
    }

    switch (img->get_state())
    {
        case Image::USED_PERS:
            img->dec_running(vm_id);

            if (failed == true)
            {
                img->set_state(Image::ERROR);
            }
            else
            {
                img->set_state(Image::READY);
            }

            ipool->update(img);

            img->unlock();
        break;

        case Image::USED:
            if ( img->dec_running(vm_id) == 0  && img->get_cloning() == 0 )
            {
                img->set_state(Image::READY);
            }

            ipool->update(img);

            img->unlock();
        break;

        case Image::LOCKED:
            if ( img->isSaving() ) //SAVE_AS images are LOCKED till released
            {
                if (failed == true)
                {
                    img->set_state(Image::ERROR);
                }
                else
                {
                    img->set_state(Image::READY);
                }

                ipool->update(img);
            }
            else
            {
                stringstream oss;
                oss << "Releasing image in wrong state: "
                    << Image::state_to_str(img->get_state());

                NebulaLog::log("ImM", Log::ERROR, oss.str());
            }

            img->unlock();
        break;

        case Image::CLONE:
        case Image::DELETE:
        case Image::INIT:
        case Image::DISABLED:
        case Image::READY:
        case Image::ERROR:
           ostringstream oss;
           oss << "Releasing image in wrong state: "
               << Image::state_to_str(img->get_state());

           NebulaLog::log("ImM", Log::ERROR, oss.str());

           img->unlock();
        break;
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void ImageManager::release_cloning_image(int iid, int clone_img_id)
{
    Image * img;

    ostringstream disk_file;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return;
    }

    switch (img->get_state())
    {
        case Image::USED:
        case Image::CLONE:

            if (img->dec_cloning(clone_img_id) == 0  && img->get_running() == 0)
            {
                img->set_state(Image::READY);
            }

            ipool->update(img);

        break;

        case Image::DELETE:
        case Image::INIT:
        case Image::DISABLED:
        case Image::READY:
        case Image::ERROR:
        case Image::USED_PERS:
        case Image::LOCKED:

           ostringstream oss;
           oss << "Releasing image in wrong state: "
               << Image::state_to_str(img->get_state());

           NebulaLog::log("ImM", Log::ERROR, oss.str());

        break;
    }

    img->unlock();
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::enable_image(int iid, bool to_enable)
{
    int rc = 0;
    Image * img;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return -1;
    }

    if ( to_enable == true )
    {
        switch (img->get_state())
        {
            case Image::DISABLED:
            case Image::ERROR:
                img->set_state(Image::READY);
                ipool->update(img);
            case Image::READY:
            break;
            default:
                rc = -1;
            break;
        }
    }
    else
    {
        switch (img->get_state())
        {
            case Image::READY:
            case Image::ERROR:
                img->set_state(Image::DISABLED);
                ipool->update(img);
            case Image::DISABLED:
            break;
            default:
                rc = -1;
            break;
        }
    }

    img->unlock();

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::delete_image(int iid, const string& ds_data)
{
    Image * img;

    string   source;
    string   img_tmpl;
    string * drv_msg;

    int size;
    int ds_id;

    int uid;
    int gid;
    int cloning_id = -1;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return -1;
    }

    switch(img->get_state())
    {
        case Image::READY:
            if ( img->get_running() != 0 )
            {
                img->unlock();
                return -1; //Cannot remove images in use
            }
        break;

        case Image::USED:
        case Image::USED_PERS:
        case Image::CLONE:
            img->unlock();
            return -1; //Cannot remove images in use
        break;

        case Image::INIT:
        case Image::DISABLED:
        case Image::ERROR:
        case Image::DELETE:
        break;

        case Image::LOCKED:
            cloning_id = img->get_cloning_id();
        break;

    }

    /* ------------------- Send RM operation request to the DS -------------- */

    const ImageManagerDriver* imd = get();

    if ( imd == 0 )
    {
        img->unlock();
        return -1;
    }

    drv_msg = format_message(img->to_xml(img_tmpl), ds_data);
    source  = img->get_source();
    size    = img->get_size();
    ds_id   = img->get_ds_id();
    uid     = img->get_uid();
    gid     = img->get_gid();

    if (source.empty())
    {
        string err_str;
        int    rc;

        rc = ipool->drop(img, err_str);

        if ( rc < 0 )
        {
            NebulaLog::log("ImM",Log::ERROR,
                "Image could not be removed from DB");
        }
    }
    else
    {
        imd->rm(img->get_oid(), *drv_msg);
        img->set_state(Image::DELETE);

        img->clear_cloning_id();

        ipool->update(img);
    }

    img->unlock();

    delete drv_msg;

    /* -------------------- Update Group & User quota counters -------------- */

    Template img_usage;

    img_usage.add("DATASTORE", ds_id);
    img_usage.add("SIZE", size);

    Quotas::ds_del(uid, gid, &img_usage);

    /* --------------- Update cloning image if needed ----------------------- */

    if ( cloning_id != -1 )
    {
        release_cloning_image(cloning_id, iid);
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::can_clone_image(  int             cloning_id,
                                    ostringstream&  oss_error)
{
    Image *       img;

    img = ipool->get(cloning_id, true);

    if (img == 0)
    {
        oss_error << "Cannot clone image, it does not exist";
        return -1;
    }

    Image::ImageState state = img->get_state();

    img->unlock();

    switch(state)
    {
        case Image::USED_PERS:
        case Image::CLONE:
        case Image::INIT:
        case Image::DISABLED:
        case Image::ERROR:
        case Image::DELETE:
        case Image::LOCKED:
            oss_error << "Cannot clone image in state: "
                << Image::state_to_str(state);

            return -1;
        break;

        case Image::READY:
        case Image::USED:
        default:
            return 0;
        break;
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::clone_image(int   new_id,
                              int   cloning_id,
                              const string& ds_data,
                              string& error)
{
    const ImageManagerDriver* imd = get();

    ostringstream oss;
    Image *       img;

    string  path;
    string  img_tmpl;
    string* drv_msg;

    if ( imd == 0 )
    {
        error = "Could not get datastore driver";

        NebulaLog::log("ImM", Log::ERROR, error);
        return -1;
    }

    img = ipool->get(cloning_id, true);

    if (img == 0)
    {
        error = "Cannot clone image, it does not exist";
        return -1;
    }

    switch(img->get_state())
    {
        case Image::READY:
            img->inc_cloning(new_id);

            if (img->isPersistent())
            {
                img->set_state(Image::CLONE);
            }
            else
            {
                img->set_state(Image::USED);
            }

            ipool->update(img);

            img->unlock();
        break;

        case Image::USED:
            img->inc_cloning(new_id);

            ipool->update(img);

            img->unlock();
        break;

        case Image::USED_PERS:
        case Image::CLONE:
        case Image::INIT:
        case Image::DISABLED:
        case Image::ERROR:
        case Image::DELETE:
        case Image::LOCKED:
            oss << "Cannot clone image in state: "
                << Image::state_to_str(img->get_state());<commit_msg>Bug #1357: Allow more than one simultaneous clone operation for persistent images<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* 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.                                             */
/* -------------------------------------------------------------------------- */

#include "ImageManager.h"
#include "NebulaLog.h"
#include "ImagePool.h"
#include "SSLTools.h"
#include "SyncRequest.h"
#include "Template.h"
#include "Nebula.h"


/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

Image * ImageManager::acquire_image(int vm_id, int image_id, string& error)
{
    Image * img;
    int     rc;

    img = ipool->get(image_id,true);

    if ( img == 0 )
    {
        ostringstream oss;
        oss << "Image with ID: " << image_id  << " does not exists";

        error = oss.str();
        return 0;
    }

    rc = acquire_image(vm_id, img, error);

    if ( rc != 0 )
    {
        img->unlock();
        img = 0;
    }

    return img;
}

/* -------------------------------------------------------------------------- */

Image * ImageManager::acquire_image(int vm_id, const string& name, int uid, string& error)
{
    Image * img;
    int     rc;

    img = ipool->get(name,uid,true);

    if ( img == 0 )
    {
        ostringstream oss;
        oss << "Image " << name << " does not exists for user " << uid;

        error = oss.str();
        return 0;
    }

    rc = acquire_image(vm_id, img, error);

    if ( rc != 0 )
    {
        img->unlock();
        img = 0;
    }

    return img;
}

/* -------------------------------------------------------------------------- */

int ImageManager::acquire_image(int vm_id, Image *img, string& error)
{
    int rc = 0;

    switch (img->get_state())
    {
        case Image::READY:
            img->inc_running(vm_id);

            if ( img->isPersistent() )
            {
                img->set_state(Image::USED_PERS);
            }
            else
            {
                img->set_state(Image::USED);
            }

            ipool->update(img);
        break;

        case Image::USED_PERS:
            error = "Cannot acquire persistent image, it is already in use";
            rc    = -1;
        break;

        case Image::USED:
            img->inc_running(vm_id);
            ipool->update(img);
        break;

        case Image::INIT:
        case Image::DISABLED:
        case Image::LOCKED:
        case Image::ERROR:
        case Image::DELETE:
        case Image::CLONE:
           ostringstream oss;
           oss << "Cannot acquire image in state: "
               << Image::state_to_str(img->get_state());

           error = oss.str();
           rc    = -1;
        break;
    }

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void ImageManager::release_image(int vm_id, int iid, bool failed)
{
    Image * img;

    ostringstream disk_file;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return;
    }

    switch (img->get_state())
    {
        case Image::USED_PERS:
            img->dec_running(vm_id);

            if (failed == true)
            {
                img->set_state(Image::ERROR);
            }
            else
            {
                img->set_state(Image::READY);
            }

            ipool->update(img);

            img->unlock();
        break;

        case Image::USED:
            if ( img->dec_running(vm_id) == 0  && img->get_cloning() == 0 )
            {
                img->set_state(Image::READY);
            }

            ipool->update(img);

            img->unlock();
        break;

        case Image::LOCKED:
            if ( img->isSaving() ) //SAVE_AS images are LOCKED till released
            {
                if (failed == true)
                {
                    img->set_state(Image::ERROR);
                }
                else
                {
                    img->set_state(Image::READY);
                }

                ipool->update(img);
            }
            else
            {
                stringstream oss;
                oss << "Releasing image in wrong state: "
                    << Image::state_to_str(img->get_state());

                NebulaLog::log("ImM", Log::ERROR, oss.str());
            }

            img->unlock();
        break;

        case Image::CLONE:
        case Image::DELETE:
        case Image::INIT:
        case Image::DISABLED:
        case Image::READY:
        case Image::ERROR:
           ostringstream oss;
           oss << "Releasing image in wrong state: "
               << Image::state_to_str(img->get_state());

           NebulaLog::log("ImM", Log::ERROR, oss.str());

           img->unlock();
        break;
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void ImageManager::release_cloning_image(int iid, int clone_img_id)
{
    Image * img;

    ostringstream disk_file;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return;
    }

    switch (img->get_state())
    {
        case Image::USED:
        case Image::CLONE:

            if (img->dec_cloning(clone_img_id) == 0  && img->get_running() == 0)
            {
                img->set_state(Image::READY);
            }

            ipool->update(img);

        break;

        case Image::DELETE:
        case Image::INIT:
        case Image::DISABLED:
        case Image::READY:
        case Image::ERROR:
        case Image::USED_PERS:
        case Image::LOCKED:

           ostringstream oss;
           oss << "Releasing image in wrong state: "
               << Image::state_to_str(img->get_state());

           NebulaLog::log("ImM", Log::ERROR, oss.str());

        break;
    }

    img->unlock();
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::enable_image(int iid, bool to_enable)
{
    int rc = 0;
    Image * img;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return -1;
    }

    if ( to_enable == true )
    {
        switch (img->get_state())
        {
            case Image::DISABLED:
            case Image::ERROR:
                img->set_state(Image::READY);
                ipool->update(img);
            case Image::READY:
            break;
            default:
                rc = -1;
            break;
        }
    }
    else
    {
        switch (img->get_state())
        {
            case Image::READY:
            case Image::ERROR:
                img->set_state(Image::DISABLED);
                ipool->update(img);
            case Image::DISABLED:
            break;
            default:
                rc = -1;
            break;
        }
    }

    img->unlock();

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::delete_image(int iid, const string& ds_data)
{
    Image * img;

    string   source;
    string   img_tmpl;
    string * drv_msg;

    int size;
    int ds_id;

    int uid;
    int gid;
    int cloning_id = -1;

    img = ipool->get(iid,true);

    if ( img == 0 )
    {
        return -1;
    }

    switch(img->get_state())
    {
        case Image::READY:
            if ( img->get_running() != 0 )
            {
                img->unlock();
                return -1; //Cannot remove images in use
            }
        break;

        case Image::USED:
        case Image::USED_PERS:
        case Image::CLONE:
            img->unlock();
            return -1; //Cannot remove images in use
        break;

        case Image::INIT:
        case Image::DISABLED:
        case Image::ERROR:
        case Image::DELETE:
        break;

        case Image::LOCKED:
            cloning_id = img->get_cloning_id();
        break;

    }

    /* ------------------- Send RM operation request to the DS -------------- */

    const ImageManagerDriver* imd = get();

    if ( imd == 0 )
    {
        img->unlock();
        return -1;
    }

    drv_msg = format_message(img->to_xml(img_tmpl), ds_data);
    source  = img->get_source();
    size    = img->get_size();
    ds_id   = img->get_ds_id();
    uid     = img->get_uid();
    gid     = img->get_gid();

    if (source.empty())
    {
        string err_str;
        int    rc;

        rc = ipool->drop(img, err_str);

        if ( rc < 0 )
        {
            NebulaLog::log("ImM",Log::ERROR,
                "Image could not be removed from DB");
        }
    }
    else
    {
        imd->rm(img->get_oid(), *drv_msg);
        img->set_state(Image::DELETE);

        img->clear_cloning_id();

        ipool->update(img);
    }

    img->unlock();

    delete drv_msg;

    /* -------------------- Update Group & User quota counters -------------- */

    Template img_usage;

    img_usage.add("DATASTORE", ds_id);
    img_usage.add("SIZE", size);

    Quotas::ds_del(uid, gid, &img_usage);

    /* --------------- Update cloning image if needed ----------------------- */

    if ( cloning_id != -1 )
    {
        release_cloning_image(cloning_id, iid);
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::can_clone_image(  int             cloning_id,
                                    ostringstream&  oss_error)
{
    Image *       img;

    img = ipool->get(cloning_id, true);

    if (img == 0)
    {
        oss_error << "Cannot clone image, it does not exist";
        return -1;
    }

    Image::ImageState state = img->get_state();

    img->unlock();

    switch(state)
    {
        case Image::USED_PERS:
        case Image::INIT:
        case Image::DISABLED:
        case Image::ERROR:
        case Image::DELETE:
        case Image::LOCKED:
            oss_error << "Cannot clone image in state: "
                << Image::state_to_str(state);

            return -1;
        break;

        case Image::CLONE:
        case Image::READY:
        case Image::USED:
        default:
            return 0;
        break;
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int ImageManager::clone_image(int   new_id,
                              int   cloning_id,
                              const string& ds_data,
                              string& error)
{
    const ImageManagerDriver* imd = get();

    ostringstream oss;
    Image *       img;

    string  path;
    string  img_tmpl;
    string* drv_msg;

    if ( imd == 0 )
    {
        error = "Could not get datastore driver";

        NebulaLog::log("ImM", Log::ERROR, error);
        return -1;
    }

    img = ipool->get(cloning_id, true);

    if (img == 0)
    {
        error = "Cannot clone image, it does not exist";
        return -1;
    }

    switch(img->get_state())
    {
        case Image::READY:
            img->inc_cloning(new_id);

            if (img->isPersistent())
            {
                img->set_state(Image::CLONE);
            }
            else
            {
                img->set_state(Image::USED);
            }

            ipool->update(img);

            img->unlock();
        break;

        case Image::USED:
        case Image::CLONE:
            img->inc_cloning(new_id);

            ipool->update(img);

            img->unlock();
        break;

        case Image::USED_PERS:
        case Image::INIT:
        case Image::DISABLED:
        case Image::ERROR:
        case Image::DELETE:
        case Image::LOCKED:
            oss << "Cannot clone image in state: "
                << Image::state_to_str(img->get_state());
{'fraction_non_alphanumeric': 0.2214209807195992, 'fraction_numerical': 0.004174889934719903, 'mean_word_length': 1.040343812916215, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 171, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '500683', 'n_tokens_mistral': 7261, 'n_tokens_neox': 6878, 'n_words': 1592}