Compare commits
23 Commits
Examplar-1
...
Examplar-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c354b4401 | ||
|
|
0db575a075 | ||
|
|
46e56b8d6f | ||
|
|
93c22887e8 | ||
|
|
9d89f5ad6a | ||
|
|
6fca4f2d91 | ||
|
|
c75fa9d2c8 | ||
|
|
49b4e77ca8 | ||
|
|
8a95bbf219 | ||
|
|
4d66a0f059 | ||
|
|
221edee07c | ||
|
|
0c70f7ee36 | ||
|
|
2b2225f3e4 | ||
|
|
6af1082852 | ||
|
|
9d5af160c5 | ||
|
|
da50f152f2 | ||
|
|
7404f07dc5 | ||
|
|
a50293a217 | ||
|
|
660745d7d5 | ||
|
|
c37b6e1a9e | ||
|
|
50478d58ed | ||
|
|
109d227166 | ||
|
|
14d526d95c |
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ftests)
|
||||
project(examplar)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z -O0 -DDEBUG=1")
|
||||
set(SOURCE_FILES examplar.cpp src/loaders/loaders.cpp src/loaders/loaders.h src/json/jsoncpp.cpp src/loaders/JSON_Loader.cpp src/loaders/JSON_Loader.h src/loaders/helpers.cpp src/loaders/helpers.h src/loaders/Suite.cpp src/loaders/Suite.h src/loaders/Plan.cpp src/loaders/Plan.h src/loaders/Conf.cpp src/loaders/Conf.h src/loaders/Unit.cpp src/loaders/Unit.h src/loaders/Task.cpp src/loaders/Task.h src/sproc/Sproc.cpp src/sproc/Sproc.h)
|
||||
|
||||
add_executable(ftests ${SOURCE_FILES})
|
||||
add_executable(examplar ${SOURCE_FILES})
|
||||
123
README.md
Normal file
123
README.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Instructions
|
||||
These are instructions for using Examplar.
|
||||
|
||||
## Build
|
||||
|
||||
Compiling Examplar is easy. There are zero external dependencies. Build does require *cmake*.
|
||||
|
||||
~~~~
|
||||
$ cmake .
|
||||
$ make
|
||||
~~~~
|
||||
|
||||
Then place the binary where you'd like. I'd recommend packaging it for your favorite Linux distribution.
|
||||
|
||||
## High Level Usage
|
||||
|
||||
1. Write a script that does a thing.
|
||||
2. Write a script that checks if that thing is done.
|
||||
3. Set up your check as a target in a unit.
|
||||
4. Set up your script as its rectifier.
|
||||
5. Turn on the rectify pattern in the unit definition.
|
||||
|
||||
## Definitions
|
||||
So you've got Examplar compiled and you're ready to start automating the world.
|
||||
|
||||
If you're thinking "how do I configure this thing", this article is for you.
|
||||
|
||||
### Units
|
||||
|
||||
A Unit is an automation definition, written in JSON in a UNIT FILE. Deeper into Examplar’s internals, Units and Tasks have slightly different functions, but for the purposes of users, the terms can be used interchangeably. A Task is a task to be performed in a Plan, and a Unit is its definition. A Unit is a JSON object that has:
|
||||
|
||||
* A `name`, which is an identifier for the Unit used by people.
|
||||
* A `target`, which is the path to the automation script performing the work. This provides a clean linear path for huge chains of scripts to be executed in order and tracked on return for additional logic in chaining.
|
||||
* A `rectifier`, which is the path to the automation script to be executed if the target call fails.
|
||||
* A `rectify` attribute, which tells Examplar whether or not to execute the rectifier in the case of failure when executing the target.
|
||||
* An `active` attribute,which tells Examplar whether or not the Unit can be used in a Plan. This gives Unit developers a way to tell Plan developers not to use the Unit.
|
||||
* A `required` attribute which tells Examplar whether or not the Plan can continue if the Unit fails. If the rectify attribute is set to true, this attribute is checked after a rectifier failure. If not, this is checked after target failure. In either case, if the rectifier or target do not return successfully, Examplar will halt the execution of the Plan if this is turned on for the unit being executed. Otherwise it simply moves to the next Unit being executed.
|
||||
|
||||
### Tasks
|
||||
|
||||
A Task is an action item in a Plan, just like in real life. In the context of Examplar, a Task is a Unit that has been loaded and incorporated into a Plan in an actionable state. Inactive Units can not be loaded into a Plan and thus can never be a Task. The primary difference between a Task and a Unit is that a Unit is not actionable — it’s just a definition — while a Task a consumable, actionable automation.
|
||||
Suite
|
||||
|
||||
A Suite is not visible to the user and this is only for informational purposes. A Suite is a collection of all available Unit definitions loaded from one or more UNIT FILES. Just as a Unit is the definition for a Task, a Suite is a collection of Units that define the Task components of a Plan.
|
||||
|
||||
A Suite is consumed by a Plan during the conversion of Units to Tasks, though this is not visible to the user — it just simply helps to understand the kind of abstraction taking place in the conceptual model of Examplar.
|
||||
Plan
|
||||
|
||||
A Plan is the glue of all the components of Examplar and is deceptively simple. A Plan loads a Suite for its Task definitions (Units), but the Tasks to actually execute are specified in the PLAN FILE. The Tasks are executed in the order specified in the PLAN FILE.
|
||||
|
||||
### FILES
|
||||
There are several files used by Examplar.
|
||||
|
||||
#### CONFIG FILE and Attributes
|
||||
|
||||
This is the one config file that Examplar uses. The default path it looks is /etc/Examplar/config.json.
|
||||
|
||||
A config file at the time of writing this specifies a single JSON object with 5 attributes:
|
||||
|
||||
* `units_path`: The `UNIT FILE` path or a path to a directory containing unit files.
|
||||
* `plan_path`: The PLAN FILE path. There is only ever one plan executed in a single run.
|
||||
* `config_version`: The configuration VERSION.
|
||||
* `execution_context`: The current working directory to use when loading unit files, plan files, or executing Tasks.
|
||||
* `execution_context_override`: A boolean indicating whether or not the execution context should be set, or left alone. It is highly recommended to set this to `true`.
|
||||
|
||||
#### Configuration VERSION
|
||||
|
||||
The configuration version is checked to ensure that the configuration is consumable by that version of Examplar. This will pave the way for reverse compatibility if the project moves in that direction.
|
||||
|
||||
|
||||
#### UNIT FILE
|
||||
|
||||
The UNIT FILE is a specification of where the Units are defined. All UNIT FILES in that directory will be amalgamated to generate the Suite. These types of files must end in `*.units` for their filename.
|
||||
|
||||
#### PLAN FILE
|
||||
|
||||
The PLAN FILE is a specification of the order that Tasks are executed, and their dependencies upon each other. Dependency implementation is a touchy matter that is pending implementation, so, mileage may vary until release.
|
||||
|
||||
|
||||
|
||||
## I still don't see how this works.
|
||||
|
||||
That's ok. It's in its infancy so we're always looking for ways to make it simpler. Here's a 'hello world' example.
|
||||
|
||||
### 1. Write your tests.
|
||||
First, we want to know all the things we need to be able to print "hello world" to the screen. In this case we just need to make we have the "echo" binary.
|
||||
|
||||
Write a bash script that checks if the "echo" binary is on the system.
|
||||
|
||||
#!/usr/bin/bash
|
||||
stat /usr/bin/echo
|
||||
exit $?
|
||||
|
||||
Save it as ~/check-echo.bash.
|
||||
This script will be your "target" attribute for your "hello world" unit definition.
|
||||
|
||||
### 2. Write your automation.
|
||||
Write a "hello world" script.
|
||||
|
||||
#!/usr/bin/bash
|
||||
echo "hello world"
|
||||
exit $?
|
||||
|
||||
Save it as ~/hello.bash
|
||||
This script will be your "rectify" attribute for your "hello world" unit definition.
|
||||
|
||||
### 3. Set up the Unit file.
|
||||
At this point you've got both the script that checks if hello world can run and you've got your hello world script. Time to set up the unit.
|
||||
|
||||
### 4. Add the Unit definition to the Plan.
|
||||
Next, add the unit to the plan by name.
|
||||
|
||||
### 5. Set up your config file.
|
||||
Point your config file at your plan file and your units directory.
|
||||
|
||||
### 6. Run Examplar pointing at that config file.
|
||||
Execute examplar:
|
||||
|
||||
examplar --verbose --config path/to/your/config/file.json
|
||||
|
||||
And you should see your 'hello world' script.
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"units_path": "/home/phanes/development/internal/Examplar/conf/units/all_test.units",
|
||||
"plan_path": "/home/phanes/development/internal/Examplar/conf/plans/test.plan",
|
||||
"config_version": "1"
|
||||
"execution_context_override": true,
|
||||
"execution_context": "/home/phanes/development/internal/Examplar/conf/",
|
||||
"units_path": "units/all_test.units",
|
||||
"plan_path": "plans/test.plan",
|
||||
"config_version": "2"
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"units": [
|
||||
{
|
||||
"name": "independent test 1",
|
||||
"target": "/usr/bin/false",
|
||||
"target": "/usr/bin/true",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"active": true,
|
||||
"required": false,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
},
|
||||
{
|
||||
@@ -26,10 +26,10 @@
|
||||
},
|
||||
{
|
||||
"name": "dependent test",
|
||||
"target": "/usr/bin/true",
|
||||
"rectifier": "/usr/bin/false",
|
||||
"target": "/usr/bin/false",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"active": true,
|
||||
"required": false,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
}
|
||||
]
|
||||
|
||||
20
conf/units/dependent_tests.units
Normal file
20
conf/units/dependent_tests.units
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"units": [
|
||||
{
|
||||
"name": "A DEFINITION THAT IS NOT USED",
|
||||
"target": "/usr/bin/dialog --yesno test 50 50",
|
||||
"rectifier": "/usr/bin/false",
|
||||
"active": true,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
},
|
||||
{
|
||||
"name": "dependent test",
|
||||
"target": "/usr/bin/false",
|
||||
"rectifier": "/usr/bin/true",
|
||||
"active": true,
|
||||
"required": true,
|
||||
"rectify": true
|
||||
}
|
||||
]
|
||||
}
|
||||
60
design/Target Execution Flow.txt
Normal file
60
design/Target Execution Flow.txt
Normal file
@@ -0,0 +1,60 @@
|
||||
# Logic Tree for Examplar Task Execution
|
||||
|
||||
Examplar - An automation and testing framework.
|
||||
|
||||
© SURRO INDUSTRIES and Chris Punches, 2017.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
## Key:
|
||||
-Actions are labled by indices in a zero-indexed array 'a'.
|
||||
-Decisions are labeled by indices in a zero-indexed array 'd'.
|
||||
-Decisions have two possible states, TRUE or FALSE (0 or 1 respectively).
|
||||
-Decision determinations are labeled with the decision label and a subspecifier.
|
||||
|
||||
## Diagram:
|
||||
|
||||
a[0] Execute Target
|
||||
d[0] Error Code Check
|
||||
d[0].0 ZERO
|
||||
a[1] NEXT
|
||||
d[0].1 NON-ZERO
|
||||
d[1] Rectify Check
|
||||
d[1].0 FALSE
|
||||
d[2] Required Check
|
||||
d[2].0 FALSE
|
||||
a[2] NEXT
|
||||
d[2].1 TRUE
|
||||
a[3] EXCEPTION
|
||||
d[1].1 TRUE
|
||||
a[4] Execute Rectifier
|
||||
d[3] Error Code Check
|
||||
d[3].1 NON-ZERO
|
||||
d[4] Required Check
|
||||
d[4].0 FALSE
|
||||
a[5] NEXT
|
||||
d[4].1 TRUE
|
||||
a[6] EXCEPTION
|
||||
d[3].0 ZERO
|
||||
a[7] Re-Execute Target
|
||||
d[5] Error Code Check
|
||||
d[5].0 ZERO
|
||||
a[8] NEXT
|
||||
d[5].1 NON-ZERO
|
||||
d[6] Required Check
|
||||
d[6].0 FALSE
|
||||
a[9] NEXT
|
||||
d[6].1 TRUE
|
||||
a[10] EXCEPTION
|
||||
|
||||
125
examplar.cpp
125
examplar.cpp
@@ -17,38 +17,149 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "src/json/json.h"
|
||||
#include "src/loaders/loaders.h"
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <syslog.h>
|
||||
#include "src/loaders/helpers.h"
|
||||
|
||||
/*
|
||||
* TODO Commandline switches
|
||||
*/
|
||||
|
||||
int main( )
|
||||
void print_usage()
|
||||
{
|
||||
bool verbose = true;
|
||||
printf("examplar [ -h | --help ] [ -v | --verbose ] [ -e | --execution-context EXECUTION_CONTEXT ][ -c | --config CONFIG_PATH ]\n\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
int flags, opt;
|
||||
bool verbose = false;
|
||||
bool show_help = false;
|
||||
|
||||
// indicator of whether examplar should use a commandline argument for overriding the context
|
||||
// instead of what's supplied in the conf file
|
||||
bool cli_context_supplied = false;
|
||||
|
||||
std::string config_path = "/etc/Examplar/config.json";
|
||||
std::string execution_context;
|
||||
|
||||
// commandline switches:
|
||||
// -h help
|
||||
// -v verbose
|
||||
// -c CONFIG_FILE_PATH -- defaults to '/etc/Examplar/config.json'
|
||||
// -e EXECUTION_CONTEXT -- current working directory when executing unit targets
|
||||
|
||||
while (1)
|
||||
{
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"config", required_argument, 0, 'c'},
|
||||
{"execution-context", required_argument, 0, 'e'},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
|
||||
opt = getopt_long (argc, argv, "vhec:", long_options, &option_index);
|
||||
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
if (long_options[option_index].flag !=0)
|
||||
break;
|
||||
case 'h':
|
||||
show_help = true;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'c':
|
||||
config_path = std::string(optarg);
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
exit(1);
|
||||
case 'e':
|
||||
cli_context_supplied = true;
|
||||
execution_context = std::string(optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( show_help == true )
|
||||
{
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
setlogmask( LOG_UPTO( LOG_INFO ) );
|
||||
|
||||
openlog( "Examplar", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_PERROR | LOG_LOCAL1 );
|
||||
|
||||
// A Plan is made up of Tasks, and a Suite is made up of Units.
|
||||
// A Plan declares what units are executed and a Suite declares the definitions of those units.
|
||||
Conf configuration = Conf("/home/phanes/development/internal/Examplar/conf/config.json", verbose );
|
||||
Conf configuration = Conf(config_path, verbose );
|
||||
|
||||
// load the configuration file which contains filepaths to definitions of a plan and definitions of units.
|
||||
// if the user set this option as a commandline argument
|
||||
if ( cli_context_supplied == true )
|
||||
{
|
||||
// override the conf file's specified execution context
|
||||
configuration.set_execution_context( execution_context );
|
||||
}
|
||||
|
||||
// check if context override
|
||||
if ( configuration.has_context_override() )
|
||||
{
|
||||
// if so, set the CWD.
|
||||
chdir( configuration.get_execution_context().c_str() );
|
||||
std::ostringstream infostring;
|
||||
infostring << "Execution context: " << get_working_path() << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
|
||||
|
||||
// load the filepaths to definitions of a plan and definitions of units.
|
||||
std::string definitions_file = configuration.get_units_path();
|
||||
std::string plan_file = configuration.get_plan_path();
|
||||
|
||||
Suite available_definitions;
|
||||
available_definitions.load_units_file( definitions_file, verbose );
|
||||
|
||||
Plan plan;
|
||||
Plan plan( &configuration );
|
||||
plan.load_plan_file( plan_file, verbose );
|
||||
|
||||
plan.load_definitions( available_definitions, verbose );
|
||||
|
||||
std::cout << "Ready to execute all tasks in Plan." << std::endl;
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
plan.execute( verbose );
|
||||
}
|
||||
catch ( std::exception& e) {
|
||||
|
||||
catch ( std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
syslog( LOG_ERR, e.what() );
|
||||
closelog();
|
||||
return 1;
|
||||
}
|
||||
|
||||
closelog();
|
||||
return 0;
|
||||
}
|
||||
@@ -19,17 +19,48 @@
|
||||
*/
|
||||
#include "Conf.h"
|
||||
|
||||
/// CONF_PLANPATH_INVALID - Exception thrown when the Conf type can not load the supplied path for the Plan definition
|
||||
/// file.
|
||||
class CONF_PLANPATH_INVALID: public std::runtime_error { public:
|
||||
CONF_PLANPATH_INVALID(): std::runtime_error("conf: The supplied path for the plan definition file is invalid.") {}
|
||||
/// ConfigLoadException - General exception handler for the Conf class.
|
||||
class ConfigLoadException: public std::exception
|
||||
{
|
||||
public:
|
||||
/** Constructor (C strings).
|
||||
* @param message C-style string error message.
|
||||
* The string contents are copied upon construction.
|
||||
* Hence, responsibility for deleting the char* lies
|
||||
* with the caller.
|
||||
*/
|
||||
explicit ConfigLoadException(const char* message):
|
||||
msg_(message)
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor (C++ STL strings).
|
||||
* @param message The error message.
|
||||
*/
|
||||
explicit ConfigLoadException(const std::string& message):
|
||||
msg_(message)
|
||||
{}
|
||||
|
||||
/** Destructor.
|
||||
* Virtual to allow for subclassing.
|
||||
*/
|
||||
virtual ~ConfigLoadException() throw (){}
|
||||
|
||||
/** Returns a pointer to the (constant) error description.
|
||||
* @return A pointer to a const char*. The underlying memory
|
||||
* is in posession of the Exception object. Callers must
|
||||
* not attempt to free the memory.
|
||||
*/
|
||||
virtual const char* what() const throw (){
|
||||
return msg_.c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Error message.
|
||||
*/
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
/// CONF_UNITSPATH_INVALID - Exception thrown when the Conf type can not load the supplied path for the Unit definition
|
||||
/// files.
|
||||
class CONF_UNITSPATH_INVALID: public std::runtime_error { public:
|
||||
CONF_UNITSPATH_INVALID(): std::runtime_error("conf: The supplied path for the unit definition file is invalid.") {}
|
||||
};
|
||||
|
||||
/// Conf::Conf - Constructor for Conf type. Loads the configuration for the application.
|
||||
/// TODO Expand to detect when a directory path is supplied for units_path or plan_path and import all Tasks and Units.
|
||||
@@ -37,18 +68,67 @@ class CONF_UNITSPATH_INVALID: public std::runtime_error { public:
|
||||
/// \param filename - The filename to load the configuration from.
|
||||
Conf::Conf( std::string filename, bool verbose ): JSON_Loader()
|
||||
{
|
||||
// prepare context spaghetti
|
||||
this->override_context = false;
|
||||
|
||||
// load the conf file.
|
||||
this->load_json_file( filename, verbose );
|
||||
|
||||
if (this->get_serialized(this->config_version, "config_version" ,true) != 0)
|
||||
{
|
||||
throw ConfigLoadException("config_version string is not set in the config file supplied: " + filename);
|
||||
}
|
||||
if ( this->config_version.asString() != VERSION_STRING )
|
||||
{
|
||||
throw ConfigLoadException("config_version string expected was " + std::string(VERSION_STRING) + " in: " + filename);
|
||||
}
|
||||
|
||||
// find the path to the plan file
|
||||
if (this->get_serialized(this->plan_path, "plan_path", true) != 0 ) { throw CONF_PLANPATH_INVALID(); }
|
||||
if (this->get_serialized(this->plan_path, "plan_path", true) != 0 )
|
||||
{
|
||||
throw ConfigLoadException("plan_path string is not set in the config file supplied:" + filename);
|
||||
}
|
||||
|
||||
// find the path to the unit definitions file
|
||||
if (this->get_serialized(this->units_path, "units_path", true) != 0 ) { throw CONF_UNITSPATH_INVALID(); }
|
||||
if (this->get_serialized(this->units_path, "units_path", true) != 0 )
|
||||
{
|
||||
throw ConfigLoadException("units_path string is not set in the config file supplied: " + filename);
|
||||
}
|
||||
|
||||
if ( this->get_serialized(this->override_execution_context, "execution_context_override", true) != 0 )
|
||||
{
|
||||
throw ConfigLoadException("execution_context_override boolean is not set in the config file supplied: " + filename);
|
||||
} else {
|
||||
this->override_context = true;
|
||||
}
|
||||
|
||||
if ( this->get_serialized(this->execution_context, "execution_context", true) != 0 )
|
||||
{
|
||||
throw ConfigLoadException("execution_context string is not set in the config file supplied: " + filename);
|
||||
} else {
|
||||
this->execution_context_literal = this->execution_context.asString();
|
||||
}
|
||||
};
|
||||
|
||||
/// Conf::has_context_override - Specifies whether or not the override context function is enabled in the conf file.
|
||||
bool Conf::has_context_override() {
|
||||
return this->override_execution_context.asBool();
|
||||
}
|
||||
|
||||
/// Conf::get_execution_context - Specifies the path to the current working directory to set for all unit executions.
|
||||
std::string Conf::get_execution_context() {
|
||||
return this->execution_context_literal;
|
||||
}
|
||||
|
||||
/// Conf::get_plan_path - Retrieves the path to the Plan definition file from the application configuration file.
|
||||
std::string Conf::get_plan_path() { return this->plan_path.asString(); }
|
||||
|
||||
|
||||
/// Conf::get_units_path - Retrieves the path to the Unit definition file from the application configuration file.
|
||||
std::string Conf::get_units_path() { return this->units_path.asString(); }
|
||||
|
||||
/// Conf::set_execution_context- Sets the execution context.
|
||||
void Conf::set_execution_context( std::string execution_context )
|
||||
{
|
||||
this->execution_context_literal = execution_context;
|
||||
}
|
||||
|
||||
@@ -21,17 +21,40 @@
|
||||
#ifndef FTESTS_CONF_H
|
||||
#define FTESTS_CONF_H
|
||||
#include "JSON_Loader.h"
|
||||
#include <exception>
|
||||
|
||||
|
||||
#define STRINGIZE2(s) #s
|
||||
#define STRINGIZE(s) STRINGIZE2(s)
|
||||
# define IMPL_CONFIG_VERSION 2
|
||||
# define VERSION_STRING STRINGIZE(IMPL_CONFIG_VERSION)
|
||||
|
||||
class Conf: public JSON_Loader
|
||||
{
|
||||
private:
|
||||
Json::Value plan_path;
|
||||
Json::Value units_path;
|
||||
Json::Value execution_context;
|
||||
Json::Value config_version;
|
||||
|
||||
// flag to indicate if execution context should be overriden in config file
|
||||
// if set to true Examplar should use whats in the config file for current working directory
|
||||
// if set to false, Examplar should use the current working directory at time of execution
|
||||
Json::Value override_execution_context;
|
||||
|
||||
bool override_context;
|
||||
std::string execution_context_literal;
|
||||
|
||||
public:
|
||||
Conf( std::string filename, bool verbose );
|
||||
std::string get_plan_path();
|
||||
std::string get_units_path();
|
||||
|
||||
bool has_context_override();
|
||||
|
||||
std::string get_execution_context();
|
||||
void set_execution_context( std::string );
|
||||
|
||||
};
|
||||
|
||||
#endif //FTESTS_CONF_H
|
||||
|
||||
@@ -120,7 +120,10 @@ protected:
|
||||
/// Plan::Plan() - Constructor for Plan class. A Plan is a managed container for a Task vector. These tasks reference
|
||||
/// Units that are defined in the Units files (Suite). If Units are definitions, Tasks are selections of those
|
||||
/// definitions to execute, and if Units together form a Suite, Tasks together form a Plan.
|
||||
Plan::Plan(): JSON_Loader() {};
|
||||
Plan::Plan( Conf * configuration ): JSON_Loader()
|
||||
{
|
||||
this->configuration = configuration;
|
||||
};
|
||||
|
||||
/// Plan::load_plan_file - Uses the json_root buffer on each run to append intact Units as they're deserialized from
|
||||
/// the provided file.
|
||||
@@ -259,7 +262,7 @@ void Plan::execute( bool verbose )
|
||||
std::cout << "Executing task \"" << this->tasks[i].get_name() << "\"." << std::endl;
|
||||
}
|
||||
try {
|
||||
this->tasks[i].execute( verbose );
|
||||
this->tasks[i].execute( this->configuration, verbose );
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
throw Plan_Task_GeneralExecutionException( "Plan Task: \"" + this->tasks[i].get_name() + "\" reported: " + e.what() );
|
||||
|
||||
@@ -25,15 +25,17 @@
|
||||
#include "../json/json.h"
|
||||
#include "JSON_Loader.h"
|
||||
#include "Task.h"
|
||||
#include "Conf.h"
|
||||
|
||||
class Plan: public JSON_Loader
|
||||
{
|
||||
private:
|
||||
// storage for the tasks that make up the plan
|
||||
std::vector<Task> tasks;
|
||||
Conf * configuration;
|
||||
|
||||
public:
|
||||
Plan();
|
||||
Plan( Conf * configuration );
|
||||
|
||||
// append this->tasks from JSON file
|
||||
void load_plan_file( std::string filename, bool verbose );
|
||||
|
||||
@@ -19,13 +19,52 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#include "Suite.h"
|
||||
#include <dirent.h>
|
||||
|
||||
/// Suite_InvalidUnitMember - Exception thrown when a Suite tries to access a contained Unit's value that is not
|
||||
/// present in the Unit.
|
||||
class Suite_InvalidUnitMember: public std::runtime_error { public:
|
||||
// TODO rework this to accept the name of the member not able to be fetched.
|
||||
Suite_InvalidUnitMember(): std::runtime_error("Suite: Attempted to access a member of a Unit that is not set.") {}
|
||||
class SuiteException: public std::exception
|
||||
{
|
||||
public:
|
||||
/** Constructor (C strings).
|
||||
* @param message C-style string error message.
|
||||
* The string contents are copied upon construction.
|
||||
* Hence, responsibility for deleting the char* lies
|
||||
* with the caller.
|
||||
*/
|
||||
explicit SuiteException(const char* message):
|
||||
msg_(message)
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor (C++ STL strings).
|
||||
* @param message The error message.
|
||||
*/
|
||||
explicit SuiteException(const std::string& message):
|
||||
msg_(message)
|
||||
{}
|
||||
|
||||
/** Destructor.
|
||||
* Virtual to allow for subclassing.
|
||||
*/
|
||||
virtual ~SuiteException() throw (){}
|
||||
|
||||
/** Returns a pointer to the (constant) error description.
|
||||
* @return A pointer to a const char*. The underlying memory
|
||||
* is in posession of the Exception object. Callers must
|
||||
* not attempt to free the memory.
|
||||
*/
|
||||
virtual const char* what() const throw (){
|
||||
return msg_.c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Error message.
|
||||
*/
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,20 +81,88 @@ class Suite_InvalidUnitMember: public std::runtime_error { public:
|
||||
/// if the implementor so desires.
|
||||
Suite::Suite(): JSON_Loader() {};
|
||||
|
||||
|
||||
bool is_file( std::string path)
|
||||
{
|
||||
struct stat buf;
|
||||
stat( path.c_str(), &buf );
|
||||
return S_ISREG(buf.st_mode);
|
||||
}
|
||||
|
||||
bool is_dir( std::string path )
|
||||
{
|
||||
struct stat buf;
|
||||
stat( path.c_str(), &buf );
|
||||
return S_ISDIR(buf.st_mode);
|
||||
}
|
||||
|
||||
void get_units_from_dir( std::vector<std::string> * files, std::string path )
|
||||
{
|
||||
DIR* dirFile = opendir( path.c_str() );
|
||||
if ( dirFile )
|
||||
{
|
||||
std::string entry;
|
||||
|
||||
struct dirent* hFile;
|
||||
errno = 0;
|
||||
while (( hFile = readdir( dirFile )) != NULL )
|
||||
{
|
||||
if ( !strcmp( hFile->d_name, "." )) continue;
|
||||
if ( !strcmp( hFile->d_name, ".." )) continue;
|
||||
|
||||
// hidden files
|
||||
if ( hFile->d_name[0] == '.' ) continue;
|
||||
|
||||
// dirFile.name is the name of the file. Do whatever string comparison
|
||||
// you want here. Something like:
|
||||
if ( strstr( hFile->d_name, ".units" ))
|
||||
{
|
||||
std::string full_path = path + hFile->d_name;
|
||||
files->push_back( full_path );
|
||||
}
|
||||
}
|
||||
closedir( dirFile );
|
||||
} else {
|
||||
std::cout << "file not found" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Suite::load_units_file - Uses the json_root buffer on each run to append intact Units as they're
|
||||
/// deserialized from the provided file.
|
||||
///
|
||||
/// \param filename - The file to pull the JSON-formatted units from.
|
||||
/// \param units_path - The file to pull the JSON-formatted units from.
|
||||
/// \param verbose - Whether to print verbose output to STDOUT.
|
||||
void Suite::load_units_file( std::string filename, bool verbose )
|
||||
void Suite::load_units_file( std::string units_path, bool verbose )
|
||||
{
|
||||
std::vector<std::string> unit_files;
|
||||
|
||||
if ( is_dir( units_path ) )
|
||||
{
|
||||
// we have a directory path. find all files ending in *.units and load them into a vector<std::string>
|
||||
get_units_from_dir( &unit_files, units_path );
|
||||
}
|
||||
|
||||
if ( is_file( units_path ) )
|
||||
{
|
||||
unit_files.push_back( units_path );
|
||||
}
|
||||
|
||||
std::ostringstream infostring;
|
||||
infostring << "Unit files found: " << unit_files.size() << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cerr << infostring.str();
|
||||
|
||||
for ( int i = 0; i < unit_files.size(); i++ )
|
||||
{
|
||||
// will use json_root buffer on each run to append to this->units vector as valid units are found.
|
||||
this->load_json_file( filename, verbose );
|
||||
this->load_json_file( unit_files[i], verbose );
|
||||
|
||||
// staging buffer
|
||||
Json::Value jbuff;
|
||||
|
||||
// fill the jbuff staging buffer with a json::value object in the supplied filename
|
||||
// fill the jbuff staging buffer with a json::value object in the supplied units_path
|
||||
if ( this->get_serialized( jbuff, "units", verbose ) == 0)
|
||||
{
|
||||
this->json_root = jbuff;
|
||||
@@ -72,10 +179,17 @@ void Suite::load_units_file( std::string filename, bool verbose )
|
||||
// append to this->units
|
||||
this->units.push_back( tmp_U );
|
||||
if ( verbose ) {
|
||||
std::cout << "Added unit \"" << tmp_U.get_name() << "\" to Suite." << std::endl;
|
||||
std::ostringstream infostring;
|
||||
infostring << "Added unit \"" << tmp_U.get_name() << "\" to Suite." << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// Suite::get_unit - returns a contained Unit identified by name attribute.
|
||||
@@ -99,8 +213,11 @@ void Suite::get_unit(Unit & result, std::string provided_name)
|
||||
|
||||
if (! foundMatch )
|
||||
{
|
||||
std::cerr << "Unit name \"" << provided_name << "\" was referenced but not defined!" << std::endl;
|
||||
throw Suite_InvalidUnitMember();
|
||||
std::ostringstream infostring;
|
||||
infostring << "Unit name \"" << provided_name << "\" was referenced but not defined!" << std::endl;
|
||||
syslog(LOG_ERR, infostring.str().c_str() );
|
||||
std::cerr << infostring.str();
|
||||
throw SuiteException( infostring.str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "Unit.h"
|
||||
|
||||
|
||||
|
||||
|
||||
class Suite: public JSON_Loader
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
*/
|
||||
|
||||
#include "Task.h"
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include "../sproc/Sproc.h"
|
||||
#include "helpers.h"
|
||||
|
||||
/// Task_InvalidDataStructure - Exception thrown when a Task is defined with invalid JSON.
|
||||
class Task_InvalidDataStructure: public std::runtime_error {
|
||||
@@ -35,18 +36,49 @@ public:
|
||||
Task_NotReady(): std::runtime_error("Task: Attempted to access a unit of a Task that is not defined.") {}
|
||||
};
|
||||
|
||||
/// Task_RequiredButFailedTask - Exception thrown when a Task is failed but required, and rectification also failed.
|
||||
class Task_RequiredButFailedTask: public std::runtime_error {
|
||||
|
||||
/// Task_RequiredButFailedTask - Exception thrown when a Task fails but should not.
|
||||
class TaskException: public std::exception
|
||||
{
|
||||
public:
|
||||
Task_RequiredButFailedTask(): std::runtime_error("Task: Attempted to execute a Task that failed and was required.") {}
|
||||
/** Constructor (C strings).
|
||||
* @param message C-style string error message.
|
||||
* The string contents are copied upon construction.
|
||||
* Hence, responsibility for deleting the char* lies
|
||||
* with the caller.
|
||||
*/
|
||||
explicit TaskException(const char* message):
|
||||
msg_(message)
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor (C++ STL strings).
|
||||
* @param message The error message.
|
||||
*/
|
||||
explicit TaskException(const std::string& message):
|
||||
msg_(message)
|
||||
{}
|
||||
|
||||
/** Destructor.
|
||||
* Virtual to allow for subclassing.
|
||||
*/
|
||||
virtual ~TaskException() throw (){}
|
||||
|
||||
/** Returns a pointer to the (constant) error description.
|
||||
* @return A pointer to a const char*. The underlying memory
|
||||
* is in posession of the Exception object. Callers must
|
||||
* not attempt to free the memory.
|
||||
*/
|
||||
virtual const char* what() const throw (){
|
||||
return msg_.c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Error message.
|
||||
*/
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
/// Task_RequiredButFailedTask - Exception thrown when a Task is failed but required, and rectification also failed but
|
||||
/// returned with a zero exit code (dont try to fool the check).
|
||||
class Task_RequiredButRectifierDoesNotHeal: public std::runtime_error {
|
||||
public:
|
||||
Task_RequiredButRectifierDoesNotHeal(): std::runtime_error("Task: The rectification script was executed and reported success, but did not actually heal the faulty condition of the Task target.") {}
|
||||
};
|
||||
|
||||
/// Task::Task() - Constructor for the Task class. The Task is the building block of a Plan indicating of which Unit to
|
||||
/// execute, and its dependencies on other units to have already been completed successfully.
|
||||
@@ -81,8 +113,12 @@ void Task::load_root(Json::Value loader_root, bool verbose )
|
||||
if ( des_dep_root[i].asString() != "" ) {
|
||||
this->dependencies.push_back( des_dep_root[i].asString() );
|
||||
if ( verbose ) {
|
||||
std::cout << "Added dependency \"" << des_dep_root[i].asString()
|
||||
<< "\" to task \"" << this->get_name() << "\"." << std::endl;
|
||||
std::ostringstream infostring;
|
||||
infostring << "Added dependency \"" << des_dep_root[i].asString() << "\" to task \""
|
||||
<< this->get_name() << "\"." << std::endl;
|
||||
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,8 +138,12 @@ void Task::load_definition( Unit selected_unit, bool verbose )
|
||||
{
|
||||
this->definition = selected_unit;
|
||||
if ( verbose ) {
|
||||
std::cout << "Loaded definition \"" << selected_unit.get_name() << "\" for task \""
|
||||
std::ostringstream infostring;
|
||||
infostring << "Loaded definition \"" << selected_unit.get_name() << "\" for task \""
|
||||
<< this->get_name() << "\"." << std::endl;
|
||||
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
this->defined = true;
|
||||
}
|
||||
@@ -137,13 +177,16 @@ bool Task::has_definition()
|
||||
/// Task::execute - execute a task's unit definition.
|
||||
/// See the design document for what flow control needs to look like here.
|
||||
/// \param verbose - Verbosity level - not implemented yet.
|
||||
void Task::execute( bool verbose )
|
||||
void Task::execute( Conf * configuration, bool verbose )
|
||||
{
|
||||
// DUFFING - If Examplar is broken it's probably going to be in this block.
|
||||
// Somebody come clean this up, eh?
|
||||
|
||||
// PREWORK
|
||||
// throw if unit not coupled to all necessary values since Task is stateful (yes, stateful is okay)
|
||||
if (! this->has_definition() ) {
|
||||
std::ostringstream infostring;
|
||||
if ( ! this->has_definition() )
|
||||
{
|
||||
throw Task_NotReady();
|
||||
}
|
||||
|
||||
@@ -151,86 +194,224 @@ void Task::execute( bool verbose )
|
||||
std::string task_name = this->definition.get_name();
|
||||
// END PREWORK
|
||||
|
||||
|
||||
// get the target execution command
|
||||
std::string target_command = this->definition.get_target();
|
||||
|
||||
// if we're in verbose mode, do some verbose things
|
||||
if ( verbose ) {
|
||||
std::cout << "\tUsing unit \"" << task_name << "\"." << std::endl;
|
||||
std::cout << "\tExecuting target \"" << target_command << "\"." << std::endl;
|
||||
if ( verbose )
|
||||
{
|
||||
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tUsing unit \"" << task_name << "\"." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
|
||||
// check if context override
|
||||
if ( configuration->has_context_override() )
|
||||
{
|
||||
// if so, set the CWD.
|
||||
chdir( configuration->get_execution_context().c_str() );
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tExecution context: " << get_working_path() << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
|
||||
// execute target
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tExecuting target \"" << target_command << "\"." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
|
||||
// a[0] execute target
|
||||
int return_code = Sproc::execute( target_command );
|
||||
|
||||
// d[0] check exit code of target
|
||||
if (return_code == 0) {
|
||||
// Zero d[0] return from target execution, good to return
|
||||
// **********************************************
|
||||
// d[0] Error Code Check
|
||||
// **********************************************
|
||||
if ( return_code == 0 )
|
||||
{
|
||||
// d[0].0 ZERO
|
||||
|
||||
if ( verbose ) {
|
||||
std::cout << "\tTarget " << task_name << " succeeded." << std::endl;
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tTarget " << task_name << " succeeded. Marking as complete." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
}
|
||||
this->mark_complete();
|
||||
// next
|
||||
} else {
|
||||
// Non-Zero d[0] from initial target execution, get to d[1]
|
||||
std::cout << "\tTarget \"" << task_name << "\" failed with exit code " << return_code << "." << std::endl;
|
||||
|
||||
// check if rectify pattern is enabled d[1]
|
||||
if ( this->definition.get_rectify() ) {
|
||||
// yes d[1]
|
||||
std::cout << "\tRectification pattern is enabled for \"" << task_name << "\"." << std::endl;
|
||||
// execute RECTIFIER
|
||||
// a[1] NEXT
|
||||
return;
|
||||
}
|
||||
|
||||
if ( return_code != 0 )
|
||||
{
|
||||
// d[0].1 NON-ZERO
|
||||
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tTarget \"" << task_name << "\" failed with exit code " << return_code << "." << std::endl;
|
||||
|
||||
syslog(LOG_ERR, infostring.str().c_str() );
|
||||
std::cerr << infostring.str();
|
||||
|
||||
// **********************************************
|
||||
// d[1] Rectify Check
|
||||
// **********************************************
|
||||
if (! this->definition.get_rectify() )
|
||||
{
|
||||
// d[1].0 FALSE
|
||||
|
||||
// **********************************************
|
||||
// d[2] Required Check
|
||||
// **********************************************
|
||||
if (! this->definition.get_required() )
|
||||
{
|
||||
// d[2].0 FALSE
|
||||
// a[2] NEXT
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tThis task is not required to continue the plan. Moving on." << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this->definition.get_required() )
|
||||
{
|
||||
// d[2].1 TRUE
|
||||
// a[3] EXCEPTION
|
||||
throw TaskException("Task \"" + task_name + "\" is required, and failed, and rectification is not enabled.");
|
||||
}
|
||||
// **********************************************
|
||||
// end - d[2] Required Check
|
||||
// **********************************************
|
||||
}
|
||||
|
||||
|
||||
if ( this->definition.get_rectify() )
|
||||
{
|
||||
// d[1].1 TRUE (Rectify Check)
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tRectification pattern is enabled for \"" << task_name << "\"." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
|
||||
// a[4] Execute RECTIFIER
|
||||
std::string rectifier_command = this->definition.get_rectifier();
|
||||
std::cout << "\tExecuting rectification: " << rectifier_command << "." << std::endl;
|
||||
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tExecuting rectification: " << rectifier_command << "." << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
|
||||
int rectifier_error = Sproc::execute( rectifier_command );
|
||||
|
||||
// d[3] check exit code of rectifier
|
||||
if (rectifier_error) {
|
||||
//d[3] non-zero
|
||||
|
||||
std::cout << "\tRectification of \"" << task_name << "\" failed with exit code "
|
||||
// **********************************************
|
||||
// d[3] Error Code Check for Rectifier
|
||||
// **********************************************
|
||||
if ( rectifier_error != 0 )
|
||||
{
|
||||
// d[3].1 Non-Zero
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tRectification of \"" << task_name << "\" failed with exit code "
|
||||
<< rectifier_error << "." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
|
||||
// d[2] check if REQUIRED
|
||||
if ( this->definition.get_required() ) {
|
||||
// d[2] yes
|
||||
// halt/exception
|
||||
throw Task_RequiredButFailedTask();
|
||||
}
|
||||
// d[2] no
|
||||
// next
|
||||
}
|
||||
// d[3] zero
|
||||
std::cout << infostring.str();
|
||||
|
||||
// **********************************************
|
||||
// d[4] Required Check
|
||||
// **********************************************
|
||||
if ( ! this->definition.get_required() ) {
|
||||
// d[4].0 FALSE
|
||||
// a[5] NEXT
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tThis task is not required to continue the plan. Moving on." << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this->definition.get_required() )
|
||||
{
|
||||
// d[4].1 TRUE
|
||||
// a[6] EXCEPTION
|
||||
throw TaskException("Task \"" + task_name + "\" is required, and failed, then rectified but rectification failed.");
|
||||
}
|
||||
// **********************************************
|
||||
// end - d[4] Required Check
|
||||
// **********************************************
|
||||
}
|
||||
|
||||
// d[3] check exit code of rectifier
|
||||
if ( rectifier_error == 0 )
|
||||
{
|
||||
// d[3].0 Zero
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tRectification returned successfully." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
|
||||
// a[7] Re-execute Target
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tRe-Executing target \"" << this->definition.get_target() << "\"." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
|
||||
// execute target
|
||||
std::cout << "\tRe-Executing target \"" << this->definition.get_target() << "\"." << std::endl;
|
||||
int retry_code = Sproc::execute( target_command );
|
||||
|
||||
// d[4] exit code of target retry
|
||||
if (retry_code == 0) {
|
||||
// d[4] zero
|
||||
// **********************************************
|
||||
// d[5] Error Code Check
|
||||
// **********************************************
|
||||
if ( retry_code == 0 )
|
||||
{
|
||||
// d[5].0 ZERO
|
||||
// a[8] NEXT
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tRe-execution was successful." << std::endl;
|
||||
syslog( LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
return;
|
||||
}
|
||||
// d[4] non-zero
|
||||
// d[5] required check
|
||||
if ( this->definition.get_required() ) {
|
||||
// d[5] yes
|
||||
std::cout << "\tTask \"" << task_name << "\" is required but rectification did not heal." << std::endl;
|
||||
throw Task_RequiredButRectifierDoesNotHeal();
|
||||
|
||||
if ( retry_code != 0 )
|
||||
{
|
||||
// d[5].1 NON-ZERO
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tRe-execution failed with exit code " << retry_code << "." << std::endl;
|
||||
syslog(LOG_ERR, infostring.str().c_str() );
|
||||
std::cerr << infostring.str();
|
||||
|
||||
// **********************************************
|
||||
// d[6] Required Check
|
||||
// **********************************************
|
||||
if ( ! this->definition.get_required() )
|
||||
{
|
||||
// d[6].0 FALSE
|
||||
// a[9] NEXT
|
||||
infostring = std::ostringstream();
|
||||
infostring << "\tThis task is not required to continue the plan. Moving on." << std::endl;
|
||||
syslog(LOG_INFO, infostring.str().c_str() );
|
||||
std::cout << infostring.str();
|
||||
|
||||
return;
|
||||
}
|
||||
// d[5] no
|
||||
// next
|
||||
|
||||
if ( this->definition.get_required() )
|
||||
{
|
||||
// d[6].1 TRUE
|
||||
// a[10] EXCEPTION
|
||||
throw TaskException("Task \"" + task_name + "\" is required, and failed, then rectified but rectifier did not heal the condition causing the target to fail. Cannot proceed with Plan.");
|
||||
}
|
||||
// no d[1]
|
||||
std::cout << "\tRectification is not enabled for \"" << task_name << "\"." << std::endl;
|
||||
// required d[2]
|
||||
if ( this->definition.get_required() ) {
|
||||
// d[2] yes
|
||||
// This is executing.....
|
||||
std::cout << "\tThis task is required to continue the plan." << std::endl;
|
||||
// but these are NOT executing?????
|
||||
throw Task_RequiredButFailedTask();
|
||||
} // d[2] no
|
||||
std::cout << "\tThis task is not required to continue the plan." << std::endl;
|
||||
// **********************************************
|
||||
// end - d[6] Required Check
|
||||
// **********************************************
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// **********************************************
|
||||
// end d[1] Rectify Check
|
||||
// **********************************************
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,15 @@
|
||||
#ifndef FTESTS_TASK_H
|
||||
#define FTESTS_TASK_H
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include "../json/json.h"
|
||||
#include "Unit.h"
|
||||
#include "Suite.h"
|
||||
#include "Conf.h"
|
||||
|
||||
class Task
|
||||
{
|
||||
|
||||
protected:
|
||||
// the name of this task
|
||||
std::string name;
|
||||
@@ -61,7 +64,7 @@ class Task
|
||||
std::string get_name();
|
||||
|
||||
// execute this task's definition
|
||||
void execute( bool verbose );
|
||||
void execute( Conf * configuration, bool verbose );
|
||||
|
||||
void mark_complete();
|
||||
|
||||
|
||||
@@ -25,3 +25,9 @@ bool exists(const std::string& name)
|
||||
struct stat buffer;
|
||||
return (stat (name.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
std::string get_working_path()
|
||||
{
|
||||
char temp[MAXPATHLEN];
|
||||
return ( getcwd(temp, MAXPATHLEN) ? std::string( temp ) : std::string("") );
|
||||
}
|
||||
@@ -22,9 +22,12 @@
|
||||
#define FTESTS_HELPERS_H
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
bool exists (const std::string& name);
|
||||
|
||||
|
||||
std::string get_working_path();
|
||||
|
||||
#endif //FTESTS_HELPERS_H
|
||||
|
||||
Reference in New Issue
Block a user