1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-11 04:58:16 +03:00

F #1548: SchedAction logic moved to its own class

This commit is contained in:
Ruben S. Montero 2018-05-07 00:54:47 +02:00
parent 9cace59933
commit 6e48575a1a
6 changed files with 677 additions and 264 deletions

193
include/ScheduledAction.h Normal file
View File

@ -0,0 +1,193 @@
/* -------------------------------------------------------------------------- */
/* 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
{
END_NONE = -1,
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);
/**
* 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("", 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;
}
/* ---------------------------------------------------------------------- */
/* 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

@ -17,13 +17,12 @@
#include <algorithm>
#include "VirtualMachineXML.h"
#include "ScheduledAction.h"
#include "DatastoreXML.h"
#include "DatastorePoolXML.h"
#include "NebulaUtil.h"
#include "History.h"
map<int, int> m_months_days = {{0, 31}, {1, 28}, {2, 30}, {3, 31}, {4, 30}, {5, 31}, {6, 30}, {7, 31}, {8, 30}, {9, 31}, {10, 30}, {11, 31}};
void VirtualMachineXML::init_attributes()
{
vector<xmlNodePtr> nodes;
@ -505,180 +504,12 @@ bool VirtualMachineXML::is_only_public_cloud() const
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void check_months(struct tm * next){
int days_month = m_months_days[next->tm_mon];
if(next->tm_year+1900 % 4 == 0 && (next->tm_year+1900 % 100 != 0 || next->tm_year+1900 % 400 == 0) && next->tm_mon == 1)
{
days_month++;
}
if (next->tm_mday > days_month)
{
next->tm_mday = next->tm_mday - m_months_days[next->tm_mon];
next->tm_mon++;
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void sum_days(struct tm * next, struct tm * now, int mayor_day, int minor_day, int max_day, int comparative){
if (mayor_day >= 0 && minor_day < max_day)
{
if( mayor_day <= comparative ) //next
{
next->tm_mday = next->tm_mday + ((max_day) - comparative + minor_day);
}
else // same
{
next->tm_mday = next->tm_mday + (mayor_day - comparative);
}
}
check_months(next);
}
/* -------------------------------------------------------------------------- */
static void generate_next_day(int rep, int mayor_day, int minor_day, struct tm * next, struct tm * now)
{
if ( rep == 0 ) //Repeat every weeks
{
sum_days(next, now, mayor_day, minor_day, 7, next->tm_wday);
next->tm_min = now->tm_min;
next->tm_hour = now->tm_hour;
}
if ( rep == 1 ) //Repeat every months
{
cout << next->tm_mday << endl;
sum_days(next, now, mayor_day, minor_day, m_months_days[next->tm_mon] , next->tm_mday);
next->tm_min = now->tm_min;
next->tm_hour = now->tm_hour;
}
if ( rep == 2 ) //Repeat every months
{
sum_days(next, now, mayor_day, minor_day, 365, next->tm_yday);
next->tm_min = now->tm_min;
next->tm_hour = now->tm_hour;
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachineXML::next_action(VectorAttribute& vatt)
{
string days;
int rep_mode, end_mode;
int end_value;
int mayor_day = -1;
int minor_day = 366;
int start_day;
time_t action_time;
time_t done_time;
vector<string> v_days;
set<int> s_days;
SchedAction action(&vatt, -1);
vatt.vector_value("DAYS", days);
vatt.vector_value("REP", rep_mode);
vatt.vector_value("END_TYPE", end_mode);
vatt.vector_value("END_VALUE", end_value);
vatt.vector_value("TIME", action_time);
vatt.vector_value("DONE", done_time);
return action.next_action();
}
v_days = one_util::split(days, ',', true);
for(vector<string>::iterator it = v_days.begin(); it != v_days.end(); ++it) {
s_days.insert(atoi((*it).c_str()));
}
struct tm next;
struct tm start_tm;
localtime_r(&action_time, &start_tm);
next = start_tm;
start_day = start_tm.tm_wday;
if (rep_mode == 1)
{
start_day = start_tm.tm_mday;
}
else if (rep_mode == 2)
{
start_day = start_tm.tm_yday;
}
std::set<int>::iterator it;
std::pair<std::set<int>::iterator,bool> ret;
if (rep_mode != 3)
{
it = s_days.begin();
minor_day = *it;
ret = s_days.insert(start_day);
if ( ret.second == false )
{
mayor_day = *ret.first;
if ( ++ret.first != s_days.end() )
{
mayor_day = *ret.first;
}
}
else
{
mayor_day = minor_day;
if ( ++ret.first != s_days.end() )
{
mayor_day = *ret.first;
}
it = s_days.find(start_day);
s_days.erase (it);
if (*--it > mayor_day)
{
mayor_day = *it;
}
}
}
if ( end_mode == 1 )
{
int num_rep = end_value;
if (num_rep <= 0)
{
cout << "Finished scheduling actions" << endl;
return -1;
}
end_value = num_rep-1;
vatt.replace("END_VALUE", end_value);
}
else if ( end_mode == 2 )
{
time_t t_end = end_value;
if ( time(0) > t_end )
{
cout << "Finished scheduling actions" << endl;
return -1;
}
}
if (rep_mode == 3)
{
it = s_days.begin();
int hours = *it;
next.tm_min = start_tm.tm_min;
next.tm_hour = start_tm.tm_hour + hours;
check_months(&next);
}
else
{
generate_next_day(rep_mode, mayor_day, minor_day, &next, &start_tm);
}
action_time = mktime (&next);
if (action_time != -1)
{
vatt.replace("TIME", action_time);
}
else
{
return -1;
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

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

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,471 @@
/* -------------------------------------------------------------------------- */
/* 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("REP");
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 = END_NONE;
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,23] range" // HOURLY - 3
};
static int fday[] = {0,1,0,1};
static int lday[] = {7,32,366,24};
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.cbegin());
int _lday = *(_d.cend());
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;
time_t value = end_value;
localtime_r(&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;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
map<int, int> MONTHS_DAYS = {{0, 31}, {1, 28}, {2, 30}, {3, 31}, {4, 30},
{5, 31}, {6, 30}, {7, 31}, {8, 30}, {9, 31}, {10, 30}, {11, 31}};
/* -------------------------------------------------------------------------- */
static void check_months(struct tm * next)
{
int days_month = MONTHS_DAYS[next->tm_mon];
if ( next->tm_year+1900 % 4 == 0 && ( next->tm_year+1900 % 100 != 0 ||
next->tm_year+1900 % 400 == 0) && next->tm_mon == 1)
{
days_month++;
}
if ( next->tm_mday > days_month)
{
next->tm_mday = next->tm_mday - MONTHS_DAYS[next->tm_mon];
next->tm_mon++;
}
}
/* -------------------------------------------------------------------------- */
static void sum_days(struct tm * next, struct tm * now, int mayor_day,
int minor_day, int max_day, int comparative)
{
if (mayor_day >= 0 && minor_day < max_day)
{
if( mayor_day <= comparative ) //next
{
next->tm_mday = next->tm_mday + (max_day - comparative + minor_day);
}
else // same
{
next->tm_mday = next->tm_mday + (mayor_day - comparative);
}
}
check_months(next);
}
/* -------------------------------------------------------------------------- */
static void generate_next_day(int rep, int mayor_day, int minor_day,
struct tm * next, struct tm * now)
{
SchedAction::Repeat r = static_cast<SchedAction::Repeat>(rep);
switch(r)
{
case SchedAction::WEEKLY:
sum_days(next, now, mayor_day, minor_day, 7, next->tm_wday);
next->tm_min = now->tm_min;
next->tm_hour = now->tm_hour;
break;
case SchedAction::MONTHLY:
sum_days(next, now, mayor_day, minor_day, MONTHS_DAYS[next->tm_mon],
next->tm_mday);
next->tm_min = now->tm_min;
next->tm_hour = now->tm_hour;
break;
case SchedAction::YEARLY:
sum_days(next, now, mayor_day, minor_day, 365, next->tm_yday);
next->tm_min = now->tm_min;
next->tm_hour = now->tm_hour;
break;
case SchedAction::HOURLY:
case SchedAction::NONE:
break;
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
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 */
/* --------------------------------------------------------------------- */
time_t action_time;
struct tm start_tm;
localtime_r(&action_time, &start_tm);
struct tm next = start_tm;
int start_day;
switch(r)
{
case WEEKLY:
start_day = start_tm.tm_wday;
break;
case MONTHLY:
start_day = start_tm.tm_mday;
break;
case YEARLY:
start_day = start_tm.tm_yday;
break;
case HOURLY:
case NONE:
break;
}
std::set<int>::iterator it = _days.begin();
if ( r != HOURLY ) //WEEKLY, MONTHLY, YEARLY
{
int minor_day = *it;
int mayor_day;
std::pair<std::set<int>::iterator, bool> ret = _days.insert(start_day);
if ( ret.second == false )
{
mayor_day = *(ret.first);
if ( ++ret.first != _days.end() )
{
mayor_day = *(ret.first);
}
}
else
{
mayor_day = minor_day;
if ( ++ret.first != _days.end() )
{
mayor_day = *(ret.first);
}
it = _days.find(start_day);
_days.erase(it);
if (*(--it) > mayor_day)
{
mayor_day = *it;
}
}
generate_next_day(r, mayor_day, minor_day, &next, &start_tm);
}
else //HOURLY
{
int hours = *it;
next.tm_min = start_tm.tm_min;
next.tm_hour = start_tm.tm_hour + hours;
check_months(&next);
}
action_time = mktime(&next);
if (action_time != -1)
{
replace("TIME", action_time);
}
else
{
return -1;
}
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 */
@ -77,6 +76,7 @@ int VMTemplate::insert(SqlDB *db, string& error_str)
// Remove DONE/MESSAGE from SCHED_ACTION and check rest attributes
// ---------------------------------------------------------------------
int rc = parse_sched_action(error_str);
if (rc == -1)
{
return rc;
@ -173,93 +173,9 @@ error_common:
int VMTemplate::parse_sched_action(string& error_str)
{
vector<VectorAttribute *> _sched_actions;
vector<VectorAttribute *>::iterator i;
set<int>::const_iterator it_set;
VectorAttribute* vatt;
int rep_mode, end_mode;
int has_mode, has_end_mode, has_days, has_end_value;
int end_value;
int first_day, last_day;
string days;
vector<string> v_days;
set<int> s_days;
SchedActions sactions(obj_template);
get_template_attribute("SCHED_ACTION", _sched_actions);
for ( i = _sched_actions.begin(); i != _sched_actions.end() ; ++i)
{
vatt = dynamic_cast<VectorAttribute*>(*i);
has_mode = vatt->vector_value("REP", rep_mode);
has_days = vatt->vector_value("DAYS", days);
if (has_mode == 0 && has_days == 0)
{
v_days = one_util::split(days, ',', true);
if ( !v_days.empty() )
{
for(vector<string>::iterator it = v_days.begin(); it != v_days.end(); ++it) {
s_days.insert(atoi((*it).c_str()));
}
}
else
{
s_days.insert(atoi((days).c_str()));
}
first_day = *s_days.cbegin();
last_day = *s_days.cend();
if (rep_mode == 0 && !(first_day >= 0 && last_day < 7)) //WEEK [0,6]
{
error_str = "Error parsing days of the week. [0,6]";
return -1;
}
else if (rep_mode == 1 && !(first_day >= 1 && last_day < 32)) //MONTH [1,31]
{
error_str = "Error parsing days of the month. [1,31]";
return -1;
}
else if (rep_mode == 2 && !(first_day >= 0 && last_day < 366)) //YEAR [0,365]
{
error_str = "Error parsing days of the year. [0,365]";
return -1;
}
else if (rep_mode == 3 && (first_day != last_day || s_days.size() > 1)) //YEAR [0,365]
{
error_str = "Error parsing hour.";
return -1;
}
}
has_end_mode = vatt->vector_value("END_TYPE", end_mode);
has_end_value = vatt->vector_value("END_VALUE", end_value);
if (has_end_mode == 0 && has_end_value == 0)
{
if (end_mode == 1 && end_value < 0) //N_REP
{
error_str = "Error parsing END_VALUE of type N_REP.";
return -1;
}
else if ( end_mode == 2 ) //DATE
{
time_t value = end_value;
struct tm val_tm;
localtime_r(&value, &val_tm);
time_t out = mktime(&val_tm);
if (out == -1)
{
error_str = "Error parsing END_VALUE of type DATE.";
return -1;
}
}
}
vatt->remove("DONE");
vatt->remove("MESSAGE");
}
return 0;
return sactions.parse(error_str);
}
/* ------------------------------------------------------------------------ */