CLI and config file parsers

Introduction

libivdparser.so consists of:

Parsing configuration files and all arguments uses the same functionality with the exception of the syntax checker (lex/yacc vs. ap parser).

General rules

There are three types of command arguments:

All operations and options are available in long form (--option).

Single character form (-x) available for often used operations/options:

Single character options can be grouped together (if there is no parameter required)

Common operations between all commands:

Common options to change behaviour: --no-header (do not print headers - used for easy parsing) --no-monitor (for commands that start jobs - run job in background) --quiet (do not print anything - do we need this?) --detail (print full detail - all fields of records in multi-line format) --dbg (turn on debugging)

Same operations to be used in different command if there is same (similar) meaning: --add (-a) --modify (-m) --remove (no short form) --list (-l) --show (-s) --status (-t)

Where arguments are required, they should (in general) do not require additional option to specify ivdpool --modify PoolName NewPoolCfg Where Filename is specified, "-" can be specified (means standard input) - not yet implemented

Syntax example for ivdjob cli:

Usage: ivdjob
{{-l | --list} [{-M | --migration}] [{-R | --recall}] [{-A | --admin}] [{-C | --recovery}] [{-T | --maint}] [Partition name] |
 {-s | --show} JobID... |
 {-a | --abort} JobID... |
 {-p | --priority} JobID {Number | {-I | --incr} Number | {-D | --decr} Number} |
 {-h | --help} |
 {-v | --version}
 } [--dbg [DBGlevel [DBGfile [DBGflags]]]] {[{-Q | --quiet}] | [--detail]}
    

Syntax explanation: Options and commands are represented by '-' or '--' prefix. Parameters or values are standing alone words without '-' or '--' prefix. All of them are arguments. Arguments can be collected in groups. Group of argument is stil an argument. Each argument can have its own group of arguments.

param represent one value. param... represent list of values of the same type. list of values can be used as last parameter of particular option/command Example of right notation: cli {--action Number File... | --help} [--dbg] Example of WRONG notation: cli {--action File... Number | --help} [--dbg]

arg arguments without [] braces are mandatory. [arg] arg is not mandatory. It's present or not. {arg...} exclusive group of arguments

-x is used for short option or command notation --xx is used for large option or command notation

Compund examples: {a1 | a2 | a3 } represent group of tree argument, but only one must be choiced [{a1 | a2 | a3 }] represent group of tree argument, non or only one can be choiced { -x | --xxx } is a mandatory group of two exclusive arguments actualy this notation represent short and long notification of the sam ecommand [{ -x | --xxx }] is not mandatory group of two exclusive arguments.

Special features: Short notation can be merged together using only one '-' prefix Merging works only for short options from same group. Example: ivdjob --list -mar mypartition Command write out list of migration, admin and recall jobs from partition mypartition.

SYNOPSIS: Each cli consist from exclusive list of commands and general options. cli {exclusive list of commands} [general options]

If cli has only one sense usage then exclusive command list could be avoided. The --help command must be present in every cli.

Example of cli without special specified action. cli [--help] [general options] without general options cli [--help]

How to create and use a new cli argument parser?

topics: 1. Define cli syntax in detail 2. Add syntax structure to /lib/parser/cfg_cliarg.cpp source file 3. Create cfg_Clixxx class that is derive from cfg_CliBase in the same source file. 4. Create cli source for binary in ivdsrc directory use ivdjob for template

Define cli systax in detail. Exapmple:

Usage: ivdjob
{{-l | --list} [{-M | --migration}] [{-R | --recall}] [{-A | --admin}] [{-C | --recovery}] [{-T | --maint}] [Partition name] |
 {-s | --show} JobID... |
 {-a | --abort} JobID... |
 {-p | --priority} JobID {Number | {-I | --incr} Number | {-D | --decr} Number} |
 {-h | --help} |
 {-v | --version}
 } [--dbg [DBGlevel [DBGfile [DBGflags]]]] {[{-Q | --quiet}] | [--detail]}
    

In /lib/parser/cfg_cliarg.cpp source file have to be done the following steps: From top of the file are constructors for particular cli commands. To add new cli possition after last ap_Argument object construction.

Each cli has its own ap_Argument object constructed. Example fo ivdJob:

const ap_Argument ivdJob = ap_Argument("ivdjob")
    

Argument in constructor represent cli command name.

Arbitrary cli command constructor looks like this:

const ap_Argument <object name> = ap_Argument("<command name>")
    

Add general options to cli: Example how to add already defined debug option and detail | quite option.

const ap_Argument ivdJob = ap_Argument("ivdjob")
    << ap_opDebug
    << ap_ogOutQuantum;
    

The systax of current structure is

ivdjob [--dbg [DBGlevel [DBGfile [DBGflags]]]] {[{-Q | --quiet}] | [--detail]}
    

Commands ap_cmHelp and ap_cmVersion are already defined and must be always add.

Example for ivdjob actions:

const ap_Argument ap_cmShowJob  = ap_Argument(cc_SHOW, "show", "s")
    << ap_paListOfJobID;

const ap_Argument ap_cmAbortJob = ap_Argument(cc_ABORT, "abort", "a")
    << ap_paListOfJobID;
    
const ap_Argument ap_cgJob = ap_Argument() // all possible commands
	<< ap_cmShowJob
	<< ap_cmAbortJob
    << ap_cmHelp
    << ap_cmVersion;

const ap_Argument ivdJob = ap_Argument("ivdjob")
    << ap_cgJob
    << ap_opDebug
    << ap_ogOutQuantum;
    

The systax of current structure is

ivdjob 
{{-s | --show} JobID... |
 {-a | --abort} JobID... |
 {-h | --help} |
 {-v | --version}
 } [--dbg [DBGlevel [DBGfile [DBGflags]]]] {[{-Q | --quiet}] | [--detail]}
    

Each parameter argument has to be defined individualy.

Example for definition of jobIDs:

const ap_Argument ap_paListOfJobID   (at_multypar , pr_mandatory, "JobID");
    

constructor arguments: at_multypar type of argument is multy parameter (value) pr_mandatory presence is mandatory "JobID" parameter long name

ap_Argument objects that have same properties and structure can be shared.

See other construction of particular argument and create all of them.

When all syntax structure is added to cli then simple unit test can be run. go to the /test/ut/lib/parser directory file ut_ap.cpp and add following code into it. Words in <> change with proper values.

    {        
        ut_Basic ut("xxxxx", "<cli> syntax");
        try {
            cout << <ap_Argument object> << endl;
	        ut.SetStatus( true );
        } catch (ivd_Exception& e) {
            cout << endl << e.GetFriendly() << endl;
        }
    }
    char <cliComm>[] = "<cli> <command to parse>";
    {        
        int argc = ArgcArgv(<cliComm>, argv);
        ut_Basic ut("15", "<cli> syntax");
        try {
            pf_ValueList parsed;
            <ap_Argument object>.Parse(argc, argv, parsed);
            cout << parsed;
	        ut.SetStatus( true );
        } catch (ivd_Exception& e) {
            cout << endl << e.GetFriendly() << endl;
        }
    }
    

First basic test just dumps out the syntax. The second one parse out the passing command. If all is OK then continue.

Create cfg_Clixxx class. Use alredy defined classes as template. All fields that is need in cli have to be present in new class. Many common fileds are already set in cfg_CliBase class. In new class goes only fields that are special for new cli.

Each cfg_Clixxx class have to have two methods: constructor and Help class implemented.

Example of cfg_CliJob class:

class cfg_CliJob : public cfg_CliBase {
public:
    cfg_CliJob(UInt32_t a_argc, char* a_argv[]);
    virtual ~cfg_CliJob() {};

    bool    migration;
    bool    recall;
    bool    admin;
    bool    recovery;
    bool    maint;
    
    vector< val_Element<UInt64_t> >   jobIds;
    // what kind of evaluation  increment or decrement
    string  priorityEval;
    UInt32_t  priority;

    virtual void HelpDump();
private:
    log_CLASSID_m;
};
    

There are olny few members that ivdjob needs. All others are alredy defined in cfg_CliBase class.

Rule to bind Class member and arguments are defined in constructor:

Example: --migration optios in bind as bool to migration member.

    GetRules().Add(
        new val_Bool(
            ap_opJobTypeMig.m_long,val_Limit(MIN_CLI_ARGLEN, MAX_CLI_ARGLEN)),         
        this->migration
    );