diff --git a/include/ScheduledAction.h b/include/ScheduledAction.h new file mode 100644 index 0000000000..132c2278c5 --- /dev/null +++ b/include/ScheduledAction.h @@ -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 + +#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& _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 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(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_*/ + diff --git a/src/scheduler/src/pool/VirtualMachineXML.cc b/src/scheduler/src/pool/VirtualMachineXML.cc index 9ac6283fb6..80a43e034b 100644 --- a/src/scheduler/src/pool/VirtualMachineXML.cc +++ b/src/scheduler/src/pool/VirtualMachineXML.cc @@ -17,13 +17,12 @@ #include #include "VirtualMachineXML.h" +#include "ScheduledAction.h" #include "DatastoreXML.h" #include "DatastorePoolXML.h" #include "NebulaUtil.h" #include "History.h" -map 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 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 v_days; - set 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::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::iterator it; - std::pair::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; -} \ No newline at end of file +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ \ No newline at end of file diff --git a/src/scheduler/src/sched/SConstruct b/src/scheduler/src/sched/SConstruct index 7da4bdd5b8..90d9963cd2 100644 --- a/src/scheduler/src/sched/SConstruct +++ b/src/scheduler/src/sched/SConstruct @@ -39,6 +39,7 @@ sched_env.Prepend(LIBS=[ 'nebula_common', 'nebula_core', 'nebula_template', + 'nebula_vmtemplate', 'nebula_vm', 'nebula_host', 'crypto', diff --git a/src/vm_template/SConstruct b/src/vm_template/SConstruct index ea7361b4e2..0987c527e4 100644 --- a/src/vm_template/SConstruct +++ b/src/vm_template/SConstruct @@ -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 diff --git a/src/vm_template/ScheduledAction.cc b/src/vm_template/ScheduledAction.cc new file mode 100644 index 0000000000..57ed2ef826 --- /dev/null +++ b/src/vm_template/ScheduledAction.cc @@ -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(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(v_eo); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int SchedAction::days(std::set& _d) +{ + std::string st; + + int rc = vector_value("DAYS", st); + + if ( rc != 0 ) + { + return 0; + } + + one_util::split_unique(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 _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 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(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 _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::iterator it = _days.begin(); + + if ( r != HOURLY ) //WEEKLY, MONTHLY, YEARLY + { + + int minor_day = *it; + int mayor_day; + + std::pair::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; +} diff --git a/src/vm_template/VMTemplate.cc b/src/vm_template/VMTemplate.cc index b0256ea100..52880cc47f 100644 --- a/src/vm_template/VMTemplate.cc +++ b/src/vm_template/VMTemplate.cc @@ -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 _sched_actions; - vector::iterator i; - set::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 v_days; - set 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(*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::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); } /* ------------------------------------------------------------------------ */