diff --git a/include/Template.h b/include/Template.h index 7726bdf23e..14c820723e 100644 --- a/include/Template.h +++ b/include/Template.h @@ -351,6 +351,16 @@ protected: */ bool check(string& rs_attr, const vector &restricted_attributes); + /** + * Updates the xml root element name + * + * @param _xml_root New name + */ + void set_xml_root(const char * _xml_root) + { + xml_root = _xml_root; + }; + private: bool replace_mode; diff --git a/include/Util.h b/include/Util.h new file mode 100644 index 0000000000..be18222f1a --- /dev/null +++ b/include/Util.h @@ -0,0 +1,33 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs */ +/* */ +/* 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. */ +/* -------------------------------------------------------------------------- */ + +#ifndef UTIL_H_ +#define UTIL_H_ + +#include + +namespace one_util +{ + std::string& toupper(std::string& st); + + std::string& tolower(std::string& st); + + std::string log_time(time_t the_time); + + std::string log_time(); +}; + +#endif /* UTIL_H_ */ diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 64a3e7fdca..e3a88b61bb 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -1178,7 +1178,7 @@ private: int parse_context(string& error_str); /** - * Parse the "REQUIREMENTS" attribute of the template by substituting + * Parse the "SCHED_REQUIREMENTS" attribute of the template by substituting * $VARIABLE, $VARIABLE[ATTR] and $VARIABLE[ATTR, ATTR = VALUE] * @param error_str Returns the error reason, if any * @return 0 on success diff --git a/include/VirtualMachineTemplate.h b/include/VirtualMachineTemplate.h index 0f2575f3f6..532c6fae0d 100644 --- a/include/VirtualMachineTemplate.h +++ b/include/VirtualMachineTemplate.h @@ -45,7 +45,12 @@ public: { return Template::check(rs_attr, restricted_attributes); }; - + + void set_xml_root(const char * _xml_root) + { + Template::set_xml_root(_xml_root); + }; + private: friend class VirtualMachinePool; diff --git a/share/etc/oned.conf b/share/etc/oned.conf index a228fac53a..506bdc8c18 100644 --- a/share/etc/oned.conf +++ b/share/etc/oned.conf @@ -499,6 +499,10 @@ DEFAULT_UMASK = 177 VM_RESTRICTED_ATTR = "CONTEXT/FILES" VM_RESTRICTED_ATTR = "NIC/MAC" VM_RESTRICTED_ATTR = "NIC/VLAN_ID" -VM_RESTRICTED_ATTR = "RANK" + +#VM_RESTRICTED_ATTR = "RANK" +#VM_RESTRICTED_ATTR = "SCHED_RANK" +#VM_RESTRICTED_ATTR = "REQUIREMENTS" +#VM_RESTRICTED_ATTR = "SCHED_REQUIREMENTS" IMAGE_RESTRICTED_ATTR = "SOURCE" diff --git a/src/cli/cli_helper.rb b/src/cli/cli_helper.rb index ff095ffdc5..1fbd83b1f1 100644 --- a/src/cli/cli_helper.rb +++ b/src/cli/cli_helper.rb @@ -252,6 +252,9 @@ module CLIHelper if @columns[field] minus=( @columns[field][:left] ? "-" : "" ) size=@columns[field][:size] + if @columns[field][:donottruncate] + return "%#{minus}#{size}s" % [ data.to_s ] + end return "%#{minus}#{size}.#{size}s" % [ data.to_s ] else exit -1, "Column #{field} not defined." diff --git a/src/cli/one_helper.rb b/src/cli/one_helper.rb index 9b02938cb3..f812bc0de6 100644 --- a/src/cli/one_helper.rb +++ b/src/cli/one_helper.rb @@ -577,13 +577,19 @@ EOT end end - def OpenNebulaHelper.time_to_str(time) + def OpenNebulaHelper.time_to_str(time, print_seconds=true) value=time.to_i if value==0 value='-' else - value=Time.at(value).strftime("%m/%d %H:%M:%S") + if print_seconds + value=Time.at(value).strftime("%m/%d %H:%M:%S") + else + value=Time.at(value).strftime("%m/%d %H:%M") + end end + + return value end def OpenNebulaHelper.period_to_str(time, print_seconds=true) diff --git a/src/cli/one_helper/onevm_helper.rb b/src/cli/one_helper/onevm_helper.rb index 41f1646da2..aaa53b8435 100644 --- a/src/cli/one_helper/onevm_helper.rb +++ b/src/cli/one_helper/onevm_helper.rb @@ -15,6 +15,7 @@ #--------------------------------------------------------------------------- # require 'one_helper' +require 'optparse/time' class OneVMHelper < OpenNebulaHelper::OneHelper MULTIPLE={ @@ -57,6 +58,13 @@ class OneVMHelper < OpenNebulaHelper::OneHelper :description => "Creates the new VM on hold state instead of pending" } + SCHEDULE = { + :name => "schedule", + :large => "--schedule TIME", + :description => "Schedules this action to be executed after the given time", + :format => Time + } + def self.rname "VM" end @@ -141,6 +149,38 @@ class OneVMHelper < OpenNebulaHelper::OneHelper table end + + def schedule_actions(ids,options,action) + # Verbose by default + options[:verbose] = true + + perform_actions( + ids, options, + "#{action} scheduled at #{options[:schedule]}") do |vm| + + rc = vm.info + + if OpenNebula.is_error?(rc) + puts rc.message + exit -1 + end + + ids = vm.retrieve_elements('USER_TEMPLATE/SCHED_ACTION/ID') + + id = 0 + if (!ids.nil? && !ids.empty?) + ids.map! {|e| e.to_i } + id = ids.max + 1 + end + + tmp_str = vm.user_template_str + + tmp_str << "\nSCHED_ACTION = [ID = #{id}, ACTION = #{action}, TIME = #{options[:schedule].to_i}]" + + vm.update(tmp_str) + end + end + private def factory(id=nil) @@ -212,12 +252,43 @@ class OneVMHelper < OpenNebulaHelper::OneHelper } puts + if vm.has_elements?("/VM/USER_TEMPLATE/SCHED_ACTION") + CLIHelper.print_header(str_h1 % "SCHEDULED ACTIONS",false) + + CLIHelper::ShowTable.new(nil, self) do + + column :"ID", "", :size=>2 do |d| + d["ID"] if !d.nil? + end + + column :"ACTION", "", :left, :size=>10 do |d| + d["ACTION"] if !d.nil? + end + + column :"SCHEDULED", "", :size=>12 do |d| + OpenNebulaHelper.time_to_str(d["TIME"], false) if !d.nil? + end + + column :"DONE", "", :size=>12 do |d| + OpenNebulaHelper.time_to_str(d["DONE"], false) if !d.nil? + end + + column :"MESSAGE", "", :left, :donottruncate, :size=>40 do |d| + d["MESSAGE"] if !d.nil? + end + end.show([vm.to_hash['VM']['USER_TEMPLATE']['SCHED_ACTION']].flatten, {}) + + puts + end + CLIHelper.print_header(str_h1 % "VIRTUAL MACHINE TEMPLATE",false) puts vm.template_str if vm.has_elements?("/VM/USER_TEMPLATE") puts + vm.delete_element("/VM/USER_TEMPLATE/SCHED_ACTION") + CLIHelper.print_header(str_h1 % "USER TEMPLATE",false) puts vm.template_like_str('USER_TEMPLATE') end diff --git a/src/cli/onevm b/src/cli/onevm index 913518457c..cea2d42190 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -192,9 +192,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: ANY EOT - command :delete, delete_desc, [:range, :vmid_list] do - helper.perform_actions(args[0],options,"deleted") do |vm| - vm.finalize + command :delete, delete_desc, [:range, :vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"deleted") do |vm| + vm.finalize + end end end @@ -205,9 +210,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: PENDING EOT - command :hold, hold_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"put on hold") do |vm| - vm.hold + command :hold, hold_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"put on hold") do |vm| + vm.hold + end end end @@ -217,9 +227,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: HOLD EOT - command :release, release_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"released") do |vm| - vm.release + command :release, release_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"released") do |vm| + vm.release + end end end @@ -257,9 +272,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :shutdown, shutdown_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"shutting down") do |vm| - vm.shutdown + command :shutdown, shutdown_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"shutting down") do |vm| + vm.shutdown + end end end @@ -270,9 +290,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :poweroff, poweroff_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"shutting down") do |vm| - vm.poweroff + command :poweroff, poweroff_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"shutting down") do |vm| + vm.poweroff + end end end @@ -283,9 +308,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :reboot, reboot_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"rebooting") do |vm| - vm.reboot + command :reboot, reboot_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"rebooting") do |vm| + vm.reboot + end end end @@ -295,9 +325,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :reset, reset_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"resetting") do |vm| - vm.reset + command :reset, reset_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"resetting") do |vm| + vm.reset + end end end @@ -366,9 +401,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: UNKNOWN, BOOT, POWEROFF EOT - command :restart, restart_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"restarting") do |vm| - vm.restart + command :restart, restart_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"restarting") do |vm| + vm.restart + end end end @@ -380,9 +420,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: ANY, except SUSPENDED or DONE EOT - command :resubmit, resubmit_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"resubmiting") do |vm| - vm.resubmit + command :resubmit, resubmit_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"resubmiting") do |vm| + vm.resubmit + end end end @@ -394,9 +439,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :cancel, cancel_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"canceling") do |vm| - vm.cancel + command :cancel, cancel_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"canceling") do |vm| + vm.cancel + end end end @@ -407,9 +457,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :stop, stop_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"stopping") do |vm| - vm.stop + command :stop, stop_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"stopping") do |vm| + vm.stop + end end end @@ -422,9 +477,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: RUNNING EOT - command :suspend, suspend_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"suspending") do |vm| - vm.suspend + command :suspend, suspend_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"suspending") do |vm| + vm.suspend + end end end @@ -434,9 +494,14 @@ cmd=CommandParser::CmdParser.new(ARGV) do States: STOPPED, SUSPENDED EOT - command :resume, resume_desc, [:range,:vmid_list] do - helper.perform_actions(args[0],options,"resuming") do |vm| - vm.resume + command :resume, resume_desc, [:range,:vmid_list], + :options => [OneVMHelper::SCHEDULE] do + if (!options[:schedule].nil?) + helper.schedule_actions(args[0], options, @comm_name) + else + helper.perform_actions(args[0],options,"resuming") do |vm| + vm.resume + end end end diff --git a/src/common/SConstruct b/src/common/SConstruct index 3b24d2edac..5f3867988a 100644 --- a/src/common/SConstruct +++ b/src/common/SConstruct @@ -25,7 +25,8 @@ source_files=[ 'ActionManager.cc', 'Attribute.cc', 'mem_collector.c', - 'SSLTools.cc' + 'SSLTools.cc', + 'Util.cc' ] # Build library diff --git a/src/common/Util.cc b/src/common/Util.cc new file mode 100644 index 0000000000..6a73eb4de5 --- /dev/null +++ b/src/common/Util.cc @@ -0,0 +1,53 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs */ +/* */ +/* 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 "Util.h" +#include + +using namespace std; + +string& one_util::toupper(string& st) +{ + transform(st.begin(),st.end(),st.begin(),(int(*)(int))toupper); + return st; +}; + +string& one_util::tolower(string& st) +{ + transform(st.begin(),st.end(),st.begin(),(int(*)(int))tolower); + return st; +}; + +string one_util::log_time(time_t the_time) +{ + char time_str[26]; + +#ifdef SOLARIS + ctime_r(&(the_time),time_str,sizeof(char)*26); +#else + ctime_r(&(the_time),time_str); +#endif + + time_str[24] = '\0'; // Get rid of final enter character + + return string(time_str); +}; + +string one_util::log_time() +{ + return log_time( time(0) ); +}; + diff --git a/src/nebula/SConstruct b/src/nebula/SConstruct index aef1674a7d..7d389a5159 100644 --- a/src/nebula/SConstruct +++ b/src/nebula/SConstruct @@ -25,7 +25,7 @@ lib_name='nebula_core' source_files=[ 'SystemDB.cc', 'Nebula.cc', - 'NebulaTemplate.cc', + 'NebulaTemplate.cc' ] # Build library diff --git a/src/oca/ruby/opennebula/virtual_machine.rb b/src/oca/ruby/opennebula/virtual_machine.rb index 94a21f22b6..ebf5be492b 100644 --- a/src/oca/ruby/opennebula/virtual_machine.rb +++ b/src/oca/ruby/opennebula/virtual_machine.rb @@ -155,6 +155,26 @@ module OpenNebula super(VM_METHODS[:update], new_template) end + # Returns the element in text form + # + # @param indent [true,false] indents the resulting string, defaults to true + # + # @return [String] The USER_TEMPLATE + def user_template_str(indent=true) + template_like_str('USER_TEMPLATE', indent) + end + + # Returns the element in XML form + # + # @return [String] The USER_TEMPLATE + def user_template_xml + if NOKOGIRI + @xml.xpath('TEMPLATE').to_s + else + @xml.elements['TEMPLATE'].to_s + end + end + # Initiates the instance of the VM on the target host. # diff --git a/src/onedb/3.8.1_to_3.9.80.rb b/src/onedb/3.8.1_to_3.9.80.rb index fec8fa19f4..5e160eb340 100644 --- a/src/onedb/3.8.1_to_3.9.80.rb +++ b/src/onedb/3.8.1_to_3.9.80.rb @@ -402,6 +402,10 @@ module Migrator ######################################################################## # Feature #1556: New elem USER_TEMPLATE + # + # Feature #1483: Move scheduling attributes + # /VM/TEMPLATE/REQUIREMENTS -> USER_TEMPLATE/SCHED_REQUIREMENTS + # /VM/TEMPLATE/RANK -> USER_TEMPLATE/SCHED_RANK ######################################################################## @db.run "ALTER TABLE vm_pool RENAME TO old_vm_pool;" @@ -410,7 +414,21 @@ module Migrator @db.fetch("SELECT * FROM old_vm_pool") do |row| doc = Document.new(row[:body]) - doc.root.add_element("USER_TEMPLATE") + user_template = doc.root.add_element("USER_TEMPLATE") + + doc.root.each_element("TEMPLATE") do |e| + elem = e.delete_element("REQUIREMENTS") + + if !elem.nil? + user_template.add_element("SCHED_REQUIREMENTS").text = elem.text + end + + elem = e.delete_element("RANK") + + if !elem.nil? + user_template.add_element("SCHED_RANK").text = elem.text + end + end @db[:vm_pool].insert( :oid => row[:oid], @@ -429,6 +447,53 @@ module Migrator @db.run "DROP TABLE old_vm_pool;" + ######################################################################## + # Feature #1483: Move scheduling attributes + # /VMTEMPLATE/TEMPLATE/REQUIREMENTS -> /VMTEMPLATE/TEMPLATE/SCHED_REQUIREMENTS + # /VMTEMPLATE/TEMPLATE/RANK -> /VMTEMPLATE/TEMPLATE/SCHED_RANK + ######################################################################## + + @db.run "ALTER TABLE template_pool RENAME TO old_template_pool;" + @db.run "CREATE TABLE template_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body TEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER);" + + @db.fetch("SELECT * FROM old_template_pool") do |row| + + doc = Document.new(row[:body]) + + template = nil + + doc.root.each_element("TEMPLATE") do |e| + template = e + end + + doc.root.each_element("TEMPLATE") do |e| + elem = e.delete_element("REQUIREMENTS") + + if !elem.nil? + template.add_element("SCHED_REQUIREMENTS").text = elem.text + end + + elem = e.delete_element("RANK") + + if !elem.nil? + template.add_element("SCHED_RANK").text = elem.text + end + end + + @db[:template_pool].insert( + :oid => row[:oid], + :name => row[:name], + :body => doc.root.to_s, + :uid => row[:uid], + :gid => row[:gid], + :owner_u => row[:owner_u], + :group_u => row[:group_u], + :other_u => row[:other_u]) + end + + @db.run "DROP TABLE old_template_pool;" + + ######################################################################## # # Banner for the new /var/lib/one/vms directory diff --git a/src/scheduler/include/Scheduler.h b/src/scheduler/include/Scheduler.h index c6ef296a6b..0059aad843 100644 --- a/src/scheduler/include/Scheduler.h +++ b/src/scheduler/include/Scheduler.h @@ -50,6 +50,7 @@ protected: hpool(0), clpool(0), vmpool(0), + vmapool(0), acls(0), timer(0), url(""), @@ -79,6 +80,11 @@ protected: delete vmpool; } + if ( vmapool != 0) + { + delete vmapool; + } + if ( acls != 0) { delete acls; @@ -94,11 +100,13 @@ protected: // Pools // --------------------------------------------------------------- - HostPoolXML * hpool; - ClusterPoolXML * clpool; - VirtualMachinePoolXML * vmpool; + HostPoolXML * hpool; + ClusterPoolXML * clpool; - AclXML * acls; + VirtualMachinePoolXML * vmpool; + VirtualMachineActionsPoolXML* vmapool; + + AclXML * acls; // --------------------------------------------------------------- // Scheduler Policies @@ -133,6 +141,9 @@ protected: */ virtual int set_up_pools(); + + virtual int do_scheduled_actions(); + private: Scheduler(Scheduler const&){}; diff --git a/src/scheduler/include/VirtualMachinePoolXML.h b/src/scheduler/include/VirtualMachinePoolXML.h index 02f3ac8989..257f5b3dc2 100644 --- a/src/scheduler/include/VirtualMachinePoolXML.h +++ b/src/scheduler/include/VirtualMachinePoolXML.h @@ -32,7 +32,7 @@ public: bool _live_resched): PoolXML(client, machines_limit), live_resched(_live_resched){}; - ~VirtualMachinePoolXML(){}; + virtual ~VirtualMachinePoolXML(){}; /** * Retrieves the pending and rescheduling VMs @@ -66,12 +66,16 @@ public: * Update the VM template * @param vid the VM id * @param st the template string + * + * @return 0 on success, -1 otherwise */ int update(int vid, const string &st) const; /** * Update the VM template * @param the VM + * + * @return 0 on success, -1 otherwise */ int update(VirtualMachineXML * vm) const { @@ -86,14 +90,62 @@ protected: { return get_nodes("/VM_POOL/VM[STATE=1 or (LCM_STATE=3 and RESCHED=1)]", content); - }; + } virtual void add_object(xmlNodePtr node); virtual int load_info(xmlrpc_c::value &result); - /* Do live migrations to resched VMs*/ + /** + * Do live migrations to resched VMs + */ bool live_resched; }; +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualMachineActionsPoolXML : public VirtualMachinePoolXML +{ +public: + + VirtualMachineActionsPoolXML(Client* client, + unsigned int machines_limit): + VirtualMachinePoolXML(client, machines_limit, false){}; + + virtual ~VirtualMachineActionsPoolXML(){}; + + /** + * Retrieves the VMs with pending actions + * + * @return 0 on success + * -1 on error + * -2 if no VMs with pending actions + */ + int set_up(); + + /** + * Calls one.vm.action + * + * @param vid The VM id + * @param action Action argument (shutdown, hold, release...) + * @param error_msg Error reason, if any + * + * @return 0 on success, -1 otherwise + */ + int action(int vid, const string &action, string &error_msg) const; + +protected: + + int get_suitable_nodes(vector& content) + { + ostringstream oss; + + oss << "/VM_POOL/VM/USER_TEMPLATE/SCHED_ACTION[TIME < " << time(0) + << " and not(DONE > 0)]/../.."; + + return get_nodes(oss.str().c_str(), content); + } +}; + #endif /* VM_POOL_XML_H_ */ diff --git a/src/scheduler/include/VirtualMachineXML.h b/src/scheduler/include/VirtualMachineXML.h index 3261d4a012..29e714e085 100644 --- a/src/scheduler/include/VirtualMachineXML.h +++ b/src/scheduler/include/VirtualMachineXML.h @@ -124,6 +124,37 @@ public: return xml_str; } + /** + * Removes (but does not delete) the scheduled actions of the VM + * + * @param attributes to hold the VM actions + */ + void get_actions(vector& attributes) const + { + attributes.clear(); + + vm_template->remove("SCHED_ACTION", attributes); + } + + /** + * Sets an attribute in the VM Template, it must be allocated in the heap + * + * @param attributes to hold the VM actions + */ + void set_attribute(Attribute* att) + { + return vm_template->set(att); + } + + /** + * Checks the action to be performed and returns the corresponding XML-RPC + * method name. + * @param action_st, the action to be performed. The XML-RPC name is + * returned here + * @return 0 on success. + */ + static int parse_action_name(string& action_st); + /** * Function to write a Virtual Machine in an output stream */ diff --git a/src/scheduler/src/pool/VirtualMachinePoolXML.cc b/src/scheduler/src/pool/VirtualMachinePoolXML.cc index c48b131e82..ec9ff9eb63 100644 --- a/src/scheduler/src/pool/VirtualMachinePoolXML.cc +++ b/src/scheduler/src/pool/VirtualMachinePoolXML.cc @@ -209,3 +209,76 @@ int VirtualMachinePoolXML::update(int vid, const string &st) const return 0; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineActionsPoolXML::set_up() +{ + ostringstream oss; + int rc; + + rc = PoolXML::set_up(); + + if ( rc == 0 ) + { + if (objects.empty()) + { + return -2; + } + + oss.str(""); + oss << "VMs with scheduled actions:" << endl; + + map::iterator it; + + for (it=objects.begin();it!=objects.end();it++) + { + oss << " " << it->first; + } + + NebulaLog::log("VM",Log::DEBUG,oss); + } + + return rc; +} +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineActionsPoolXML::action( + int vid, + const string& action, + string& error_msg) const +{ + xmlrpc_c::value result; + bool success; + + try + { + client->call( client->get_endpoint(), // serverUrl + "one.vm.action", // methodName + "ssi", // arguments format + &result, // resultP + client->get_oneauth().c_str(), // session + action.c_str(), // action + vid // VM ID + ); + } + catch (exception const& e) + { + return -1; + } + + vector values = + xmlrpc_c::value_array(result).vectorValueValue(); + + success = xmlrpc_c::value_boolean(values[0]); + + if (!success) + { + error_msg = xmlrpc_c::value_string( values[1] ); + + return -1; + } + + return 0; +} diff --git a/src/scheduler/src/pool/VirtualMachineXML.cc b/src/scheduler/src/pool/VirtualMachineXML.cc index fd4d3561a0..3ef22c174c 100644 --- a/src/scheduler/src/pool/VirtualMachineXML.cc +++ b/src/scheduler/src/pool/VirtualMachineXML.cc @@ -17,6 +17,7 @@ #include #include "VirtualMachineXML.h" +#include "Util.h" void VirtualMachineXML::init_attributes() { @@ -51,7 +52,7 @@ void VirtualMachineXML::init_attributes() cpu = 0; } - result = ((*this)["/VM/TEMPLATE/RANK"]); + result = ((*this)["/VM/USER_TEMPLATE/SCHED_RANK"]); if (result.size() > 0) { @@ -59,18 +60,42 @@ void VirtualMachineXML::init_attributes() } else { - rank = ""; + // Compatibility with previous versions + result = ((*this)["/VM/USER_TEMPLATE/RANK"]); + + if (result.size() > 0) + { + rank = result[0]; + } + else + { + rank = ""; + } } - result = ((*this)["/VM/TEMPLATE/REQUIREMENTS"]); + result = ((*this)["/VM/TEMPLATE/AUTOMATIC_REQUIREMENTS"]); if (result.size() > 0) { requirements = result[0]; } - else + + result = ((*this)["/VM/USER_TEMPLATE/SCHED_REQUIREMENTS"]); + + if (result.size() > 0) { - requirements = ""; + if ( !requirements.empty() ) + { + ostringstream oss; + + oss << requirements << " & ( " << result[0] << " )"; + + requirements = oss.str(); + } + else + { + requirements = result[0]; + } } result = ((*this)["/VM/HISTORY_RECORDS/HISTORY/HID"]); @@ -255,21 +280,42 @@ void VirtualMachineXML::log(const string &st) { return; } + ostringstream oss; - char str[26]; - time_t the_time = time(NULL); - - ostringstream oss; - -#ifdef SOLARIS - ctime_r(&(the_time),str,sizeof(char)*26); -#else - ctime_r(&(the_time),str); -#endif - - str[24] = '\0'; // Get rid of final enter character - - oss << str << " : " << st; + oss << one_util::log_time() << " : " << st; vm_template->replace("SCHED_MESSAGE", oss.str()); } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineXML::parse_action_name(string& action_st) +{ + one_util::tolower(action_st); + + // onevm delete command uses the xml-rpc finalize action + if (action_st == "delete") + { + action_st = "finalize"; + } + + if ( action_st != "shutdown" + && action_st != "hold" + && action_st != "release" + && action_st != "stop" + && action_st != "cancel" + && action_st != "suspend" + && action_st != "resume" + && action_st != "restart" + && action_st != "resubmit" + && action_st != "reboot" + && action_st != "reset" + && action_st != "poweroff" + && action_st != "finalize") + { + return -1; + } + + return 0; +}; diff --git a/src/scheduler/src/sched/Scheduler.cc b/src/scheduler/src/sched/Scheduler.cc index 43ac4a69f9..50b4e81235 100644 --- a/src/scheduler/src/sched/Scheduler.cc +++ b/src/scheduler/src/sched/Scheduler.cc @@ -34,6 +34,7 @@ #include "RankPolicy.h" #include "NebulaLog.h" #include "PoolObjectAuth.h" +#include "Util.h" using namespace std; @@ -214,6 +215,8 @@ void Scheduler::start() vmpool = new VirtualMachinePoolXML(client, machines_limit, (live_rescheds == 1)); + vmapool= new VirtualMachineActionsPoolXML(client, machines_limit); + acls = new AclXML(client); // ----------------------------------------------------------- @@ -465,7 +468,7 @@ void Scheduler::match() matched = false; n_error++; - error_msg << "Error evaluating REQUIREMENTS expression: '" + error_msg << "Error evaluating SCHED_REQUIREMENTS expression: '" << reqs << "', error: " << error; oss << "VM " << oid << ": " << error_msg.str(); @@ -488,7 +491,7 @@ void Scheduler::match() ostringstream oss; oss << "VM " << oid << ": Host " << host->get_hid() << - " filtered out. It does not fulfill REQUIREMENTS."; + " filtered out. It does not fulfill SCHED_REQUIREMENTS."; NebulaLog::log("SCHED",Log::DEBUG,oss); continue; @@ -537,7 +540,7 @@ void Scheduler::match() } else if (n_matched == 0) { - vm->log("No host meets the REQUIREMENTS expression"); + vm->log("No host meets the SCHED_REQUIREMENTS expression"); } else { @@ -658,12 +661,117 @@ void Scheduler::dispatch() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int Scheduler::do_scheduled_actions() +{ + VirtualMachineXML* vm; + + const map vms = vmapool->get_objects(); + map::const_iterator vm_it; + + vector attributes; + vector::iterator it; + + VectorAttribute* vatt; + + int action_time; + int done_time; + int has_time; + int has_done; + + string action_st, error_msg; + + time_t the_time = time(0); + string time_str = one_util::log_time(the_time); + + for (vm_it=vms.begin(); vm_it != vms.end(); vm_it++) + { + vm = static_cast(vm_it->second); + + vm->get_actions(attributes); + + // TODO: Sort actions by TIME + for (it=attributes.begin(); it != attributes.end(); it++) + { + vatt = dynamic_cast(*it); + + if (vatt == 0) + { + if ( *it != 0 ) + { + delete *it; + } + + continue; + } + + has_time = vatt->vector_value("TIME", action_time); + has_done = vatt->vector_value("DONE", done_time); + action_st = vatt->vector_value("ACTION"); + + if (has_time == 0 && has_done == -1 && action_time < the_time) + { + ostringstream oss; + + int rc = VirtualMachineXML::parse_action_name(action_st); + + oss << "Executing action '" << action_st << "' for VM " + << vm->get_oid() << " : "; + + if ( rc != 0 ) + { + error_msg = "This action is not supported."; + } + else + { + rc = vmapool->action(vm->get_oid(), action_st, error_msg); + } + + if (rc == 0) + { + vatt->remove("MESSAGE"); + vatt->replace("DONE", static_cast(the_time)); + + oss << "Success."; + } + else + { + ostringstream oss_aux; + + oss_aux << time_str << " : " << error_msg; + + vatt->replace("MESSAGE", oss_aux.str()); + + oss << "Failure. " << error_msg; + } + + NebulaLog::log("VM", Log::INFO, oss); + } + + vm->set_attribute(vatt); + } + + vmpool->update(vm); + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void Scheduler::do_action(const string &name, void *args) { int rc; if (name == ACTION_TIMER) { + rc = vmapool->set_up(); + + if ( rc == 0 ) + { + do_scheduled_actions(); + } + rc = set_up_pools(); if ( rc != 0 ) diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 4adf0b1b44..7d79d4ce53 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -63,14 +63,17 @@ VirtualMachine::VirtualMachine(int id, { if (_vm_template != 0) { - obj_template = _vm_template; + // This is a VM Template, with the root TEMPLATE. + _vm_template->set_xml_root("USER_TEMPLATE"); + + user_obj_template = _vm_template; } else { - obj_template = new VirtualMachineTemplate; + user_obj_template = new Template(false,'=',"USER_TEMPLATE"); } - user_obj_template = new Template(false,'=',"USER_TEMPLATE"); + obj_template = new VirtualMachineTemplate; set_umask(umask); } @@ -257,13 +260,24 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) oss << oid; value = oss.str(); - replace_template_attribute("VMID", value); + user_obj_template->erase("VMID"); + obj_template->add("VMID", value); - get_template_attribute("NAME",name); + user_obj_template->get("TEMPLATE_ID", value); + user_obj_template->erase("TEMPLATE_ID"); + + if (!value.empty()) + { + obj_template->add("TEMPLATE_ID", value); + } + + user_obj_template->get("NAME",name); + user_obj_template->erase("NAME"); if (name.empty() == true) { - get_template_attribute("TEMPLATE_NAME", prefix); + user_obj_template->get("TEMPLATE_NAME", prefix); + user_obj_template->erase("TEMPLATE_NAME"); if (prefix.empty()) { @@ -273,8 +287,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) oss.str(""); oss << prefix << "-" << oid; name = oss.str(); - - replace_template_attribute("NAME", name); } else if (name.length() > 128) { @@ -287,26 +299,35 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) // Check for CPU, VCPU and MEMORY attributes // ------------------------------------------------------------------------ - if ( get_template_attribute("MEMORY", ivalue) == false || ivalue <= 0 ) + if ( user_obj_template->get("MEMORY", ivalue) == false || ivalue <= 0 ) { goto error_memory; } - if ( get_template_attribute("CPU", fvalue) == false || fvalue <= 0 ) + user_obj_template->erase("MEMORY"); + obj_template->add("MEMORY", ivalue); + + if ( user_obj_template->get("CPU", fvalue) == false || fvalue <= 0 ) { goto error_cpu; } + user_obj_template->erase("CPU"); + obj_template->add("CPU", fvalue); + // VCPU is optional, first check if the attribute exists, then check it is // an integer - get_template_attribute("VCPU", value); + user_obj_template->get("VCPU", value); if ( value.empty() == false ) { - if ( get_template_attribute("VCPU", ivalue) == false || ivalue <= 0 ) + if ( user_obj_template->get("VCPU", ivalue) == false || ivalue <= 0 ) { goto error_vcpu; } + + user_obj_template->erase("VCPU"); + obj_template->add("VCPU", ivalue); } // ------------------------------------------------------------------------ @@ -532,7 +553,14 @@ int VirtualMachine::parse_os(string& error_str) vector os_attr; VectorAttribute * os; - num = obj_template->get("OS", os_attr); + vector::iterator it; + + num = user_obj_template->remove("OS", os_attr); + + for (it=os_attr.begin(); it != os_attr.end(); it++) + { + obj_template->set(*it); + } if ( num == 0 ) { @@ -724,7 +752,14 @@ void VirtualMachine::parse_graphics() vector array_graphics; VectorAttribute * graphics; - num = obj_template->get("GRAPHICS", array_graphics); + vector::iterator it; + + num = user_obj_template->remove("GRAPHICS", array_graphics); + + for (it=array_graphics.begin(); it != array_graphics.end(); it++) + { + obj_template->set(*it); + } if ( num == 0 ) { @@ -771,7 +806,16 @@ int VirtualMachine::parse_requirements(string& error_str) string parsed; - num = obj_template->remove("REQUIREMENTS", array_reqs); + num = user_obj_template->remove("SCHED_REQUIREMENTS", array_reqs); + + if ( num == 0 ) // Compatibility with old REQUIREMENTS attribute + { + num = user_obj_template->remove("REQUIREMENTS", array_reqs); + } + else + { + user_obj_template->erase("REQUIREMENTS"); + } if ( num == 0 ) { @@ -779,7 +823,7 @@ int VirtualMachine::parse_requirements(string& error_str) } else if ( num > 1 ) { - error_str = "Only one REQUIREMENTS attribute can be defined."; + error_str = "Only one SCHED_REQUIREMENTS attribute can be defined."; goto error_cleanup; } @@ -787,7 +831,7 @@ int VirtualMachine::parse_requirements(string& error_str) if ( reqs == 0 ) { - error_str = "Wrong format for REQUIREMENTS attribute."; + error_str = "Wrong format for SCHED_REQUIREMENTS attribute."; goto error_cleanup; } @@ -797,19 +841,13 @@ int VirtualMachine::parse_requirements(string& error_str) { SingleAttribute * reqs_parsed; - reqs_parsed = new SingleAttribute("REQUIREMENTS",parsed); - obj_template->set(reqs_parsed); + reqs_parsed = new SingleAttribute("SCHED_REQUIREMENTS",parsed); + user_obj_template->set(reqs_parsed); } - /* --- Delete old requirements attributes --- */ + /* --- Delete old requirements attribute --- */ - for (int i = 0; i < num ; i++) - { - if (array_reqs[i] != 0) - { - delete array_reqs[i]; - } - } + delete array_reqs[0]; return rc; @@ -943,14 +981,7 @@ int VirtualMachine::automatic_requirements(string& error_str) oss.str(""); oss << "CLUSTER_ID = " << cluster_id; - obj_template->get("REQUIREMENTS", requirements); - - if ( !requirements.empty() ) - { - oss << " & ( " << requirements << " )"; - } - - replace_template_attribute("REQUIREMENTS", oss.str()); + obj_template->add("AUTOMATIC_REQUIREMENTS", oss.str()); } return 0; @@ -1310,11 +1341,23 @@ int VirtualMachine::get_disk_images(string& error_str) Nebula& nd = Nebula::instance(); ipool = nd.get_ipool(); + vector::iterator it; + // ------------------------------------------------------------------------- // The context is the first of the cdroms // ------------------------------------------------------------------------- - num_context = obj_template->get("CONTEXT", context_disks); - num_disks = obj_template->get("DISK", disks); + num_context = user_obj_template->remove("CONTEXT", context_disks); + num_disks = user_obj_template->remove("DISK", disks); + + for (it=context_disks.begin(); it != context_disks.end(); it++) + { + obj_template->set(*it); + } + + for (it=disks.begin(); it != disks.end(); it++) + { + obj_template->set(*it); + } if ( num_disks > 20 ) { @@ -1429,11 +1472,11 @@ error_duplicated_target: error_common: ImageManager * imagem = nd.get_imagem(); - vector::iterator it; + vector::iterator img_it; - for ( it=acquired_images.begin() ; it < acquired_images.end(); it++ ) + for ( img_it=acquired_images.begin() ; img_it < acquired_images.end(); img_it++ ) { - imagem->release_image(oid, *it, false); + imagem->release_image(oid, *img_it, false); } return -1; @@ -1770,7 +1813,12 @@ int VirtualMachine::get_network_leases(string& estr) Nebula& nd = Nebula::instance(); vnpool = nd.get_vnpool(); - num_nics = obj_template->get("NIC",nics); + num_nics = user_obj_template->remove("NIC",nics); + + for (vector::iterator it=nics.begin(); it != nics.end(); it++) + { + obj_template->set(*it); + } for(int i=0; i