From 1dbeaa171941db1e53c0ad5e638e417c78a5c417 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 15 Jun 2012 12:28:20 +0200 Subject: [PATCH 1/4] feature #1233: Added quotas.Refactor Attach methods --- include/DispatchManager.h | 18 +- include/Quotas.h | 135 +++---------- include/RequestManagerVirtualMachine.h | 11 +- include/Template.h | 9 + include/VirtualMachine.h | 56 ++++-- src/dm/DispatchManagerActions.cc | 145 +++++++------- src/dm/DispatchManagerStates.cc | 37 +--- src/image/ImageManagerActions.cc | 36 +--- src/lcm/LifeCycleStates.cc | 21 +- src/rm/RequestManagerVirtualMachine.cc | 83 ++++---- src/template/Template.cc | 27 +++ src/um/Quotas.cc | 222 +++++++++++++++++++++ src/um/SConstruct | 3 +- src/vm/VirtualMachine.cc | 261 +++++++++---------------- 14 files changed, 571 insertions(+), 493 deletions(-) create mode 100644 src/um/Quotas.cc diff --git a/include/DispatchManager.h b/include/DispatchManager.h index 48a9c1e1a3..d1f8bc3df5 100644 --- a/include/DispatchManager.h +++ b/include/DispatchManager.h @@ -232,6 +232,7 @@ public: * Set the re-scheduling flag for the VM (must be in RUNNING state) * @param vid VirtualMachine identification * @param do_resched set or unset the flag + * * @return 0 on success, -1 if the VM does not exits or -2 if the VM is * in a wrong a state */ @@ -240,19 +241,14 @@ public: bool do_resched); /** - * Starts the attach disk action. + * Starts the attach disk action. + * @param vid VirtualMachine identification + * @param tmpl Template containing the new DISK attribute. + * @param error_str Error reason, if any * - * @param vm pointer to a VirtualMachine with its mutex locked. It will be - * unlocked - * @param tmpl Template containing the new DISK attribute. - * It will be deleted - * @param error_str Error reason, if any - * @return 0 on success, -1 action error, -2 if the VM is in a wrong a state + * @return 0 on success, -1 otherwise */ - int attach( - VirtualMachine * vm, - VirtualMachineTemplate * tmpl, - string & error_str); + int attach(int vid, VirtualMachineTemplate * tmpl, string & error_str); private: /** diff --git a/include/Quotas.h b/include/Quotas.h index d6767668d1..5c9daf17d9 100644 --- a/include/Quotas.h +++ b/include/Quotas.h @@ -22,6 +22,8 @@ #include "QuotaVirtualMachine.h" #include "QuotaImage.h" +class ObjectXML; + class Quotas { public: @@ -44,52 +46,7 @@ public: * * @return 0 on success, -1 otherwise */ - int set(Template *tmpl, string& error) - { - vector vquotas; - - if ( tmpl->get(datastore_quota.get_quota_name(), vquotas) > 0 ) - { - if ( datastore_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - if ( tmpl->get(network_quota.get_quota_name(), vquotas) > 0 ) - { - if ( network_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - if ( tmpl->get(image_quota.get_quota_name(), vquotas) > 0 ) - { - if ( image_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - if ( tmpl->get(vm_quota.get_quota_name(), vquotas) > 0 ) - { - if ( vm_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - return 0; - } + int set(Template *tmpl, string& error); /** * Check Datastore quotas, it updates usage counters if quotas are not @@ -159,76 +116,32 @@ public: * @param xml the string to store the XML * @return the same xml string to use it in << compounds */ - string& to_xml(string& xml) const - { - ostringstream oss; - - string ds_quota_xml; - string net_quota_xml; - string vm_quota_xml; - string image_quota_xml; - - oss << datastore_quota.to_xml(ds_quota_xml) - << network_quota.to_xml(net_quota_xml) - << vm_quota.to_xml(vm_quota_xml) - << image_quota.to_xml(image_quota_xml); - - xml = oss.str(); - - return xml; - } + string& to_xml(string& xml) const; /** * Builds quota object from an ObjectXML * @param object_xml pointer to the ObjectXML * @return 0 if success */ - int from_xml(ObjectXML * object_xml) - { - vector content; - int rc = 0; + int from_xml(ObjectXML * object_xml); - object_xml->get_nodes(ds_xpath, content); + /** + * Delete VM related usage (network, image and compute) from quota counters. + * for the given user and group + * @param uid of the user + * @param gid of the group + * @param tmpl template for the image, with usage + */ + static void vm_del(int uid, int gid, Template * tmpl); - if (!content.empty()) - { - rc += datastore_quota.from_xml_node(content[0]); - } - - object_xml->free_nodes(content); - content.clear(); - - object_xml->get_nodes(net_xpath, content); - - if (!content.empty()) - { - rc += network_quota.from_xml_node(content[0]); - } - - object_xml->free_nodes(content); - content.clear(); - - object_xml->get_nodes(vm_xpath, content); - - if (!content.empty()) - { - rc += vm_quota.from_xml_node(content[0]); - } - - object_xml->free_nodes(content); - content.clear(); - - object_xml->get_nodes(img_xpath, content); - - if (!content.empty()) - { - rc += image_quota.from_xml_node(content[0]); - } - - object_xml->free_nodes(content); - - return rc; - } + /** + * Delete Datastore related usage from quota counters. + * for the given user and group + * @param uid of the user + * @param gid of the group + * @param tmpl template for the image, with usage + */ + static void ds_del(int uid, int gid, Template * tmpl); private: //-------------------------------------------------------------------------- @@ -238,17 +151,17 @@ private: /** * Datastore Quotas */ - QuotaDatastore datastore_quota; + QuotaDatastore datastore_quota; /** * Network Quotas */ - QuotaNetwork network_quota; + QuotaNetwork network_quota; /** * Image Quotas */ - QuotaImage image_quota; + QuotaImage image_quota; /** * Virtual Machine Quotas diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 05ac3caccb..3bd73b53e0 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -49,11 +49,12 @@ protected: RequestAttributes& att) = 0; bool vm_authorization(int id, - ImageTemplate * tmpl, - RequestAttributes& att, - PoolObjectAuth * host_perms, - PoolObjectAuth * ds_perm, - AuthRequest::Operation op); + ImageTemplate * tmpl, + VirtualMachineTemplate* vtmpl, + RequestAttributes& att, + PoolObjectAuth * host_perms, + PoolObjectAuth * ds_perm, + AuthRequest::Operation op); int get_host_information(int hid, string& name, string& vmm, string& vnm, RequestAttributes& att, PoolObjectAuth& host_perms); diff --git a/include/Template.h b/include/Template.h index 0b4f061f58..dc9b79946d 100644 --- a/include/Template.h +++ b/include/Template.h @@ -169,6 +169,15 @@ public: const string& name, vector& values); + /** + * Removes an attribute from the template, but it DOES NOT free the + * attribute. + * @param att Attribute to remove. It will be deleted + * @return pointer to the removed attribute or 0 if non attribute was + * removed + */ + virtual Attribute * remove(Attribute * att); + /** * Removes an attribute from the template, and frees the attributes. * @param name of the attribute diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 07a77a5405..632faf5663 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -24,6 +24,7 @@ #include "NebulaLog.h" #include +#include #include using namespace std; @@ -744,17 +745,31 @@ public: // ------------------------------------------------------------------------ /** - * Attaches a new disk. It will acquire the Image used, if any, and add the - * disk to the VM template. The VM must be updated in the DB afterwards. - * - * @param tmpl Template containing a single DISK vector attribute. The - * caller must delete this template - * @param error_str Returns the error reason, if any - * - * @return 0 on success + * Collects information about VM DISKS + * @param num_disks of the VM + * @param used_targets by the DISKS of the VM */ - int attach_disk(VirtualMachineTemplate * tmpl, string& error_str); + void get_disk_info(int& num_disks, set& used_targets); + /** + * Generate a DISK attributed to be attached to the VM. + * @param tmpl Template containing a single DISK vector attribute. + * @param used_targets targets in use by current DISKS + * @param num_disks of the VM + * @param uid of the VM owner + * @param image_id returns the id of the aquired image + * @param error_str describes the error + * + * @return a new vectorattribute with the DISK (should be freed if not + * added to the template), 0 in case of error; + */ + static VectorAttribute * set_up_attach_disk( + VirtualMachineTemplate * tmpl, + set& used_targets, + int num_disks, + int uid, + int& image_id, + string& error_str); /** * Returns the disk that is waiting for an attachment action * @@ -764,18 +779,27 @@ public: /** * Cleans the ATTACH = YES attribute from the disks - * - * @return 0 on success, -1 otherwise */ - int attach_success(); + void clear_attach_disk(); /** - * Deletes the DISK that was in the process of being attached, and releases - * the image, if any + * Deletes the DISK that was in the process of being attached * - * @return 0 on success, -1 otherwise + * @return the DISK or 0 if no disk was deleted */ - int attach_failure(); + VectorAttribute * delete_attach_disk(); + + /** + * Adds a new disk to the virtual machine template. The disk should be + * generated by the build_attach_disk + * @param new_disk must be allocated in the heap + */ + void attach_disk(VectorAttribute * new_disk) + { + new_disk->replace("ATTACH", "YES"); + + obj_template->set(new_disk); + } private: diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 30dac49742..c7e9fb3b5a 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -685,9 +685,6 @@ int DispatchManager::finalize( VirtualMachine * vm; ostringstream oss; Template * tmpl; - - User * user; - Group * group; int uid; int gid; @@ -709,8 +706,6 @@ int DispatchManager::finalize( Nebula& nd = Nebula::instance(); TransferManager * tm = nd.get_tm(); LifeCycleManager * lcm = nd.get_lcm(); - UserPool * upool = nd.get_upool(); - GroupPool * gpool = nd.get_gpool(); switch (state) { @@ -739,34 +734,7 @@ int DispatchManager::finalize( vm->unlock(); - if ( uid != UserPool::ONEADMIN_ID ) - { - - user = upool->get(uid, true); - - if ( user != 0 ) - { - user->quota.vm_del(tmpl); - - upool->update(user); - - user->unlock(); - } - } - - if ( gid != GroupPool::ONEADMIN_ID ) - { - group = gpool->get(gid, true); - - if ( group != 0 ) - { - group->quota.vm_del(tmpl); - - gpool->update(group); - - group->unlock(); - } - } + Quotas::vm_del(uid, gid, tmpl); delete tmpl; break; @@ -845,59 +813,96 @@ int DispatchManager::resubmit(int vid) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int DispatchManager::attach( - VirtualMachine * vm, - VirtualMachineTemplate * tmpl, - string & error_str) + +int DispatchManager::attach(int vid, + VirtualMachineTemplate * tmpl, + string & error_str) { - ostringstream oss; - int rc; - int vid = vm->get_oid(); + ostringstream oss; - Nebula& nd = Nebula::instance(); - VirtualMachineManager * vmm = nd.get_vmm(); + int num_disks; + int uid; + int image_id; - oss << "Attaching a new disk to VM " << vid; - NebulaLog::log("DiM",Log::DEBUG,oss); + set used_targets; + VectorAttribute * disk; + + Nebula& nd = Nebula::instance(); + VirtualMachineManager* vmm = nd.get_vmm(); + + VirtualMachine * vm = vmpool->get(vid, true); + + if ( vm == 0 ) + { + oss << "VirtualMachine " << vid << " no longer exists"; + return -1; + } if ( vm->get_state() != VirtualMachine::ACTIVE || vm->get_lcm_state() != VirtualMachine::RUNNING ) { - goto error_state; - } + oss << "Could not attach a new disk to VM " << vid << ", wrong state."; + error_str = oss.str(); - rc = vm->attach_disk(tmpl, error_str); + NebulaLog::log("DiM", Log::ERROR, error_str); - if ( rc != 0 ) - { - goto error; + vm->unlock(); + return -1; } + + vm->get_disk_info(num_disks, used_targets); vm->set_state(VirtualMachine::HOTPLUG); + + vm->set_resched(false); + + uid = vm->get_uid(); + vmpool->update(vm); vm->unlock(); - delete tmpl; - vmm->trigger(VirtualMachineManager::ATTACH,vid); + disk = VirtualMachine::set_up_attach_disk(tmpl, + used_targets, + num_disks, + uid, + image_id, + error_str); + vm = vmpool->get(vid, true); + + if ( vm == 0 ) + { + Nebula& nd = Nebula::instance(); + ImageManager* imagem = nd.get_imagem(); + + if ( image_id != -1 ) + { + imagem->release_image(image_id, false); + } + + return -1; + } + + if ( disk == 0 ) + { + vm->set_state(VirtualMachine::RUNNING); + + vmpool->update(vm); + + vm->unlock(); + + return -1; + } + else + { + vm->attach_disk(disk); + } + + vmpool->update(vm); + + vm->unlock(); + + vmm->trigger(VirtualMachineManager::ATTACH,vid); return 0; - -error: - vm->unlock(); - delete tmpl; - - return -1; - -error_state: - oss.str(""); - oss << "Could not attach a new disk to VM " << vid << ", wrong state."; - error_str = oss.str(); - - NebulaLog::log("DiM", Log::ERROR, error_str); - - vm->unlock(); - delete tmpl; - - return -2; } diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index b2305cb026..6061820e1c 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -108,13 +108,6 @@ void DispatchManager::done_action(int vid) VirtualMachine::LcmState lcm_state; VirtualMachine::VmState dm_state; - Nebula& nd = Nebula::instance(); - UserPool * upool = nd.get_upool(); - GroupPool* gpool = nd.get_gpool(); - - User * user; - Group * group; - vm = vmpool->get(vid,true); if ( vm == 0 ) @@ -150,35 +143,7 @@ void DispatchManager::done_action(int vid) vm->unlock(); - /* ---------------- Update Group & User quota counters -------------- */ - - if ( uid != UserPool::ONEADMIN_ID ) - { - user = upool->get(uid, true); - - if ( user != 0 ) - { - user->quota.vm_del(tmpl); - - upool->update(user); - - user->unlock(); - } - } - - if ( gid != GroupPool::ONEADMIN_ID ) - { - group = gpool->get(gid, true); - - if ( group != 0 ) - { - group->quota.vm_del(tmpl); - - gpool->update(group); - - group->unlock(); - } - } + Quotas::vm_del(uid, gid, tmpl); delete tmpl; } diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 0258c70885..8bd464d082 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -274,13 +274,7 @@ int ImageManager::delete_image(int iid, const string& ds_data) int uid; int gid; - Group* group; - User * user; - - Nebula& nd = Nebula::instance(); - UserPool * upool = nd.get_upool(); - GroupPool* gpool = nd.get_gpool(); - + img = ipool->get(iid,true); if ( img == 0 ) @@ -354,33 +348,7 @@ int ImageManager::delete_image(int iid, const string& ds_data) img_usage.add("DATASTORE", ds_id); img_usage.add("SIZE", size); - if ( uid != UserPool::ONEADMIN_ID ) - { - user = upool->get(uid, true); - - if ( user != 0 ) - { - user->quota.ds_del(&img_usage); - - upool->update(user); - - user->unlock(); - } - } - - if ( gid != GroupPool::ONEADMIN_ID ) - { - group = gpool->get(gid, true); - - if ( group != 0 ) - { - group->quota.ds_del(&img_usage); - - gpool->update(group); - - group->unlock(); - } - } + Quotas::ds_del(uid, gid, &img_usage); return 0; } diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 0e53f4864e..c533696bdd 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -846,7 +846,7 @@ void LifeCycleManager::attach_success_action(int vid) return; } - vm->attach_success(); + vm->clear_attach_disk(); vm->set_state(VirtualMachine::RUNNING); @@ -860,7 +860,11 @@ void LifeCycleManager::attach_success_action(int vid) void LifeCycleManager::attach_failure_action(int vid) { - VirtualMachine * vm; + VirtualMachine * vm; + VectorAttribute * disk; + + int uid; + int gid; vm = vmpool->get(vid,true); @@ -869,7 +873,9 @@ void LifeCycleManager::attach_failure_action(int vid) return; } - vm->attach_failure(); + disk = vm->delete_attach_disk(); + uid = vm->get_uid(); + gid = vm->get_gid(); vm->set_state(VirtualMachine::RUNNING); @@ -877,7 +883,14 @@ void LifeCycleManager::attach_failure_action(int vid) vm->unlock(); - // TODO: update quotas, here or in VirtualMachine::attach_failure + if ( disk != 0 ) + { + Template tmpl; + + tmpl.set(disk); + + Quotas::vm_del(uid, gid, &tmpl); + } } /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index d862c5697b..a81d0ed078 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -22,12 +22,13 @@ /* -------------------------------------------------------------------------- */ bool RequestManagerVirtualMachine::vm_authorization( - int oid, - ImageTemplate * tmpl, - RequestAttributes& att, - PoolObjectAuth * host_perm, - PoolObjectAuth * ds_perm, - AuthRequest::Operation op) + int oid, + ImageTemplate * tmpl, + VirtualMachineTemplate* vtmpl, + RequestAttributes& att, + PoolObjectAuth * host_perm, + PoolObjectAuth * ds_perm, + AuthRequest::Operation op) { PoolObjectSQL * object; PoolObjectAuth vm_perms; @@ -69,6 +70,11 @@ bool RequestManagerVirtualMachine::vm_authorization( ar.add_create_auth(PoolObjectSQL::IMAGE, tmpl->to_xml(t_xml)); } + if ( vtmpl != 0 ) + { + VirtualMachine::set_auth_request(att.uid, ar, vtmpl); + } + if ( ds_perm != 0 ) { ar.add_auth(AuthRequest::USE, *ds_perm); @@ -196,7 +202,7 @@ void VirtualMachineAction::request_execute(xmlrpc_c::paramList const& paramList, op = AuthRequest::ADMIN; } - if ( vm_authorization(id, 0, att, 0, 0, op) == false ) + if ( vm_authorization(id, 0, 0, att, 0, 0, op) == false ) { return; } @@ -313,7 +319,7 @@ void VirtualMachineDeploy::request_execute(xmlrpc_c::paramList const& paramList, return; } - auth = vm_authorization(id, 0, att, &host_perms, 0, auth_op); + auth = vm_authorization(id, 0, 0, att, &host_perms, 0, auth_op); if ( auth == false ) { @@ -375,7 +381,7 @@ void VirtualMachineMigrate::request_execute(xmlrpc_c::paramList const& paramList return; } - auth = vm_authorization(id, 0, att, &host_perms, 0, auth_op); + auth = vm_authorization(id, 0, 0, att, &host_perms, 0, auth_op); if ( auth == false ) { @@ -546,7 +552,7 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis // Authorize the operation & check quotas // ------------------------------------------------------------------------- - if ( vm_authorization(id, itemplate, att, 0, &ds_perms, auth_op) == false ) + if ( vm_authorization(id, itemplate, 0, att, 0, &ds_perms, auth_op) == false ) { delete itemplate; return; @@ -598,7 +604,7 @@ void VirtualMachineMonitoring::request_execute( ostringstream oss; - bool auth = vm_authorization(id, 0, att, 0, 0, auth_op); + bool auth = vm_authorization(id, 0, 0, att, 0, 0, auth_op); if ( auth == false ) { @@ -624,59 +630,66 @@ void VirtualMachineMonitoring::request_execute( void VirtualMachineAttach::request_execute(xmlrpc_c::paramList const& paramList, RequestAttributes& att) { - Nebula& nd = Nebula::instance(); - DispatchManager * dm = nd.get_dm(); + Nebula& nd = Nebula::instance(); + DispatchManager * dm = nd.get_dm(); - VirtualMachine * vm; - VirtualMachineTemplate * tmpl; - PoolObjectAuth host_perms; + VirtualMachine * vm; + VirtualMachineTemplate * tmpl = new VirtualMachineTemplate(); + PoolObjectAuth host_perms; - int rc; + int rc; string error_str; int id = xmlrpc_c::value_int(paramList.getInt(1)); string str_tmpl = xmlrpc_c::value_string(paramList.getString(2)); - tmpl = new VirtualMachineTemplate(); + // ------------------------------------------------------------------------- + // Parse Disk template + // ------------------------------------------------------------------------- - rc = tmpl->parse_str_or_xml(str_tmpl, error_str); + rc = tmpl->parse_str_or_xml(str_tmpl, error_str); if ( rc != 0 ) { - failure_response(INTERNAL, "", att); // TODO: error message + failure_response(INTERNAL, error_str, att); delete tmpl; return; } - // TODO: auth & quotas - - // TODO: set vm state HOTPLUG & vm->set_resched(false); // Cancel re-scheduling actions - - vm = get_vm(id, att); - - if ( vm == 0 ) + // ------------------------------------------------------------------------- + // Authorize the operation & check quotas + // ------------------------------------------------------------------------- + + if ( vm_authorization(id, 0, tmpl, att, 0, 0, auth_op) == false ) { - failure_response(NO_EXISTS, - get_error(object_name(auth_object),id), - att); delete tmpl; - return; } - rc = dm->attach(vm, tmpl, error_str); + if ( quota_authorization(tmpl, att) == false ) + { + delete tmpl; + return; + } + + rc = dm->attach(id, tmpl, error_str); if ( rc != 0 ) { + quota_rollback(tmpl, att); + failure_response(ACTION, request_error(error_str, ""), att); - - return; + } + else + { + success_response(id, att); } - success_response(id, att); + delete tmpl; + return; } /* -------------------------------------------------------------------------- */ diff --git a/src/template/Template.cc b/src/template/Template.cc index 962546b097..52e72343a3 100644 --- a/src/template/Template.cc +++ b/src/template/Template.cc @@ -309,6 +309,33 @@ int Template::erase(Attribute * att) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +Attribute * Template::remove(Attribute * att) +{ + multimap::iterator i; + + pair< + multimap::iterator, + multimap::iterator + > index; + + index = attributes.equal_range( att->name() ); + + for ( i = index.first; i != index.second; i++ ) + { + if ( i->second == att ) + { + attributes.erase(i); + + return att; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int Template::get( const string& name, vector& values) const diff --git a/src/um/Quotas.cc b/src/um/Quotas.cc new file mode 100644 index 0000000000..e6a0b918f0 --- /dev/null +++ b/src/um/Quotas.cc @@ -0,0 +1,222 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "Quotas.h" +#include "Nebula.h" + +#include "ObjectXML.h" + + +int Quotas::set(Template *tmpl, string& error) +{ + vector vquotas; + + if ( tmpl->get(datastore_quota.get_quota_name(), vquotas) > 0 ) + { + if ( datastore_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(network_quota.get_quota_name(), vquotas) > 0 ) + { + if ( network_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(image_quota.get_quota_name(), vquotas) > 0 ) + { + if ( image_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(vm_quota.get_quota_name(), vquotas) > 0 ) + { + if ( vm_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string& Quotas::to_xml(string& xml) const +{ + ostringstream oss; + + string ds_quota_xml; + string net_quota_xml; + string vm_quota_xml; + string image_quota_xml; + + oss << datastore_quota.to_xml(ds_quota_xml) + << network_quota.to_xml(net_quota_xml) + << vm_quota.to_xml(vm_quota_xml) + << image_quota.to_xml(image_quota_xml); + + xml = oss.str(); + + return xml; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int Quotas::from_xml(ObjectXML * object_xml) +{ + vector content; + int rc = 0; + + object_xml->get_nodes(ds_xpath, content); + + if (!content.empty()) + { + rc += datastore_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + content.clear(); + + object_xml->get_nodes(net_xpath, content); + + if (!content.empty()) + { + rc += network_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + content.clear(); + + object_xml->get_nodes(vm_xpath, content); + + if (!content.empty()) + { + rc += vm_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + content.clear(); + + object_xml->get_nodes(img_xpath, content); + + if (!content.empty()) + { + rc += image_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void Quotas::vm_del(int uid, int gid, Template * tmpl) +{ + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + GroupPool * gpool = nd.get_gpool(); + + User * user; + Group * group; + + if ( uid != UserPool::ONEADMIN_ID ) + { + user = upool->get(uid, true); + + if ( user != 0 ) + { + user->quota.vm_del(tmpl); + + upool->update(user); + + user->unlock(); + } + } + + if ( gid != GroupPool::ONEADMIN_ID ) + { + group = gpool->get(gid, true); + + if ( group != 0 ) + { + group->quota.vm_del(tmpl); + + gpool->update(group); + + group->unlock(); + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void Quotas::ds_del(int uid, int gid, Template * tmpl) +{ + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + GroupPool * gpool = nd.get_gpool(); + + User * user; + Group * group; + + if ( uid != UserPool::ONEADMIN_ID ) + { + user = upool->get(uid, true); + + if ( user != 0 ) + { + user->quota.ds_del(tmpl); + + upool->update(user); + + user->unlock(); + } + } + + if ( gid != GroupPool::ONEADMIN_ID ) + { + group = gpool->get(gid, true); + + if ( group != 0 ) + { + group->quota.ds_del(tmpl); + + gpool->update(group); + + group->unlock(); + } + } +} diff --git a/src/um/SConstruct b/src/um/SConstruct index 893811e548..0c8ea406dd 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -28,7 +28,8 @@ source_files=[ 'QuotaDatastore.cc', 'QuotaNetwork.cc', 'QuotaVirtualMachine.cc', - 'QuotaImage.cc' + 'QuotaImage.cc', + 'Quotas.cc' ] # Build library diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 75aa159d23..449e34958b 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -1109,77 +1109,16 @@ error_common: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -// TODO: this method requires the VM to be locked, and then it locks the Image -// to acquire. Check if this can be troublesome - -int VirtualMachine::attach_disk(VirtualMachineTemplate * tmpl, string& error_str) +void VirtualMachine::get_disk_info(int& num_disks, + set& used_targets) { - int num_disks, rc; vector disks; - ImagePool * ipool; VectorAttribute * disk; - VectorAttribute * new_disk; - vector acquired_images; - int new_disk_id; - int image_id; - string dev_prefix; - string target; + string target; - queue > disks_queue; - - set used_targets; - - ostringstream oss; - Image::ImageType img_type; - - Nebula& nd = Nebula::instance(); - ipool = nd.get_ipool(); - - // ------------------------------------------------------------------------- - // Get the DISK attribute from the template - // ------------------------------------------------------------------------- - - num_disks = tmpl->get("DISK", disks); - - if ( num_disks != 1 ) - { - goto error_no_disk; - } - - new_disk = new VectorAttribute( *(dynamic_cast(disks[0])) ); - - // ------------------------------------------------------------------------- - // See if there is a CONTEXT cdrom, and get the target it is using - // ------------------------------------------------------------------------- - num_disks = obj_template->get("CONTEXT", disks); - - if ( num_disks > 0 ) - { - disk = dynamic_cast(disks[0]); - - if ( disk != 0 ) - { - target = disk->vector_value("TARGET"); - - if ( !target.empty() ) - { - used_targets.insert(target); - } - } - } - - // ------------------------------------------------------------------------- - // Check the used targets - // ------------------------------------------------------------------------- - disks.clear(); num_disks = obj_template->get("DISK", disks); - if ( num_disks >= 20 ) - { - goto error_max_disks; - } - for(int i=0; i(disks[i]); @@ -1197,79 +1136,101 @@ int VirtualMachine::attach_disk(VirtualMachineTemplate * tmpl, string& error_str } } + if ( obj_template->get("CONTEXT", disks) > 0 ) + { + disk = dynamic_cast(disks[0]); + + if ( disk != 0 ) + { + target = disk->vector_value("TARGET"); + + if ( !target.empty() ) + { + used_targets.insert(target); + } + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +VectorAttribute * VirtualMachine::set_up_attach_disk( + VirtualMachineTemplate * tmpl, + set& used_targets, + int num_disks, + int uid, + int& image_id, + string& error_str) +{ + vector disks; + VectorAttribute * new_disk; + + string target; + + Nebula& nd = Nebula::instance(); + ImagePool * ipool = nd.get_ipool(); + ImageManager* imagem = nd.get_imagem(); + + string dev_prefix; + Image::ImageType img_type; + + image_id = -1; + + // ------------------------------------------------------------------------- + // Get the DISK attribute from the template + // ------------------------------------------------------------------------- + + if ( tmpl->get("DISK", disks) != 1 ) + { + error_str = "The template must contain one DISK attribute"; + return 0; + } + + new_disk = new VectorAttribute(*(dynamic_cast(disks[0]))); + // ------------------------------------------------------------------------- // Acquire the new disk image // ------------------------------------------------------------------------- - - // num_disks +1 because the context is not a DISK, but it takes the - // ds//disk.num_disks file - new_disk_id = num_disks + 1; - - rc = ipool->disk_attribute(new_disk, - new_disk_id, - img_type, - dev_prefix, - uid, - image_id, - error_str); - if ( rc == 0 ) + + int rc = ipool->disk_attribute(new_disk, + num_disks + 1, //Preserv CONTEXT disk.i file + img_type, + dev_prefix, + uid, + image_id, + error_str); + if ( rc != 0 ) { - acquired_images.push_back(image_id); + return 0; + } - target = new_disk->vector_value("TARGET"); + target = new_disk->vector_value("TARGET"); - if ( !target.empty() ) + if ( !target.empty() ) + { + if ( used_targets.insert(target).second == false ) { - if ( used_targets.insert(target).second == false ) - { - goto error_duplicated_target; - } - } - else - { - disks_queue.push( make_pair(dev_prefix, new_disk) ); + ostringstream oss; + + oss << "Target " << target << "is already in use."; + error_str = oss.str(); + + imagem->release_image(image_id, false); } + + return 0; } else { - goto error_common; + queue > disks_queue; + + disks_queue.push(make_pair(dev_prefix, new_disk)); + + assign_disk_targets(disks_queue, used_targets); } - assign_disk_targets(disks_queue, used_targets); - - // ------------------------------------------------------------------------- - // Add the disk to the VM template - // ------------------------------------------------------------------------- - - new_disk->replace("ATTACH", "YES"); - - obj_template->set(new_disk); - - return 0; - -error_no_disk: - error_str = "The template must contain one DISK attribute"; - return -1; - -error_max_disks: - error_str = "Exceeded the maximum number of disks (20)"; - return -1; - -error_duplicated_target: - oss << "Two disks have defined the same target " << target; - error_str = oss.str(); - -error_common: - ImageManager * imagem = nd.get_imagem(); - - vector::iterator it; - - for ( it=acquired_images.begin() ; it < acquired_images.end(); it++ ) - { - imagem->release_image(*it, false); - } - - return -1; + return new_disk; } /* -------------------------------------------------------------------------- */ @@ -1281,11 +1242,6 @@ VectorAttribute* VirtualMachine::get_attach_disk() vector disks; VectorAttribute * disk; - ostringstream oss; - - // ------------------------------------------------------------------------- - // Set DISK attributes & Targets - // ------------------------------------------------------------------------- num_disks = obj_template->get("DISK", disks); for(int i=0; i disks; VectorAttribute * disk; - bool removed; num_disks = obj_template->get("DISK", disks); @@ -1330,46 +1285,25 @@ int VirtualMachine::attach_success() if ( disk->vector_value("ATTACH") == "YES" ) { disk->remove("ATTACH"); - removed = true; + return; } } - - if ( removed ) - { - return 0; - } - - return -1; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -// TODO: this method requires the VM to be locked, and then it locks the Image -// to release. Check if this can be troublesome - -int VirtualMachine::attach_failure() +VectorAttribute * VirtualMachine::delete_attach_disk() { - int num_disks; vector disks; VectorAttribute * disk; - bool found = false; - bool uses_image = false; - int iid; - Nebula& nd = Nebula::instance(); - ImageManager * imagem = nd.get_imagem(); + int num_disks = obj_template->get("DISK", disks); - num_disks = obj_template->get("DISK", disks); - - int i = 0; - - while( !found && i(disks[i]); - i++; - if ( disk == 0 ) { continue; @@ -1377,23 +1311,10 @@ int VirtualMachine::attach_failure() if ( disk->vector_value("ATTACH") == "YES" ) { - uses_image = ( disk->vector_value("IMAGE_ID", iid) != -1 ); - - obj_template->erase(disk); - found = true; + return static_cast(obj_template->remove(disk)); } } - if ( !found ) - { - return -1; - } - - if ( uses_image ) - { - imagem->release_image(iid, false); - } - return 0; } From 6e44c6ebf32175cdfd4ba186a921b44cd2e4e196 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 15 Jun 2012 12:43:52 +0200 Subject: [PATCH 2/4] feature #1233: Fix compilation. Detach operation broken --- src/dm/DispatchManagerActions.cc | 8 ++++---- src/vm/VirtualMachine.cc | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 651afc07c9..627bf300d6 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -919,7 +919,7 @@ int DispatchManager::detach( int disk_id, string & error_str) { - ostringstream oss; + /* ostringstream oss; int rc; int vid = vm->get_oid(); @@ -942,8 +942,8 @@ int DispatchManager::detach( goto error; } - // TODO: Cancel resched? - // vm->set_resched(false); + TODO: Cancel resched? + vm->set_resched(false); vm->set_state(VirtualMachine::HOTPLUG); vmpool->update(vm); @@ -968,5 +968,5 @@ error_state: vm->unlock(); - return -2; + return -2;*/ } diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 3b49ebd8f5..69bad3686b 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -1238,9 +1238,10 @@ VectorAttribute * VirtualMachine::set_up_attach_disk( // TODO: this method requires the VM to be locked, and then it locks the Image // to acquire. Check if this can be troublesome - + /* int VirtualMachine::detach_disk(int disk_id, string& error_str) { + int num_disks; vector disks; VectorAttribute * disk; @@ -1276,8 +1277,9 @@ int VirtualMachine::detach_disk(int disk_id, string& error_str) } return 0; -} +} + */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -1368,7 +1370,8 @@ VectorAttribute * VirtualMachine::delete_attach_disk() int VirtualMachine::detach_success() { - return attach_failure(); + return 0; + /*return attach_failure();*/ } /* -------------------------------------------------------------------------- */ @@ -1376,7 +1379,8 @@ int VirtualMachine::detach_success() int VirtualMachine::detach_failure() { - return attach_success(); + return 0; + /*return attach_success();*/ } /* -------------------------------------------------------------------------- */ From e2927e871abdb0d1351008c15689c006840b4e99 Mon Sep 17 00:00:00 2001 From: Jaime Melis Date: Fri, 15 Jun 2012 13:27:06 +0200 Subject: [PATCH 3/4] Feature #1223: Add OCA and CLI for onevm attach and onevm detach --- src/cli/command_parser.rb | 4 ++ src/cli/one_helper/onevm_helper.rb | 18 ++++++++ src/cli/onevm | 53 +++++++++++++++++++++-- src/oca/ruby/OpenNebula/VirtualMachine.rb | 28 ++++++++++-- 4 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/cli/command_parser.rb b/src/cli/command_parser.rb index efe463d962..7c844738f1 100644 --- a/src/cli/command_parser.rb +++ b/src/cli/command_parser.rb @@ -405,6 +405,10 @@ EOT arg.instance_of?(String) ? [0,arg] : [-1] end + def format_int(arg) + arg.match(/^\d+$/) ? [0,arg] : [-1] + end + def format_file(arg) File.file?(arg) ? [0,arg] : [-1] end diff --git a/src/cli/one_helper/onevm_helper.rb b/src/cli/one_helper/onevm_helper.rb index b7d32be70d..99f638ac1f 100644 --- a/src/cli/one_helper/onevm_helper.rb +++ b/src/cli/one_helper/onevm_helper.rb @@ -25,6 +25,24 @@ class OneVMHelper < OpenNebulaHelper::OneHelper :description => "Instance multiple VMs" } + IMAGE = { + :name => "image", + :short => "-i id|name", + :large => "--image id|name" , + :description => "Selects the image", + :format => String, + :proc => lambda { |o, options| + rc, imid = OpenNebulaHelper.rname_to_id(o, "IMAGE") + if rc == 0 + options[:image] = imid + else + puts imid + puts "option image: Parsing error" + exit -1 + end + } + } + def self.rname "VM" end diff --git a/src/cli/onevm b/src/cli/onevm index 720d7a1f84..7bfa063a0f 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -77,6 +77,10 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.filterflag_to_i(arg) end + set :format, :diskid, "Integer" do |arg| + format_int(arg) + end + ######################################################################## # Commands ######################################################################## @@ -206,7 +210,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do vm.reset end end - + deploy_desc = <<-EOT.unindent Deploys the given VM in the specified Host. This command forces the deployment, in a standard installation the Scheduler is in charge @@ -334,6 +338,49 @@ cmd=CommandParser::CmdParser.new(ARGV) do end end + attachdisk_desc = <<-EOT.unindent + Attaches a disk to a running VM + + States: RUNNING + EOT + + command :attachdisk, attachdisk_desc, :vmid, [:file, nil], :options => OneVMHelper::IMAGE do + + template_file = args[1] + + if template_file.nil? and options[:image].nil? + STDERR.puts "Provide a template file or an image:" + STDERR.puts "\t" + STDERR.puts "\t--image " + exit -1 + end + + if template_file.nil? + image_id = options[:image] + template = "DISK = [ IMAGE_ID = #{image_id} ]" + else + template = File.read(template_file) + end + + helper.perform_action(args[0],options,"Attach disk") do |vm| + vm.attachdisk(template) + end + end + + detachdisk_desc = <<-EOT.unindent + Detaches a disk from a running VM + + States: RUNNING + EOT + + command :detachdisk, detachdisk_desc, :vmid, :diskid do + diskid = args[1].to_i + + helper.perform_action(args[0],options,"Detach disk") do |vm| + vm.detachdisk(diskid) + end + end + chgrp_desc = <<-EOT.unindent Changes the VM group EOT @@ -367,7 +414,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do end resched_desc = <<-EOT.unindent - Sets the rescheduling flag for the VM. + Sets the rescheduling flag for the VM. States: RUNNING EOT @@ -379,7 +426,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do end unresched_desc = <<-EOT.unindent - Clears the rescheduling flag for the VM. + Clears the rescheduling flag for the VM. States: RUNNING EOT diff --git a/src/oca/ruby/OpenNebula/VirtualMachine.rb b/src/oca/ruby/OpenNebula/VirtualMachine.rb index fd8f126366..69d0855800 100644 --- a/src/oca/ruby/OpenNebula/VirtualMachine.rb +++ b/src/oca/ruby/OpenNebula/VirtualMachine.rb @@ -33,7 +33,9 @@ module OpenNebula :savedisk => "vm.savedisk", :chown => "vm.chown", :chmod => "vm.chmod", - :monitoring => "vm.monitoring" + :monitoring => "vm.monitoring", + :attach => "vm.attach", + :detach => "vm.detach" } VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED} @@ -154,7 +156,7 @@ module OpenNebula def reset action('reset') end - + # Cancels a running VM def cancel action('cancel') @@ -185,6 +187,26 @@ module OpenNebula action('resume') end + # Attaches a disk to a running VM + def attachdisk(disk) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(VM_METHODS[:attach], @pe_id, disk) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Detaches a disk from a running VM + def detachdisk(disk) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(VM_METHODS[:detach], @pe_id, disk) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + # Deletes a VM from the pool def finalize action('finalize') @@ -291,7 +313,7 @@ module OpenNebula # @example # vm.monitoring( ['CPU', 'NET_TX', 'TEMPLATE/CUSTOM_PROBE'] ) # - # { "NET_TX" => + # { "NET_TX" => # [["1337264510", "210"], # ["1337264553", "220"], # ["1337264584", "230"]], From ba04cb0420467360942f2d68073550c76c9a209b Mon Sep 17 00:00:00 2001 From: Jaime Melis Date: Fri, 15 Jun 2012 14:02:08 +0200 Subject: [PATCH 4/4] feature #1223: Add HOTPLUG to the LCM states in OCA ruby, OCA Java, Ozones GUI and Sunstone GUI --- .../java/src/org/opennebula/client/vm/VirtualMachine.java | 6 ++++-- src/oca/ruby/OpenNebula/VirtualMachine.rb | 5 +++-- src/ozones/Server/public/js/ozones.js | 3 ++- src/sunstone/public/js/opennebula.js | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java b/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java index c8147d5b35..464bd9c873 100644 --- a/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java +++ b/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java @@ -78,7 +78,8 @@ public class VirtualMachine extends PoolElement{ "CANCEL", "FAILURE", "CLEANUP", - "UNKNOWN" }; + "UNKNOWN". + "HOTPLUG" }; private static final String[] SHORT_LCM_STATES = { @@ -98,7 +99,8 @@ public class VirtualMachine extends PoolElement{ "shut", "fail", "dele", - "unkn" }; + "unkn", + "hotp" }; /** * Creates a new VM representation. diff --git a/src/oca/ruby/OpenNebula/VirtualMachine.rb b/src/oca/ruby/OpenNebula/VirtualMachine.rb index 69d0855800..22936afa78 100644 --- a/src/oca/ruby/OpenNebula/VirtualMachine.rb +++ b/src/oca/ruby/OpenNebula/VirtualMachine.rb @@ -42,7 +42,7 @@ module OpenNebula LCM_STATE=%w{LCM_INIT PROLOG BOOT RUNNING MIGRATE SAVE_STOP SAVE_SUSPEND SAVE_MIGRATE PROLOG_MIGRATE PROLOG_RESUME EPILOG_STOP EPILOG - SHUTDOWN CANCEL FAILURE CLEANUP UNKNOWN} + SHUTDOWN CANCEL FAILURE CLEANUP UNKNOWN HOTPLUG} SHORT_VM_STATES={ "INIT" => "init", @@ -71,7 +71,8 @@ module OpenNebula "CANCEL" => "shut", "FAILURE" => "fail", "CLEANUP" => "clea", - "UNKNOWN" => "unkn" + "UNKNOWN" => "unkn", + "HOTPLUG" => "hotp" } MIGRATE_REASON=%w{NONE ERROR STOP_RESUME USER CANCEL} diff --git a/src/ozones/Server/public/js/ozones.js b/src/ozones/Server/public/js/ozones.js index d7fd08e3c3..a6bc0b42d4 100644 --- a/src/ozones/Server/public/js/ozones.js +++ b/src/ozones/Server/public/js/ozones.js @@ -89,7 +89,8 @@ var oZones = { "CANCEL", "FAILURE", "CLEANUP", - "UNKNOWN"][value]; + "UNKNOWN", + "HOTPLUG"][value]; break; case "IMAGE","image": return ["INIT", diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js index 1324599362..080dbd4747 100644 --- a/src/sunstone/public/js/opennebula.js +++ b/src/sunstone/public/js/opennebula.js @@ -93,7 +93,8 @@ var OpenNebula = { "CANCEL", "FAILURE", "CLEANUP", - "UNKNOWN"][value]); + "UNKNOWN" + "HOTPLUG"][value]); break; case "IMAGE": case "image":