diff --git a/include/Image.h b/include/Image.h index f3687dede2..1ff370ea52 100644 --- a/include/Image.h +++ b/include/Image.h @@ -496,6 +496,14 @@ public: return snapshots; }; + /** + * Clear all the snapshots in the list + */ + void clear_snapshots() + { + snapshots.clear(); + } + /** * Set the snapshots for this image * @param snapshot list diff --git a/include/ImageManager.h b/include/ImageManager.h index 8be735fbf1..a12ece2ed1 100644 --- a/include/ImageManager.h +++ b/include/ImageManager.h @@ -217,6 +217,15 @@ public: */ int revert_snapshot(int iid, int sid, string& error); + /** + * Flattens ths snapshot by commiting changes to base image. + * @param iid id of image + * @param sid id of the snapshot + * @param error_str Error reason, if any + * @return 0 on success + */ + int flatten_snapshot(int iid, int sid, string& error); + private: /** * Generic name for the Image driver diff --git a/include/Snapshots.h b/include/Snapshots.h index eba353bf01..adbfea9a8c 100644 --- a/include/Snapshots.h +++ b/include/Snapshots.h @@ -100,6 +100,19 @@ public: */ int active_snapshot(unsigned int id); + /** + * Clear all the snapshots in the list + */ + void clear() + { + next_snapshot = 0; + active = -1; + disk_id = -1; + + snapshot_template.clear(); + snapshot_pool.clear(); + } + /** * Return the disk_id of the snapshot list */ diff --git a/src/cli/oneimage b/src/cli/oneimage index 19f23a4fa0..431f764a1b 100755 --- a/src/cli/oneimage +++ b/src/cli/oneimage @@ -321,6 +321,16 @@ cmd=CommandParser::CmdParser.new(ARGV) do end end + snapshot_flatten_desc = <<-EOT.unindent + Flattens the snapshot and removes all other snapshots in the image + EOT + + command :"snapshot-flatten", snapshot_flatten_desc, :imageid, :snapshot_id do + helper.perform_action(args[0], options, "snapshot flattened") do |o| + o.snapshot_flatten(args[1].to_i) + end + end + list_desc = <<-EOT.unindent Lists Images in the pool EOT diff --git a/src/datastore_mad/one_datastore.rb b/src/datastore_mad/one_datastore.rb index 9c791c114f..a0680c01db 100755 --- a/src/datastore_mad/one_datastore.rb +++ b/src/datastore_mad/one_datastore.rb @@ -52,7 +52,8 @@ class DatastoreDriver < OpenNebulaDriver :clone => "CLONE", :monitor => "MONITOR", :snap_delete => "SNAP_DELETE", - :snap_revert => "SNAP_REVERT" + :snap_revert => "SNAP_REVERT", + :snap_flatten=> "SNAP_FLATTEN" } # Register default actions for the protocol @@ -69,7 +70,8 @@ class DatastoreDriver < OpenNebulaDriver ACTION[:clone] => nil, ACTION[:monitor] => nil, ACTION[:snap_delete] => nil, - ACTION[:snap_revert] => nil + ACTION[:snap_revert] => nil, + ACTION[:snap_flatten] => nil } }.merge!(options) @@ -93,6 +95,7 @@ class DatastoreDriver < OpenNebulaDriver register_action(ACTION[:monitor].to_sym, method("monitor")) register_action(ACTION[:snap_delete].to_sym, method("snap_delete")) register_action(ACTION[:snap_revert].to_sym, method("snap_revert")) + register_action(ACTION[:snap_flatten].to_sym, method("snap_flatten")) end ############################################################################ @@ -139,6 +142,11 @@ class DatastoreDriver < OpenNebulaDriver do_image_action(id, ds, :snap_revert, "#{drv_message} #{id}") end + def snap_flatten(id, drv_message) + ds = get_ds_type(drv_message) + do_image_action(id, ds, :snap_flatten, "#{drv_message} #{id}") + end + private def is_available?(ds, id, action) diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 3cf9c5c5f4..0fabd69811 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -1129,3 +1129,99 @@ int ImageManager::revert_snapshot(int iid, int sid, string& error) return 0; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int ImageManager::flatten_snapshot(int iid, int sid, string& error) +{ + const ImageManagerDriver* imd = get(); + + if ( imd == 0 ) + { + error = "Could not get datastore driver"; + NebulaLog::log("ImM",Log::ERROR, error); + + return -1; + } + + /* ---------------------------------------------------------------------- */ + /* Check action consistency: */ + /* state is READY */ + /* snapshot exists */ + /* ---------------------------------------------------------------------- */ + + Image * img = ipool->get(iid,true); + + if ( img == 0 ) + { + error = "Image does not exist"; + return -1; + } + + if (img->get_state() != Image::READY) + { + error = "Cannot flatten snapshot in state " + Image::state_to_str(img->get_state()); + img->unlock(); + return -1; + } + + const Snapshots& snaps = img->get_snapshots(); + + if (!snaps.exists(sid)) + { + error = "Snapshot does not exist"; + + img->unlock(); + return -1; + } + + /* ---------------------------------------------------------------------- */ + /* Get DS data for driver */ + /* ---------------------------------------------------------------------- */ + int ds_id = img->get_ds_id(); + + img->unlock(); + + string ds_data; + + Datastore * ds = dspool->get(ds_id, true); + + if ( ds == 0 ) + { + error = "Datastore no longer exists"; + return -1; + } + + ds->to_xml(ds_data); + + ds->unlock(); + + /* ---------------------------------------------------------------------- */ + /* Format message and send action to driver */ + /* ---------------------------------------------------------------------- */ + img = ipool->get(iid,true); + + if ( img == 0 ) + { + error = "Image does not exist"; + return -1; + } + + img->set_target_snapshot(sid); + + string img_tmpl; + string * drv_msg = format_message(img->to_xml(img_tmpl), ds_data); + + imd->snapshot_flatten(iid, *drv_msg); + + img->set_state(Image::LOCKED); + + ipool->update(img); + + img->unlock(); + + delete drv_msg; + + return 0; +} + diff --git a/src/image/ImageManagerDriver.cc b/src/image/ImageManagerDriver.cc index fa6dec4442..cb83c6566b 100644 --- a/src/image/ImageManagerDriver.cc +++ b/src/image/ImageManagerDriver.cc @@ -815,6 +815,52 @@ static void snap_revert_action(istringstream& is, image->unlock(); } +/* -------------------------------------------------------------------------- */ + +static void snap_flatten_action(istringstream& is, + ImagePool* ipool, + int id, + const string& result) +{ + ostringstream oss; + string info; + + Image * image = ipool->get(id, true); + + if ( image == 0 ) + { + return; + } + + if ( result == "SUCCESS") + { + image->clear_snapshots(); + } + else + { + oss << "Error flattening image snapshot"; + + getline(is, info); + + if (!info.empty() && (info[0] != '-')) + { + oss << ": " << info; + } + + image->set_template_error_message(oss.str()); + + NebulaLog::log("ImM", Log::ERROR, oss); + } + + image->set_state(Image::READY); + + image->clear_target_snapshot(); + + ipool->update(image); + + image->unlock(); +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -898,6 +944,10 @@ void ImageManagerDriver::protocol(const string& message) const { snap_revert_action(is, ipool, id, result); } + else if (action == "SNAP_FLATTEN") + { + snap_flatten_action(is, ipool, id, result); + } else if (action == "LOG") { getline(is,info); diff --git a/src/oca/ruby/opennebula/image.rb b/src/oca/ruby/opennebula/image.rb index c5d9f61837..3940989487 100644 --- a/src/oca/ruby/opennebula/image.rb +++ b/src/oca/ruby/opennebula/image.rb @@ -37,7 +37,8 @@ module OpenNebula :clone => "image.clone", :rename => "image.rename", :snapshotdelete => "image.snapshotdelete", - :snapshotrevert => "image.snapshotrevert" + :snapshotrevert => "image.snapshotrevert", + :snapshotflatten=> "image.snapshotflatten" } IMAGE_STATES=%w{INIT READY USED DISABLED LOCKED ERROR CLONE DELETE USED_PERS} @@ -243,6 +244,14 @@ module OpenNebula return call(IMAGE_METHODS[:snapshotrevert], @pe_id, snap_id) end + # Flattens an image snapshot + # + # @param snap_id [Integet] ID of the snapshot to flatten + # + # @return [nil, OpenNebula::Error] nil in case of success or Error + def snapshot_flatten(snap_id) + return call(IMAGE_METHODS[:snapshotflatten], @pe_id, snap_id) + end ####################################################################### # Helpers to get Image information ####################################################################### diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 15616dd1d7..a390631b72 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -399,6 +399,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr image_clone(new ImageClone()); xmlrpc_c::methodPtr image_snap_delete(new ImageSnapshotDelete()); xmlrpc_c::methodPtr image_snap_revert(new ImageSnapshotRevert()); + xmlrpc_c::methodPtr image_snap_flatten(new ImageSnapshotFlatten()); // Datastore Methods xmlrpc_c::methodPtr datastore_enable(new DatastoreEnable()); @@ -657,6 +658,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.image.rename", image_rename); RequestManagerRegistry.addMethod("one.image.snapshotdelete", image_snap_delete); RequestManagerRegistry.addMethod("one.image.snapshotrevert", image_snap_revert); + RequestManagerRegistry.addMethod("one.image.snapshotflatten", image_snap_flatten); RequestManagerRegistry.addMethod("one.imagepool.info", imagepool_info); diff --git a/src/rm/RequestManagerImage.cc b/src/rm/RequestManagerImage.cc index 5a7768da7c..f13dc45170 100644 --- a/src/rm/RequestManagerImage.cc +++ b/src/rm/RequestManagerImage.cc @@ -498,7 +498,6 @@ void ImageSnapshotDelete::request_execute(xmlrpc_c::paramList const& paramList, success_response(id, att); } - /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ @@ -528,3 +527,32 @@ void ImageSnapshotRevert::request_execute(xmlrpc_c::paramList const& paramList, success_response(id, att); } +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void ImageSnapshotFlatten::request_execute(xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + int id = xmlrpc_c::value_int(paramList.getInt(1)); + int snap_id = xmlrpc_c::value_int(paramList.getInt(2)); + + Nebula& nd = Nebula::instance(); + ImageManager * imagem = nd.get_imagem(); + + if ( basic_authorization(id, att) == false ) + { + return; + } + + string err_msg; + int rc = imagem->flatten_snapshot(id, snap_id, err_msg); + + if ( rc < 0 ) + { + failure_response(ACTION, request_error(err_msg, ""), att); + return; + } + + success_response(id, att); +} +