1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-07 17:17:41 +03:00

Merge branch 'feature-1548'

Conflicts:
	src/sunstone/public/app/tabs/templates-tab/form-panels/instantiate.js
This commit is contained in:
Ruben S. Montero 2018-05-08 15:45:05 +02:00
commit f9699aed19
21 changed files with 1380 additions and 333 deletions

203
include/ScheduledAction.h Normal file
View File

@ -0,0 +1,203 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2018, OpenNebula Project, OpenNebula Systems */
/* */
/* 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 SCHED_ACTION_ATTRIBUTE_H_
#define SCHED_ACTION_ATTRIBUTE_H_
#include <set>
#include "VirtualMachineAttribute.h"
/**
* The VirtualMachine SCHED_ACTION attribute
*/
class SchedAction: public VirtualMachineAttribute
{
public:
enum Repeat
{
NONE = -1,
WEEKLY = 0,
MONTHLY = 1,
YEARLY = 2,
HOURLY = 3
};
enum EndOn
{
NEVER = 0,
TIMES = 1,
DATE = 2
};
SchedAction(VectorAttribute *va, int id):VirtualMachineAttribute(va, id){};
virtual ~SchedAction(){};
/**
* Returns the REPEAT value of the SCHED_ACTION
* @param r repeat WEEKLY, MONTHLY, YEARLY or HOURLY
* @return -1 if REPEAT not found or in wrong format 0 on success
*/
int repeat(Repeat& r);
/**
* Returns the END_TYPE value of the SCHED_ACTION
* @param eo ends on TIMES WEEKLY, MONTHLY, YEARLY or HOURLY
* @return -1 if REPEAT not found or in wrong format 0 on success
*/
int endon(EndOn& eo);
/**
* Parse the DAYS attribute of the sched action
* @param d set of days (unique)
* @return -1 if not found (d will be empty) 0 otherwise
*/
int days(std::set<int>& _d);
/**
* This function checks that the DAYS attributes are in range
* according to the Repeat value:
* @param r repeat type (hourly, weekly...)
* @param error in case of error
*
* @return true if days are in range false (error) if not
*/
bool days_in_range(Repeat r, std::string& error);
/**
* This function checks that END_VALUE is properly defined for the
* associated END_TYPE
* @param eo end type (date, times)
* @param error in case of error
*
* @return true if days are in range false (error) if not
*/
bool ends_in_range(EndOn eo, std::string& error);
/**
* This function parse and checks the sched action attributes: REPEAT, DAYS
* , END_TYPE, END_DATE. It also removed DONE and MESSAGE.
* @param error
* @return 0 if success -1 otherwise
*/
int parse(std::string& error);
/**
* @return true if the action needs to be executed.
*/
bool is_due();
/**
* Compute the next action, updating the TIME attribute for this action
* @return -1 if action ended 0 otherwise
*/
int next_action();
};
/**
* Set of VirtualMachine SCHED_ACTION attributes
*/
class SchedActions : public VirtualMachineAttributeSet
{
public:
/* ---------------------------------------------------------------------- */
/* Constructor and Initialization functions */
/* ---------------------------------------------------------------------- */
/**
* Creates the SchedAction set from a Template with SCHED_ACTION=[...]
* attributes, id's are auto-assigned for internal use
* @param tmpl template with DISK
*/
SchedActions(Template * tmpl): VirtualMachineAttributeSet(false)
{
std::vector<VectorAttribute *> vas;
tmpl->get("SCHED_ACTION", vas);
init_attribute_map("TIME", vas);
};
virtual ~SchedActions(){};
/* ---------------------------------------------------------------------- */
/**
* Parse the ScheduleActions of a template
* @param error
* @return -1 in case of error 0 otherwise
*/
int parse(std::string& error)
{
for ( schedaction_iterator action = begin(); action != end(); ++action)
{
if ( (*action)->parse(error) == -1 )
{
return -1;
}
}
return 0;
}
bool empty()
{
return a_set.empty();
}
/* ---------------------------------------------------------------------- */
/* Iterators */
/* ---------------------------------------------------------------------- */
/**
* Generic iterator for the SchedActions set.
*/
class SchedActionIterator : public AttributeIterator
{
public:
SchedActionIterator():AttributeIterator(){};
SchedActionIterator(const AttributeIterator& i):AttributeIterator(i){};
virtual ~SchedActionIterator(){};
SchedAction * operator*() const
{
return static_cast<SchedAction *>(map_it->second);
}
};
SchedActionIterator begin()
{
SchedActionIterator it(ExtendedAttributeSet::begin());
return it;
}
SchedActionIterator end()
{
SchedActionIterator it(ExtendedAttributeSet::end());
return it;
}
typedef class SchedActionIterator schedaction_iterator;
protected:
VirtualMachineAttribute * attribute_factory(VectorAttribute * va,
int id) const
{
return new SchedAction(va, id);
};
};
#endif /*SCHED_ACTION_ATTRIBUTE_H_*/

View File

@ -170,7 +170,7 @@ private:
/**
* This method removes sched_action DONE/MESSAGE attributes
*/
void parse_sched_action();
int parse_sched_action(string& error_str);
protected:

View File

@ -828,15 +828,31 @@ EOT
end
end
def OpenNebulaHelper.time_to_str(time, print_seconds=true)
def OpenNebulaHelper.time_to_str(time, print_seconds=true, print_hours=true, print_years=false)
value=time.to_i
if value==0
value='-'
else
if print_seconds
value=Time.at(value).strftime("%m/%d %H:%M:%S")
if print_hours
if print_seconds
if print_years
value=Time.at(value).strftime("%m/%d/%y %H:%M:%S")
else
value=Time.at(value).strftime("%m/%d %H:%M:%S")
end
else
if print_years
value=Time.at(value).strftime("%m/%d/%y %H:%M")
else
value=Time.at(value).strftime("%m/%d %H:%M")
end
end
else
value=Time.at(value).strftime("%m/%d %H:%M")
if print_years
value=Time.at(value).strftime("%m/%d/%y")
else
value=Time.at(value).strftime("%m/%d")
end
end
end

View File

@ -114,6 +114,45 @@ class OneVMHelper < OpenNebulaHelper::OneHelper
:format => Time
}
WEEKLY = {
:name => "weekly",
:large => "--weekly days",
:description => "Schedules this action to be executed after" \
"the given time. For example: onevm resume 0 --schedule \"09/23 14:15\"",
:format => String
}
MONTHLY = {
:name => "monthly",
:large => "--monthly days",
:description => "Schedules this action to be executed after" \
"the given time. For example: onevm resume 0 --schedule \"09/23 14:15\"",
:format => String
}
YEARLY = {
:name => "yearly",
:large => "--yearly days",
:description => "Schedules this action to be executed after" \
"the given time. For example: onevm resume 0 --schedule \"09/23 14:15\"",
:format => String
}
HOURLY = {
:name => "hourly",
:large => "--hourly hour",
:description => "Schedules this action to be executed after" \
"the given time. For example: onevm resume 0 --schedule \"09/23 14:15\"",
:format => Numeric
}
END_TIME = {
:name => "end",
:large => "--end number|TIME",
:description => "----",
:format => String
}
ALL_TEMPLATE = {
:name => "all",
:large => "--all",
@ -314,6 +353,31 @@ class OneVMHelper < OpenNebulaHelper::OneHelper
ids, options,
"#{action} scheduled at #{options[:schedule]}") do |vm|
str_periodic = ""
if options.key?(:weekly)
str_periodic << ", REPEAT = 0, DAYS = \"#{options[:weekly]}\""
elsif options.key?(:monthly)
str_periodic << ", REPEAT = 1, DAYS = \"#{options[:monthly]}\""
elsif options.key?(:yearly)
str_periodic << ", REPEAT = 2, DAYS = \"#{options[:yearly]}\""
elsif options.key?(:hourly)
str_periodic << ", REPEAT = 3, DAYS = \"#{options[:hourly].to_s}\""
end
if options.key?(:end)
begin
end_date = Date.parse(options[:end])
str_periodic << ", END_TYPE = 2, END_VALUE = #{end_date.to_time.to_i}"
rescue ArgumentError
if options[:end].to_i > 0
str_periodic << ", END_TYPE = 1, END_VALUE = #{options[:end].to_i}"
end
end
elsif str_periodic != ""
str_periodic << ", END_TYPE = 0"
end
rc = vm.info
if OpenNebula.is_error?(rc)
@ -331,7 +395,7 @@ class OneVMHelper < OpenNebulaHelper::OneHelper
tmp_str = vm.user_template_str
tmp_str << "\nSCHED_ACTION = [ID = #{id}, ACTION = #{action}, TIME = #{options[:schedule].to_i}]"
tmp_str << "\nSCHED_ACTION = [ID = #{id}, ACTION = #{action}, TIME = #{options[:schedule].to_i}" << str_periodic << "]"
vm.update(tmp_str)
end
@ -998,6 +1062,39 @@ in the frontend machine.
OpenNebulaHelper.time_to_str(d["TIME"], false) if !d.nil?
end
column :"REPEAT", "", :size=>20 do |d|
str_rep = ""
if !d.nil? && d.key?("REPEAT")
if d["REPEAT"] == "0"
str_rep << "Weekly "
elsif d["REPEAT"] == "1"
str_rep << "Monthly "
elsif d["REPEAT"] == "2"
str_rep << "Yearly "
elsif d["REPEAT"] == "3"
str_rep << "Each " << d['DAYS'] << " hours"
end
if d["REPEAT"] != "3"
str_rep << d["DAYS"]
end
end
str_rep if !d.nil?
end
column :"END", "", :size=>20 do |d|
str_end = ""
if !d.nil? && d.key?("END_TYPE")
if d["END_TYPE"] == "0"
str_end << "None"
elsif d["END_TYPE"] == "1"
str_end << "After " << d["END_VALUE"] << " times"
elsif d["END_TYPE"] == "2"
str_end << "On " << OpenNebulaHelper.time_to_str(d["END_VALUE"], false, false, true)
end
end
str_end if !d.nil?
end
column :"DONE", "", :size=>12 do |d|
OpenNebulaHelper.time_to_str(d["DONE"], false) if !d.nil?
end

View File

@ -316,7 +316,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :hold, hold_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME] do
if (!options[:schedule].nil?)
helper.schedule_actions(args[0], options, @comm_name)
else
@ -333,7 +333,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :release, release_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME] do
if (!options[:schedule].nil?)
helper.schedule_actions(args[0], options, @comm_name)
else
@ -391,7 +391,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :terminate, terminate_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE, OneVMHelper::HARD] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME, OneVMHelper::HARD] do
command_name='terminate'
command_name<<'-hard' if options[:hard]
@ -414,7 +414,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :undeploy, undeploy_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE, OneVMHelper::HARD] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME, OneVMHelper::HARD] do
command_name='undeploy'
command_name<<'-hard' if options[:hard]
@ -436,7 +436,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :poweroff, poweroff_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE, OneVMHelper::HARD] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME, OneVMHelper::HARD] do
command_name='poweroff'
command_name<<'-hard' if options[:hard]
@ -460,7 +460,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :reboot, reboot_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE, OneVMHelper::HARD] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME, OneVMHelper::HARD] do
command_name='reboot'
command_name<<'-hard' if options[:hard]
@ -525,7 +525,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :stop, stop_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME] do
if (!options[:schedule].nil?)
helper.schedule_actions(args[0], options, @comm_name)
else
@ -545,7 +545,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :suspend, suspend_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME] do
if (!options[:schedule].nil?)
helper.schedule_actions(args[0], options, @comm_name)
else
@ -562,7 +562,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :resume, resume_desc, [:range,:vmid_list],
:options => [OneVMHelper::SCHEDULE] do
:options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME] do
if (!options[:schedule].nil?)
helper.schedule_actions(args[0], options, @comm_name)
else
@ -804,7 +804,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :"snapshot-create", snapshot_create_desc, [:range,:vmid_list],
[:name, nil], :options => [OneVMHelper::SCHEDULE] do
[:name, nil], :options => [OneVMHelper::SCHEDULE, OneVMHelper::WEEKLY, OneVMHelper::MONTHLY, OneVMHelper::YEARLY, OneVMHelper::HOURLY, OneVMHelper::END_TIME] do
if (!options[:schedule].nil?)
helper.schedule_actions(args[0], options, @comm_name)

View File

@ -25,6 +25,7 @@
#include "Resource.h"
#include "VirtualMachineTemplate.h"
#include "ScheduledAction.h"
class ImageDatastorePoolXML;
@ -325,11 +326,9 @@ public:
*
* @param attributes to hold the VM actions
*/
void get_actions(vector<Attribute *>& attributes) const
SchedActions * get_actions()
{
attributes.clear();
user_template->remove("SCHED_ACTION", attributes);
return new SchedActions(user_template);
}
/**

View File

@ -17,6 +17,7 @@
#include <algorithm>
#include "VirtualMachineXML.h"
#include "ScheduledAction.h"
#include "DatastoreXML.h"
#include "DatastorePoolXML.h"
#include "NebulaUtil.h"
@ -507,3 +508,6 @@ bool VirtualMachineXML::is_only_public_cloud() const
{
return only_public_cloud;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -39,6 +39,7 @@ sched_env.Prepend(LIBS=[
'nebula_common',
'nebula_core',
'nebula_template',
'nebula_vmtemplate',
'nebula_vm',
'nebula_host',
'nebula_parsers',

View File

@ -34,6 +34,7 @@
#include "NebulaLog.h"
#include "PoolObjectAuth.h"
#include "NebulaUtil.h"
#include "ScheduledAction.h"
using namespace std;
@ -1381,86 +1382,74 @@ int Scheduler::do_scheduled_actions()
const map<int, ObjectXML*> vms = vmapool->get_objects();
map<int, ObjectXML*>::const_iterator vm_it;
vector<Attribute *> attributes;
vector<Attribute *>::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);
string time_str = one_util::log_time(time(0));
for (vm_it=vms.begin(); vm_it != vms.end(); vm_it++)
{
vm = static_cast<VirtualMachineXML*>(vm_it->second);
SchedActions::schedaction_iterator action;
vm->get_actions(attributes);
vm = static_cast<VirtualMachineXML *>(vm_it->second);
// TODO: Sort actions by TIME
for (it=attributes.begin(); it != attributes.end(); it++)
SchedActions * sas = vm->get_actions();
for ( action = sas->begin(); action != sas->end(); ++action)
{
vatt = dynamic_cast<VectorAttribute*>(*it);
ostringstream oss;
if (vatt == 0)
if (!(*action)->is_due())
{
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");
action_st = (*action)->vector_value("ACTION");
if (has_time == 0 && has_done == -1 && action_time < the_time)
int rc = VirtualMachineXML::parse_action_name(action_st);
oss << "Executing action '" << action_st << "' for VM "
<< vm->get_oid() << " : ";
if ( rc != 0 )
{
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);
}
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<int>(the_time));
(*action)->remove("MESSAGE");
(*action)->replace("DONE", time(0));
(*action)->next_action();
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);
if ( rc != 0 )
{
ostringstream oss_aux;
oss_aux << time_str << " : " << error_msg;
(*action)->replace("MESSAGE", oss_aux.str());
oss << "Failure. " << error_msg;
}
NebulaLog::log("VM", Log::INFO, oss);
}
vmpool->update(vm);
if ( sas->empty() != 0 ) //Do not update VMs without SCHED_ACTION
{
vmpool->update(vm);
}
delete sas;
}
return 0;

View File

@ -20,6 +20,7 @@ define(function(require) {
*/
var Config = require('sunstone-config');
var ScheduleActions = require('utils/schedule_action');
var Locale = require('utils/locale');
var Tips = require('utils/tips');
var WizardFields = require('utils/wizard-fields');
@ -59,7 +60,9 @@ define(function(require) {
return WizardTab;
function _html() {
return TemplateHTML();
return TemplateHTML({
'table_sched_actions': ScheduleActions.htmlTable("temp")
});
}
function _onShow(context, panelForm) {
@ -68,103 +71,39 @@ define(function(require) {
function _setup(context) {
var that = this;
var actions = ["terminate", "terminate-hard", "hold", "release", "stop", "suspend", "resume", "reboot", "reboot-hard", "poweroff", "poweroff-hard", "undeploy", "undeploy-hard", "snapshot-create"];
context.off('click', '#add_scheduling_temp_action');
context.on('click', '#add_scheduling_temp_action', function() {
$("#add_scheduling_temp_action", context).attr("disabled", "disabled");
var html = '<tr>\
<td></td>\
<td>\
<select id="select_new_action" class="select_new_action" name="select_action">';
$.each(actions, function(key, action){
var actionAux = action.replace("-", "_");
if (Config.isTabActionEnabled("vms-tab", "VM." + actionAux)){
html += '<option value="' + action + '">' + Locale.tr(action) + '</option>';
}
});
html += '</select>\
</td>\
<td>\
<input id="date_input" type="date" placeholder="2013/12/30"/>\
<input id="time_input" type="time" placeholder="12:30"/>\
</td>\
<td>\
<button id="add_temp_action_json" class="button small secondary radius" >' + Locale.tr("Add") + '</button>\
</td>\
<td colspan=2></td>\
</tr>';
$("#scheduling_temp_actions_table").append(html);
ScheduleActions.htmlNewAction(actions, context, "temp");
ScheduleActions.setup(context)
return false;
});
context.off("click", "#add_temp_action_json");
context.on("click" , "#add_temp_action_json", function(){
var date_input_value = $("#date_input", context).val();
var time_input_value = $("#time_input", context).val();
if (date_input_value == "" || time_input_value == ""){
return false;
var sched_action = ScheduleActions.retrieveNewAction(context);
if (sched_action != false) {
$("#sched_temp_actions_body").append(ScheduleActions.fromJSONtoActionsTable(sched_action));
}
var time_value = date_input_value + ' ' + time_input_value;
var epoch_str = new Date(time_value);
var time = parseInt(epoch_str.getTime()) / 1000;
var new_action = $("#select_new_action", context).val();
var sched_action = {};
sched_action.ACTION = new_action;
sched_action.TIME = time;
$(this).parents('tr').remove();
$("#add_scheduling_temp_action", context).removeAttr("disabled");
$("#sched_temp_actions_body").append(Actions.fromJSONtoActionsTable(sched_action));
return false;
});
context.on("focusout" , "#time_input", function(){
$("#time_input").removeAttr("data-invalid");
$("#time_input").removeAttr("class");
context.off("click", ".remove_action_x");
context.on("click", ".remove_action_x", function () {
$(this).parents('tr').remove();
});
context.off("click", ".remove_action_x");
context.on("click", ".remove_action_x", function(){
$(this).parents('tr').remove();
});
}
function _retrieve(context) {
var templateJSON = {};
var actionsJSON = [];
$("#scheduling_temp_actions_table tbody tr").each(function(index){
var first = $(this).children("td")[0];
if(!$('select', first).html()){
var actionJSON = {};
actionJSON.ID = index;
$(this).children("td").each(function(index2){
if(index2 == 0)
actionJSON.ACTION = $(this).text();
else if (index2 == 1){
var pretty_time = $(this).text();
pretty_time = pretty_time.split(' ');
var date = Actions.convertDate(pretty_time[1]);
var time_value = date + ' ' + pretty_time[0];
var epoch_str = new Date(time_value);
var time = parseInt(epoch_str.getTime()) / 1000;
actionJSON.TIME = time;
}
});
}
if (!$.isEmptyObject(actionJSON)) {actionsJSON.push(actionJSON)};
});
templateJSON['SCHED_ACTION'] = actionsJSON;
templateJSON['SCHED_ACTION'] = ScheduleActions.retrieve(context);
return templateJSON;
}
function _fill(context, templateJSON) {
var actions = Actions.fromJSONtoActionsTable(templateJSON.SCHED_ACTION);
var actions = ScheduleActions.fromJSONtoActionsTable(templateJSON.SCHED_ACTION);
$("#sched_temp_actions_body").append(actions);
delete templateJSON['SCHED_ACTION'];
}

View File

@ -1,15 +1,5 @@
<div class="row">
<div class="large-12 columns">
<table id="scheduling_temp_actions_table" class="info_table dataTable">
<thead>
<tr>
<th> {{tr "Action"}} </th>
<th> {{tr "Time"}} </th>
<th colspan=""> {{tr "Actions"}} </th>
<th><button id="add_scheduling_temp_action" class="button small success right radius"> {{tr "Add action"}} </button></th>
</tr>
</thead>
<tbody id="sched_temp_actions_body"></tbody>
</table>
{{{table_sched_actions}}}
</div>
</div>

View File

@ -43,7 +43,7 @@ define(function(require) {
var Humanize = require("utils/humanize");
var TemplateUtils = require("utils/template-utils");
var UniqueId = require("utils/unique-id");
var Actions = require("utils/actions");
var ScheduleActions = require("utils/schedule_action");
/*
CONSTANTS
@ -128,54 +128,20 @@ define(function(require) {
context.on("click", "#add_scheduling_inst_action", function() {
var actions = ["terminate", "terminate-hard", "hold", "release", "stop", "suspend", "resume", "reboot", "reboot-hard", "poweroff", "poweroff-hard", "undeploy", "undeploy-hard", "snapshot-create"];
$("#add_scheduling_inst_action", context).attr("disabled", "disabled");
var html = "<tr>\
<td></td>\
<td>\
<select id=\"select_new_action\" class=\"select_new_action\" name=\"select_action\">";
$.each(actions, function(key, action){
var actionAux = action.replace("-", "_");
if (Config.isTabActionEnabled("vms-tab", "VM." + actionAux)){
html += "<option value=\"" + action + "\">" + Locale.tr(action) + "</option>";
}
});
html += "</select>\
</td>\
<td>\
<input id=\"date_input\" type=\"date\" placeholder=\"2013/12/30\"/>\
<input id=\"time_input\" type=\"time\" placeholder=\"12:30\"/>\
</td>\
<td>\
<button id=\"add_inst_action_json\" class=\"button small secondary radius\" >" + Locale.tr("Add") + "</button>\
</td>\
<td colspan=2></td>\
</tr>";
$("#scheduling_inst_actions_table").append(html);
ScheduleActions.htmlNewAction(actions, context, "inst");
ScheduleActions.setup(context)
return false;
});
context.off("click", "#add_inst_action_json");
context.on("click" , "#add_inst_action_json", function(){
var date_input_value = $("#date_input", context).val();
var time_input_value = $("#time_input", context).val();
if (date_input_value == "" || time_input_value == ""){
return false;
var sched_action = ScheduleActions.retrieveNewAction(context);
if (sched_action != false) {
$("#sched_inst_actions_body").append(ScheduleActions.fromJSONtoActionsTable(sched_action));
}
var time_value = date_input_value + " " + time_input_value;
var epoch_str = new Date(time_value);
var time = parseInt(epoch_str.getTime()) / 1000;
var new_action = $("#select_new_action", context).val();
var sched_action = {};
sched_action.ACTION = new_action;
sched_action.TIME = time;
$(this).parents("tr").remove();
$("#add_scheduling_inst_action", context).removeAttr("disabled");
$("#sched_inst_actions_body").append(Actions.fromJSONtoActionsTable(sched_action));
return false;
});
@ -338,32 +304,11 @@ define(function(require) {
}
}
var templateJSON = {};
var actionsJSON = [];
tmp_json['SCHED_ACTION'] = ScheduleActions.retrieve(context);
$("#scheduling_inst_actions_table tbody tr").each(function(index){
var first = $(this).children("td")[0];
if(!$("select", first).html()){
var actionJSON = {};
actionJSON.ID = index;
$(this).children("td").each(function(index2){
if(index2 == 0)
actionJSON.ACTION = $(this).text();
else if (index2 == 1){
var pretty_time = $(this).text();
pretty_time = pretty_time.split(" ");
var date = Actions.convertDate(pretty_time[1]);
var time_value = date + " " + pretty_time[0];
var epoch_str = new Date(time_value);
var time = parseInt(epoch_str.getTime()) / 1000;
actionJSON.TIME = time;
}
});
}
if (!$.isEmptyObject(actionJSON)) {actionsJSON.push(actionJSON);};
});
tmp_json["SCHED_ACTION"] = actionsJSON;
if (tmp_json['SCHED_ACTION'].length == 0) {
delete tmp_json['SCHED_ACTION'];
}
capacityContext = $(".capacityContext" + template_id, context);
$.extend(tmp_json, CapacityInputs.retrieveChanges(capacityContext));
@ -442,7 +387,8 @@ define(function(require) {
hostsDatatable: that.hostsTable.dataTableHTML,
dsDatatable: that.datastoresTable.dataTableHTML,
usersDatatable: that.usersTable.dataTableHTML,
groupDatatable: that.groupTable.dataTableHTML
groupDatatable: that.groupTable.dataTableHTML,
table_sched_actions: ScheduleActions.htmlTable("inst")
}) );
$(".provision_host_selector" + template_json.VMTEMPLATE.ID, context).data("hostsTable", that.hostsTable);
@ -450,7 +396,7 @@ define(function(require) {
$(".provision_uid_selector" + template_json.VMTEMPLATE.ID, context).data("usersTable", that.usersTable);
$(".provision_gid_selector" + template_json.VMTEMPLATE.ID, context).data("groupTable", that.groupTable);
var actions = Actions.fromJSONtoActionsTable(template_json.VMTEMPLATE.TEMPLATE.SCHED_ACTION);
var actions = ScheduleActions.fromJSONtoActionsTable(template_json.VMTEMPLATE.TEMPLATE.SCHED_ACTION);
$("#sched_inst_actions_body").append(actions);
var selectOptions = {

View File

@ -137,17 +137,7 @@
<legend>
<i class="fas fa-calendar-alt"></i> {{tr "Schedule Actions for VM"}}
</legend>
<table id="scheduling_inst_actions_table" class="info_table dataTable">
<thead>
<tr>
<th> {{tr "Action"}} </th>
<th> {{tr "Time"}} </th>
<th colspan=""> {{tr "Actions"}} </th>
<th><button id="add_scheduling_inst_action" class="button small success right radius"> {{tr "Add action"}} </button></th>
</tr>
</thead>
<tbody id="sched_inst_actions_body"></tbody>
</table>
{{{table_sched_actions}}}
</fieldset>
</div>
</div>

View File

@ -24,6 +24,7 @@ define(function(require) {
var Humanize = require('utils/humanize');
var TemplateUtils = require('utils/template-utils');
var Config = require('sunstone-config');
var ScheduleActions = require('utils/schedule_action');
/*
CONSTANTS
@ -62,99 +63,59 @@ define(function(require) {
var that = this;
var html = '<div class="row">\
<div class="large-12 columns">\
<table id="scheduling_actions_table" class="info_table dataTable">\
<table id="scheduling_vms_actions_table" class="info_table dataTable">\
<thead>\
<tr>\
<th>' + Locale.tr("ID") + '</th>\
<th>' + Locale.tr("Action") + '</th>\
<th>' + Locale.tr("Time") + '</th>\
<th>' + Locale.tr("Rep") + '</th>\
<th>' + Locale.tr("End") + '</th>\
<th>' + Locale.tr("Done") + '</th>\
<th>' + Locale.tr("Message") + '</th>\
<th colspan="">' + Locale.tr("Actions") + '</th>\
<th><button id="add_scheduling_action" class="button small success right radius" >' + Locale.tr("Add action") + '</button></th>\
<th><button id="add_scheduling_vms_action" class="button small success right radius" >' + Locale.tr("Add action") + '</button></th>\
</tr>\
</thead>' +
fromJSONtoActionsTable(that.element.USER_TEMPLATE.SCHED_ACTION) +
'</table>\
</thead>\
<tbody id="sched_vms_actions_body">'+
vmsfromJSONtoActionsTable(that.element.USER_TEMPLATE.SCHED_ACTION) +
'</tbody>\
</table>\
</div>\
</div>';
ScheduleActions.htmlTable("vms") //only charge the resource attribute
return html;
}
function _setup(context) {
var that = this;
var actions = ["terminate", "terminate-hard", "hold", "release", "stop", "suspend", "resume", "reboot", "reboot-hard", "poweroff", "poweroff-hard", "undeploy", "undeploy-hard", "snapshot-create"];
context.off('click', '#add_scheduling_action');
context.on('click', '#add_scheduling_action', function() {
$("#add_scheduling_action", context).attr("disabled", "disabled");
var html = '<tr>\
<td></td>\
<td>\
<select id="select_new_action" class="select_new_action" name="select_action">';
$.each(actions, function(key, action){
var actionAux = action.replace("-", "_");
if (Config.isTabActionEnabled("vms-tab", "VM." + actionAux)){
html += '<option value="' + action + '">' + Locale.tr(action) + '</option>';
}
});
html += '</select>\
</td>\
<td>\
<input id="date_input" type="date" placeholder="2013/12/30"/>\
<input id="time_input" type="time" placeholder="12:30"/>\
</td>\
<td>\
<button id="submit_scheduling_action" class="button small secondary radius" >' + Locale.tr("Add") + '</button>\
</td>\
<td colspan=2></td>\
</tr>';
$("#scheduling_actions_table").append(html);
context.off('click', '#add_scheduling_vms_action');
context.on('click', '#add_scheduling_vms_action', function() {
$("#add_scheduling_vms_action", context).attr("disabled", "disabled");
ScheduleActions.htmlNewAction(actions, context, "vms");
ScheduleActions.setup(context)
return false;
});
context.off("click", "#submit_scheduling_action");
context.on("click", "#submit_scheduling_action", function() {
var date_input_value = $("#date_input", context).val();
var time_input_value = $("#time_input", context).val();
if (date_input_value == "" || time_input_value == "")
return false;
var time_value = date_input_value + ' ' + time_input_value
// Calculate MAX_ID
var max_id = -1;
if (that.element.USER_TEMPLATE.SCHED_ACTION) {
if (!that.element.USER_TEMPLATE.SCHED_ACTION.length) {
var tmp_element = that.element.USER_TEMPLATE.SCHED_ACTION;
that.element.USER_TEMPLATE.SCHED_ACTION = new Array();
that.element.USER_TEMPLATE.SCHED_ACTION.push(tmp_element);
}
$.each(that.element.USER_TEMPLATE.SCHED_ACTION, function(i, element) {
if (max_id < element.ID)
max_id = element.ID
})
context.off("click", "#add_vms_action_json");
context.on("click" , "#add_vms_action_json", function(){
var sched_action = ScheduleActions.retrieveNewAction(context);
if (sched_action != false) {
$("#no_actions_tr", context).remove();
$("#sched_vms_actions_body").append(ScheduleActions.fromJSONtoActionsTable(sched_action));
} else {
that.element.USER_TEMPLATE.SCHED_ACTION = new Array();
return false;
}
var new_action = {};
new_action.ID = parseInt(max_id) + 1;
new_action.ACTION = $("#select_new_action", context).val();
var epoch_str = new Date(time_value);
new_action.TIME = parseInt(epoch_str.getTime()) / 1000;
that.element.USER_TEMPLATE.SCHED_ACTION.push(new_action);
that.element.USER_TEMPLATE.SCHED_ACTION = ScheduleActions.retrieve(context);
// Let OpenNebula know
var template_str = TemplateUtils.templateToString(that.element.USER_TEMPLATE);
Sunstone.runAction("VM.update_template", that.element.ID, template_str);
$("#add_scheduling_action", context).removeAttr("disabled");
$("#add_scheduling_vms_action", context).removeAttr("disabled");
return false;
});
@ -179,7 +140,7 @@ define(function(require) {
}
// Returns an HTML string with the json keys and values
function fromJSONtoActionsTable(actions_array) {
function vmsfromJSONtoActionsTable(actions_array) {
var str = ""
var empty = '\
<tr id="no_actions_tr">\
@ -201,24 +162,22 @@ define(function(require) {
}
$.each(actions_array, function(index, scheduling_action) {
str += fromJSONtoActionRow(scheduling_action);
str += "<tr class='tr_action_"+scheduling_action.ID+"' data='" + JSON.stringify(scheduling_action) + "'>";
str += '<td class="id_row">' + TemplateUtils.htmlEncode(scheduling_action.ID) + '</td>'
var actions = ScheduleActions.fromJSONtoActionsTable([scheduling_action], scheduling_action.ID, true);
str += actions;
str += vmsfromJSONtoActionRow(scheduling_action);
});
return str;
}
// Helper for fromJSONtoHTMLTable function
function fromJSONtoActionRow(scheduling_action) {
function vmsfromJSONtoActionRow(scheduling_action) {
var done_str = scheduling_action.DONE ? (Humanize.prettyTime(scheduling_action.DONE)) : "";
var message_str = scheduling_action.MESSAGE ? scheduling_action.MESSAGE : "";
var time_str = Humanize.prettyTime(scheduling_action.TIME);
var str = "";
str += '<tr class="tr_action_' + scheduling_action.ID + '">\
<td class="id_row">' + TemplateUtils.htmlEncode(scheduling_action.ID) + '</td>\
<td class="action_row">' + TemplateUtils.htmlEncode(scheduling_action.ACTION) + '</td>\
<td nowrap class="time_row">' + time_str + '</td>\
<td class="done_row">' + done_str + '</td>\
var str = '<td class="done_row">' + done_str + '</td>\
<td class="message_row">' + TemplateUtils.htmlEncode(message_str) + '</td>\
<td>\
<div>\

View File

@ -62,6 +62,8 @@ define(function(require) {
}
};
var week_days_str = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
/*
CONSTRUCTOR
*/
@ -80,7 +82,8 @@ define(function(require) {
'prettyTimeAgo': _format_date,
'prettyTimeDatatable': _prettyTimeDatatable,
'lock_to_str': _lock_to_str,
'state_lock_to_color': _state_lock_to_color
'state_lock_to_color': _state_lock_to_color,
'week_days': _week_days
}
/*
@ -392,4 +395,13 @@ define(function(require) {
return '<span style="'+show_lock+' float:left; margin-right: 3px; width: 5px; height: 20px; background: '+color+';"></span>'
}
function _week_days(days){
var days = days.split(",");
var str = "";
days.forEach(function(day){
str += week_days_str[day] + ",";
});
return str.slice(0, -1);
}
})

View File

@ -0,0 +1,342 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2018, OpenNebula Project, OpenNebula Systems */
/* */
/* 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. */
/* -------------------------------------------------------------------------- */
define(function (require) {
var Config = require("sunstone-config");
var Locale = require("utils/locale");
var Humanize = require("utils/humanize");
var TemplateUtils = require("utils/template-utils");
var Tips = require('utils/tips');
var TemplateHTML = require("hbs!./schedule_action/html");
var TemplateTableHTML = require("hbs!./schedule_action/table");
function _html(resource) {
this.res = resource;
return TemplateTableHTML({
res: resource
});
}
function _htmlNewAction(actions, context, res) {
var options = "";
var that = this;
$.each(actions, function (key, action) {
var actionAux = action.replace("-", "_");
if (Config.isTabActionEnabled("vms-tab", "VM." + actionAux)) {
options += "<option value=\"" + action + "\">" + Locale.tr(action) + "</option>";
}
});
$("#scheduling_" + res + "_actions_table tbody", context).append(TemplateHTML({
"actions": options,
"res": that.res
}));
if (res === "vms"){
$("#title", context).prop("colspan", "2");
$("#td_days", context).prop("colspan", "5");
}
}
function _setup(context) {
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
if (dd < 10) {
dd = "0" + dd;
}
if (mm < 10) {
mm = "0" + mm;
}
today = yyyy + "-" + mm + "-" + dd;
$("#date_input", context).attr("min", today);
$("#date_input", context).attr("value", today);
$(".periodic", context).hide();
$("input#schedule_type", context).on("change", function () {
var periodic = $(this).prop("checked");
if (periodic) {
$(".periodic", context).show();
$(".non-periodic", context).hide();
} else {
$(".periodic", context).hide();
$(".non-periodic", context).show();
}
});
var that = this;
this.rep = "week";
this.end_type = "never";
$("select[name='repeat']", context).change(function () {
var value = $(this).val();
that.repeat = value;
var input_html = "";
switch (value) {
case "week":
input_html = "<div id=\"days_week_value\" style=\"margin: 10px 0 10px 0;\">\
<input type=\"checkbox\" id=\"mon\" name=\"days\" value=\"0\"><label for=\"mon\">" + Locale.tr("Mo") + "</label>\
<input type=\"checkbox\" id=\"tue\" name=\"days\" value=\"1\"><label for=\"tue\">" + Locale.tr("Tu") + "</label>\
<input type=\"checkbox\" id=\"wed\" name=\"days\" value=\"2\"><label for=\"wed\">" + Locale.tr("We") + "</label>\
<input type=\"checkbox\" id=\"thu\" name=\"days\" value=\"3\"><label for=\"thu\">" + Locale.tr("Th") + "</label>\
<input type=\"checkbox\" id=\"fri\" name=\"days\" value=\"4\"><label for=\"fri\">" + Locale.tr("Fr") + "</label>\
<input type=\"checkbox\" id=\"sat\" name=\"days\" value=\"5\"><label for=\"sat\">" + Locale.tr("Sa") + "</label>\
<input type=\"checkbox\" id=\"sun\" name=\"days\" value=\"6\"><label for=\"sun\">" + Locale.tr("Su") + "</label>\
</div>";
break;
case "month":
input_html = "<div style=\"display: -webkit-box;\"><input style=\"margin-right: 4px;\" id=\"days_month_value\" type=\"text\" placeholder=\"1,31\"/>\
<span class=\"tip\">"+Locale.tr("Comma separated list of days of the month to repeat the action on. Ex: 1,15,25 repeats the action every first, 15th and 25th day of the month")+" </span></div>";
break;
case "year":
input_html = "<div style=\"display: -webkit-box;\"><input style=\"margin-right: 4px;\" id=\"days_year_value\" type=\"text\" placeholder=\"0,365\"/>\
<span class=\"tip\">"+Locale.tr("Comma separated list of days of the year to repeat the action on. Ex: 1,30,330 repeats the action every first, 30th and 330th day of the year")+" </span></div>";
break;
case "hour":
input_html = "<div style=\"display: -webkit-box;\">\
<label style=\"margin-right: 5px;\">"+ Locale.tr("Each")+"</label>\
<input style=\"margin-right: 4px;\" id=\"days_hour_value\" min=\"0\" max=\"168\" type=\"number\" placeholder=\"5\"/>\
<label> "+ Locale.tr("hours")+"</label>\
</div>";
break;
}
$("#td_days").html(input_html);
Tips.setup(context);
});
$("input[name='end_type']", context).change(function () {
var value = $(this).val();
that.end_type = value;
var input_html = "";
var min;
$(".end_input", context).prop('disabled', true);
switch (value) {
case "n_rep":
min = 1;
break;
case "date":
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
if (dd < 10) {
dd = "0" + dd
}
if (mm < 10) {
mm = "0" + mm
}
min = yyyy + "-" + mm + "-" + dd;
break;
}
$("#end_value_" + value, context).attr("min", min);
$("#end_value_" + value, context).prop('disabled', false);
});
context.on("focusout", "#time_input", function () {
$("#time_input").removeAttr("data-invalid");
$("#time_input").removeAttr("class");
});
}
function _retrieve(context) {
var actionsJSON = [];
$("#scheduling_" + this.res + "_actions_table tbody tr").each(function (index) {
var first = $(this).children("td")[0];
if (!$("select", first).html()) {
var actionJSON = {};
actionJSON = JSON.parse($(this).attr("data"));
actionJSON.ID = index;
}
if (!$.isEmptyObject(actionJSON)) { actionsJSON.push(actionJSON) };
});
return actionsJSON;
}
function _retrieveNewAction(context) {
var periodic = $("input#schedule_type", context).prop("checked");
var time_input_value = $("#time_input", context).val();
var date_input_value = $("#date_input", context).val();
var new_action = $("#select_new_action", context).val();
var rep = 0;
var end_type = 1;
var days = "";
var end_value = 1;
var sched_action = {};
if (time_input_value == "" || date_input_value == "") {
return false;
}
if (periodic) {
if (!this.repeat || !this.end_type) {
return false;
}
if (this.repeat == "week") {
$("input[name='days']:checked").each(function () {
days = days + (this).value + ",";
});
days = days.slice(0, -1);
} else if (this.repeat == "month") {
rep = 1;
days = $("#days_month_value", context).val();
} else if (this.repeat == "year"){
rep = 2;
days = $("#days_year_value", context).val();
} else {
rep = 3;
days = $("#days_hour_value", context).val();
}
if (days == "") {
return false;
}
if (this.end_type == "never") {
end_type = 0;
} else if (this.end_type == "n_rep") {
end_value = $("#end_value_n_rep", context).val();
if (end_value == "") {
return false;
}
} else if (this.end_type == "date") {
end_type = 2;
end_date = $("#end_value_date", context).val();
var time_value = end_date + " " + time_input_value;
var epoch_str = new Date(time_value);
end_value = parseInt(epoch_str.getTime()) / 1000;
if (end_value == "") {
return false;
}
}
sched_action.DAYS = days;
sched_action.REPEAT = rep;
sched_action.END_TYPE = end_type;
sched_action.END_VALUE = end_value;
}
var time_value = date_input_value + " " + time_input_value;
var epoch_str = new Date(time_value);
var time = parseInt(epoch_str.getTime()) / 1000;
sched_action.ACTION = new_action;
sched_action.TIME = time;
$("#scheduling_" + this.res + "_actions_table .create", context).remove();
$("#add_scheduling_" + this.res + "_action", context).removeAttr("disabled");
return sched_action;
}
function _fromJSONtoActionsTable(actions_array, action_id, minus) {
var str = "";
if (!actions_array) {
return "";
}
if (!$.isArray(actions_array)) {
var tmp_array = new Array();
tmp_array[0] = actions_array;
actions_array = tmp_array;
}
if (!actions_array.length) {
return "";
}
$.each(actions_array, function (index, scheduling_action) {
str += _fromJSONtoActionRow(scheduling_action, action_id, minus);
});
return str;
}
function _fromJSONtoActionRow(scheduling_action, action_id, minus) {
var time_str = Humanize.prettyTime(scheduling_action.TIME);
var rep_str = "";
var end_str = "";
if (scheduling_action.REPEAT != undefined) {
if (scheduling_action.REPEAT == 0) {
rep_str = "Weekly ";
} else if (scheduling_action.REPEAT == 1) {
rep_str = "Monthly ";
} else if (scheduling_action.REPEAT == 2) {
rep_str = "Yearly ";
} else if (scheduling_action.REPEAT == 3) {
rep_str = "Each " + scheduling_action.DAYS + " hours";
}
if (scheduling_action.REPEAT != 3) {
if (scheduling_action.REPEAT != 0) {
rep_str += scheduling_action.DAYS;
} else {
rep_str += Humanize.week_days(scheduling_action.DAYS);
}
}
}
if (scheduling_action.END_TYPE != undefined) {
if (scheduling_action.END_TYPE == 0) {
end_str = "None";
} else if (scheduling_action.END_TYPE == 1) {
end_str = "After " + scheduling_action.END_VALUE + " times";
} else if (scheduling_action.END_TYPE == 2) {
end_str = "on " + Humanize.prettyTime(scheduling_action.END_VALUE);
}
}
var str = "";
if (action_id == undefined) {
str += "<tr class='tr_action' data='" + JSON.stringify(scheduling_action) + "'>";
}
str += "<td class='action_row'>" + TemplateUtils.htmlEncode(scheduling_action.ACTION) + "</td>\
<td nowrap class='time_row'>" + time_str + "</td>\
<td nowrap class='rep_row'>" + rep_str + "</td>\
<td nowrap class='end_row'>" + end_str + "</td>";
if (minus == undefined) {
str += "<td>\
<div>\
<a id='minus' class='remove_action_x' href='#'><i class='fa fa-trash-o'/></a>\
</div>\
</td>\
</tr>";
}
return str;
}
function convertDate(date_string) {
date_string = date_string.split("/");
return date_string[2] + "-" + date_string[1] + "-" + date_string[0];
}
return {
"fromJSONtoActionRow": _fromJSONtoActionRow,
"fromJSONtoActionsTable": _fromJSONtoActionsTable,
"htmlNewAction": _htmlNewAction,
"setup": _setup,
"htmlTable": _html,
"retrieveNewAction": _retrieveNewAction,
"retrieve": _retrieve
};
});

View File

@ -0,0 +1,98 @@
{{! -------------------------------------------------------------------------- }} {{! Copyright 2002-2018, OpenNebula Project,
OpenNebula Systems }} {{! }} {{! 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. }} {{! --------------------------------------------------------------------------
}}
<tr class="create">
<td id="title">
<h6 class="title_label">{{tr "New scheduled action"}}:</h6>
</td>
</tr>
<tr id="tr_schedule_base" class="create">
<td>
<select style="width: 80%" id="select_new_action" class="select_new_action" name="select_action">{{{actions}}}</select>
</td>
<td>
<input style="width: 80%" id="date_input" type="date" />
</td>
<td>
<input style="width: 80%" id="time_input" type="time" placeholder="12:30" />
</td>
<td>
<input id="schedule_type" type="checkbox">
<label>{{tr "Periodic"}}</label>
</td>
</tr>
<tr class="periodic create">
<td>
<label class="title_label">{{tr "Repeat"}}: </label>
</td>
<td>
<select style="width: 75%" id="repeat" name="repeat">
<option value="week">{{tr "Weekly"}}</option>
<option value="month">{{tr "Monthly"}}</option>
<option value="year">{{tr "Yearly"}}</option>
<option value="hour">{{tr "Hourly"}}</option>
</select>
</td>
<td id="td_days" colspan="3">
<div id="days_week_value" style="margin: 10px 0 10px 0;">
<input type="checkbox" id="mon" name="days" value="0">
<label for="mon">{{tr "Mo"}}</label>
<input type="checkbox" id="tue" name="days" value="1">
<label for="tue">{{tr "Tu"}}</label>
<input type="checkbox" id="wed" name="days" value="2">
<label for="wed">{{tr "We"}}</label>
<input type="checkbox" id="thu" name="days" value="3">
<label for="thu">{{tr "Th"}}</label>
<input type="checkbox" id="fri" name="days" value="4">
<label for="fri">{{tr "Fr"}}</label>
<input type="checkbox" id="sat" name="days" value="5">
<label for="sat">{{tr "Sa"}}</label>
<input type="checkbox" id="sun" name="days" value="6">
<label for="sun">{{tr "Su"}}</label>
</div>
</td>
</tr>
<tr class="periodic create">
<td>
<label class="title_label">{{tr "Ends"}}: </label>
</td>
</tr>
<tr class="periodic create">
<td>
<input type="radio" name="end_type" id="end_type_ever" value="never" checked>
<label for="end_type_ever">{{tr "Never"}}</label>
</td>
</tr>
<tr class="periodic create">
<td>
<input type="radio" name="end_type" id="end_type_n_rep" value="date">
<label for="end_type_date">{{tr "On"}}</label>
</td>
<td id="td_end_value_date">
<input class="end_input" id="end_value_date" type="date" disabled/>
</td>
</tr>
<tr class="periodic create">
<td>
<input type="radio" name="end_type" id="end_type_n_rep" value="n_rep">
<label for="end_type_n_rep">{{tr "After"}}</label>
</td>
<td id="td_end_value_nrep">
<input class="end_input" id="end_value_n_rep" type="number" placeholder="1" disabled/>
</td>
<td>
<label for="end_value_date">{{tr "times"}}</label>
</td>
</tr>
<br>
<tr class="create">
<td>
<button style="margin-top: 15px;" id="add_{{res}}_action_json" class="button small success secondary radius">{{tr "Add"}}</button>
</td>
</tr>

View File

@ -0,0 +1,38 @@
{{! -------------------------------------------------------------------------- }}
{{! Copyright 2002-2018, OpenNebula Project, OpenNebula Systems }}
{{! }}
{{! 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. }}
{{! -------------------------------------------------------------------------- }}
<style>
.title_label{
font-weight: bold;
color:#555;
}
</style>
<table id="scheduling_{{res}}_actions_table" class="info_table dataTable">
<thead>
<tr>
<th> {{tr "Action"}} </th>
<th> {{tr "Time"}} </th>
<th> {{tr "Rep"}} </th>
<th> {{tr "Ends"}} </th>
<th colspan=""> {{tr "Actions"}} </th>
<th>
<button id="add_scheduling_{{res}}_action" class="button small success right radius"> {{tr "Add action"}} </button>
</th>
</tr>
</thead>
<tbody id="sched_{{res}}_actions_body"></tbody>
</table>

View File

@ -23,7 +23,8 @@ lib_name='nebula_vmtemplate'
# Sources to generate the library
source_files=[
'VMTemplate.cc',
'VMTemplatePool.cc'
'VMTemplatePool.cc',
'ScheduledAction.cc'
]
# Build library

View File

@ -0,0 +1,422 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2018, OpenNebula Project, OpenNebula Systems */
/* */
/* 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 "ScheduledAction.h"
#include "NebulaUtil.h"
int SchedAction::repeat(Repeat& r)
{
r = NONE;
std::string rep_s = vector_value("REPEAT");
if ( rep_s.empty() )
{
return 0;
}
std::istringstream iss(rep_s);
int v_r;
iss >> v_r;
if (iss.fail() || !iss.eof())
{
return -1;
}
if ( v_r < WEEKLY || v_r > HOURLY )
{
return -1;
}
r = static_cast<Repeat>(v_r);
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int SchedAction::endon(EndOn& eo)
{
eo = NEVER;
std::string et_s = vector_value("END_TYPE");
if ( et_s.empty() )
{
return 0;
}
std::istringstream iss(et_s);
int v_eo;
iss >> v_eo;
if (iss.fail() || !iss.eof())
{
return -1;
}
if ( v_eo < TIMES || v_eo > DATE )
{
return -1;
}
eo = static_cast<EndOn>(v_eo);
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int SchedAction::days(std::set<int>& _d)
{
std::string st;
int rc = vector_value("DAYS", st);
if ( rc != 0 )
{
return 0;
}
one_util::split_unique<int>(st, ',', _d);
if ( _d.empty() )
{
return -1;
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool SchedAction::days_in_range(Repeat r, std::string& error)
{
static const char * e[] = {
"Days in a week have to be in [0,6] range", //WEEKLY - 0
"Days in a month have to be in [0,31] range", // MONTHLY - 1
"Days in a year have to be in [0,365] range", // YEARLY - 2
"Hours have to be in [0,168] range" // HOURLY - 3
};
static int fday[] = {0,1,0,1};
static int lday[] = {7,32,366,168};
bool extra_check;
std::set<int> _d;
if ( days(_d) == -1 )
{
error = "Wrong format of DAYS attribute";
return false;
}
else if ( _d.empty() )
{
return true;
}
int _fday = *(_d.begin());
int _lday = *(_d.rbegin());
switch(r)
{
case WEEKLY:
case MONTHLY:
case YEARLY:
extra_check = false;
break;
case HOURLY:
extra_check = _d.size() != 1;
break;
case NONE:
return false;
}
if ( _fday < fday[r] || _lday >= lday[r] || extra_check )
{
error = e[r];
return false;
}
return true;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool SchedAction::ends_in_range(EndOn eo, std::string& error)
{
int end_value;
int rc = vector_value("END_VALUE", end_value);
if ( rc == -1 )
{
error = "Missing END_VALUE";
return false;
}
if ( eo == TIMES && end_value <= 0 )
{
error = "Error parsing END_VALUE, times has to be greater than 0";
return false;
}
else if ( eo == DATE )
{
struct tm val_tm;
localtime_r((time_t *)&end_value, &val_tm);
time_t out = mktime(&val_tm);
if (out == -1)
{
error = "Error parsing END_VALUE, wrong format for date.";
return false;
}
}
return true;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int SchedAction::parse(std::string& error)
{
Repeat r;
EndOn eo;
if ( repeat(r) == -1 )
{
error = "Error parsing REPEAT attribute";
return -1;
}
if ( endon(eo) == -1 )
{
error = "Error parsing END_TYPE attribute";
return -1;
}
if ( !days_in_range(r, error) )
{
return -1;
}
if ( !ends_in_range(eo, error) )
{
return -1;
}
remove("DONE");
remove("MESSAGE");
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int days_in_period(SchedAction::Repeat& r, int month, int year)
{
static map<int, int> MONTHS_DAYS = {{0, 31}, {1, 28}, {2, 31}, {3, 30},
{4, 31}, {5, 30}, {6, 31}, {7, 31}, {8, 30}, {9, 31}, {10, 30},
{11, 31}};
int leap_year = 0;
int leap_month = 0;
year += 1900;
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
{
leap_year = 1;
if ( month == 1 )
{
leap_month = 1;
}
}
switch(r)
{
case SchedAction::WEEKLY:
return 7;
//Return value for months assume month day starts in 0
case SchedAction::MONTHLY:
return MONTHS_DAYS[month] + leap_month;
case SchedAction::YEARLY:
return 365 + leap_year;
case SchedAction::HOURLY:
case SchedAction::NONE:
return 0;
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool SchedAction::is_due()
{
time_t action_time, done_time;
bool has_done = vector_value("DONE", done_time) == 0;
vector_value("TIME", action_time);
return ((!has_done || done_time < action_time) && action_time < time(0));
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int SchedAction::next_action()
{
Repeat r;
EndOn eo;
if ( repeat(r) == -1 )
{
return -1;
}
if ( endon(eo) == -1 )
{
return -1;
}
std::set<int> _days;
if ( days(_days) == -1 )
{
return -1;
}
else if ( _days.empty() )
{
return -1;
}
/* --------------------------------------------------------------------- */
/* Check if action is already finished */
/* --------------------------------------------------------------------- */
int end_value;
int rc = vector_value("END_VALUE", end_value);
if ( rc == -1 )
{
return -1;
}
if ( eo == TIMES )
{
if (end_value <= 0)
{
return -1;
}
replace("END_VALUE", end_value - 1);
}
else if ( eo == DATE )
{
if ( time(0) > end_value )
{
return -1;
}
}
/* --------------------------------------------------------------------- */
/* Compute next event for the action - HOURLY */
/* --------------------------------------------------------------------- */
time_t action_time;
vector_value("TIME", action_time);
if ( r == HOURLY )
{
action_time += *(_days.begin()) * 3600;
replace("TIME", action_time);
return 0;
}
/* --------------------------------------------------------------------- */
/* Compute next event for the action - WEEKLY, MONTHLY & YEARLY */
/* --------------------------------------------------------------------- */
time_t current = time(0);
struct tm current_tm;
int cday;
localtime_r(&current, &current_tm);
switch(r)
{
case WEEKLY:
cday = current_tm.tm_wday;
break;
case MONTHLY:
cday = current_tm.tm_mday;
break;
case YEARLY:
cday = current_tm.tm_yday;
break;
case HOURLY:
case NONE:
break;
}
std::set<int>::iterator it = _days.lower_bound(cday);
int delta = 0;
if (it == _days.end()) //after last day in range
{
int pdays = days_in_period(r, current_tm.tm_mon, current_tm.tm_year);
delta = pdays - cday + *(_days.begin()); //assume start day is 0
}
else if (cday < *(_days.begin())) //before first day in range
{
delta = *(_days.begin()) - cday;
}
else if (*it != cday ) //is not today
{
delta = *it - cday;
}
action_time += delta * 24 * 3600;
replace("TIME", action_time);
return 0;
}

View File

@ -15,8 +15,7 @@
/* ------------------------------------------------------------------------ */
#include "VMTemplate.h"
#define TO_UPPER(S) transform(S.begin(),S.end(),S.begin(),(int(*)(int))toupper)
#include "ScheduledAction.h"
/* ************************************************************************ */
/* VMTemplate :: Constructor/Destructor */
@ -74,9 +73,14 @@ int VMTemplate::insert(SqlDB *db, string& error_str)
erase_template_attribute("NAME", name);
// ---------------------------------------------------------------------
// Remove DONE/MESSAGE from SCHED_ACTION
// Remove DONE/MESSAGE from SCHED_ACTION and check rest attributes
// ---------------------------------------------------------------------
parse_sched_action();
int rc = parse_sched_action(error_str);
if (rc == -1)
{
return rc;
}
// ------------------------------------------------------------------------
// Insert the Template
@ -167,18 +171,11 @@ error_common:
return -1;
}
void VMTemplate::parse_sched_action()
int VMTemplate::parse_sched_action(string& error_str)
{
vector<VectorAttribute *> _sched_actions;
vector<VectorAttribute *>::iterator i;
SchedActions sactions(obj_template);
get_template_attribute("SCHED_ACTION", _sched_actions);
for ( i = _sched_actions.begin(); i != _sched_actions.end() ; ++i)
{
(*i)->remove("DONE");
(*i)->remove("MESSAGE");
}
return sactions.parse(error_str);
}
/* ------------------------------------------------------------------------ */
@ -186,7 +183,11 @@ void VMTemplate::parse_sched_action()
int VMTemplate::post_update_template(string& error)
{
parse_sched_action();
int rc = parse_sched_action(error);
if (rc == -1)
{
return rc;
}
return 0;
}