Most syntax elements are tweakeable, (it's not universal though). So play with the code. The current defined rules are:
- sections are called "contexts", they are not nestable
- a context begins with <contextname> and ends with </contextname>
- inside a context you can use name=value pairs, called "directives"
- "\" can be used to break a directive in multiple lines
- spaces, tabs and enters are free (won't be taken into account by the parser)
- ";" is both used as a comment mark or optional line delimiter, that is, "everything after a semicolon is ignored, wherever it appears"
Perhaps its not very usable at this point but who knows, maybe someone finds it useful. I should warn that its "play" code, IOW can contain any kind of bugs, its awful to read, and all that stuff.
code:
Code: Select all
# config parser
# name: parseini
# policy: read
# LOCALS -----------------------------------------------------------
# name, size and contents of config script
:local CFName "my.ini"
:local CFContents [/system script get [find name=$CFName] source];
:local CFLen [:len $CFContents];
# line elements
:local EOL 0;
:local LastEOL 0;
:local EOLMark "\n";
:local EOF false;
:local CFLine "";
:local CFLineNumber -1;
:local CFLinetemp "";
:local CFLineBuffer "";
# delimiters
:local ContextStartOpen "<";
:local ContextStartClose ">";
:local ContextEndOpen "</";
:local ContextEndClose ">";
:local SentenceDelimiter ";";
:local SentenceContinuation "\\";
:local Assignment "=";
:local ListDelimiter ",";
:local Delimiters "$ContextStartOpen$ContextStartClose$ContextEndOpen$ContextEndClose$SentenceDelimiter$SentenceContinuation$Assignment$ListDelimiter";
# character sets
:local Lowercase "abcdefghijklmnopqrstuvwxyz";
:local Uppercase "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
:local Numbers "0123456789";
:local ExtraChars "-_.";
:local ValidChars "$Lowercase$Uppercase$Numbers$Delimiters$ExtraChars";
:local IgnoreChars "\t\_\r\n";
# context / directives config
:local ValidContexts "global,monitor,chain,debug";
:local CurrentContext "";
# name, mandatory, multiple
:local ContextsConfig {\
"global, true, false";\
"monitor, false, false";\
"chain, false, true";\
"debug, false, false";\
};
# context, name, mandatory, multiple, value_type, value_default
:local DirectivesConfig {\
"global, default_route_selector, false, false, str, nth";\
"global, default_l7_persistence, false, false, str, no";\
"global, default_pcc_persistence, false, false, str, src-address";\
"global, default_l7_entry_expiration, false, false, str, 30m";\
"monitor, enabled, false, false, bool, false";\
"monitor, tests_per_route, false, false, num, 10";\
"monitor, public_hosts, false, false, array, ";\
"monitor, dynamic_scheduling, false, false, bool, false";\
"chain, name, true, false, string, ";\
"chain, local_interfaces, true, false, array, ";\
"chain, lists, false, false, array, ";\
"chain, primary_routes, true, false, array, ";\
"chain, secondary_routes, false, false, array, ";\
"chain, route_selector, false, false, str, ";\
"chain, l7_persistence, false, false, str, ";\
"chain, l7_entry_expiration, false, false, str, ";\
"debug, entry_exit, false, false, bool, false";\
"debug, locking, false, false, bool, false";\
"debug, route_info, false, false, bool, false";\
"debug, misc, false, false, bool, false";\
"debug, rtmon_dynsched, false, false, bool, false";\
"debug, rtmon_tests, false, false, bool, false";\
};
# START -----------------------------------------------------------
:do {
# get line and update markers
:set CFLineNumber ($CFLineNumber+1);
:set EOL [:find $CFContents $EOLMark $LastEOL];
:set EOF ($EOL >= $CFLen);
# have into account that the last line could not end with EOLMark
:if (!$EOF) do={
:set CFLinetemp [:pick $CFContents $LastEOL $EOL];
:set LastEOL ($EOL+[:len $EOLMark]);
} else={
:set CFLinetemp [:pick $CFContents $LastEOL $CFLen];
# this is b/c an apparent bug in ROS
:set EOF true;
};
# trim ignored chars and end of sentence
# in case of invalid char stop
:set CFLine "";
:local charpos 0;
:do {
:local character [:pick $CFLinetemp $charpos ($charpos+1)]
:if ([:typeof [:find $ValidChars $character]]="num") do={
:if ($character=$SentenceDelimiter) do={
:set charpos ([:len $CFLinetemp]-1);
} else={
:set CFLine "$CFLine$character";
};
} else={
:if ([:typeof [:find $IgnoreChars $character]]="nil") do={
:error "parseini: invalid char, config file \"$CFName\", line $($CFLineNumber+1), column $($charpos+1)";
};
};
:set charpos ($charpos+1);
} while ($charpos < [:len $CFLinetemp]);
# context analysis
# valid lines will go to config vars
# if error detected stop
:if ($EOF || [:len $CFLine]>0) do={
:if ($CurrentContext="") do={
# null context: expect a context start
if ([:pick $CFLine 0 [:len $ContextStartOpen]]!=$ContextStartOpen || [:pick $CFLine ([:len $CFLine]-[:len $ContextStartClose]) [:len $CFLine]]!=$ContextStartClose) do={
:error "parseini: \"$CFLine\" context start expected, config file \"$CFName\", line $($CFLineNumber+1)";
};
# validate context name
:set CurrentContext [:pick $CFLine [:len $ContextStartOpen] ([:len $CFLine]-[:len $ContextStartClose])];
:if ([:typeof [:find $ValidContexts $CurrentContext]]="nil") do={
:error "parseini: \"$CurrentContext\" is not a valid context, config file \"$CFName\", line $($CFLineNumber+1)";
};
# uncomment to print context start
:put $CFLine;
:set CFLine "";
} else={
# valid context
# if we found a context end validate it
if ([:pick $CFLine 0 [:len $ContextEndOpen]]=$ContextEndOpen) do={
if ([:pick $CFLine [:len $ContextEndOpen] ([:len $CFLine]-[:len $ContextEndClose])] != $CurrentContext) do={
:error "parseini: expected \"$ContextEndOpen$CurrentContext$ContextEndClose\" or valid directive, config file \"$CFName\", line $($CFLineNumber+1)";
};
# uncomment to print context end
:put $CFLine;
# if we had a continuation buffer close it, otherwise clean line
if ($CFLineBuffer!="") do={
:set CFLine $CFLineBuffer;
:set CFLineBuffer "";
} else={
:set CFLine "";
};
# close context
:set CurrentContext "";
} else={
# if we hit EOF and the context isn't closed exit with error
if ($EOF) do={
:error "parseini: EOF when a \"$ContextEndOpen$CurrentContext$ContextEndClose\" was expected, config file \"$CFName\", line $($CFLineNumber+1)";
};
# invalidate nested context start
if ([:pick $CFLine 0 [:len $ContextStartOpen]]=$ContextStartOpen) do={
:error "parseini: invalid directive \"$CFLine\" (apparent nested context), config file \"$CFName\", line $($CFLineNumber+1)";
};
# detect if we have to continue a line or start a multiple line directive
if ($CFLineBuffer!="") do={
:set CFLine "$CFLineBuffer$CFLine";
:set CFLineBuffer "";
};
if ([:pick $CFLine ([:len $CFLine]-[:len $SentenceContinuation]) [:len $CFLine]]=$SentenceContinuation) do={
:set CFLineBuffer [:pick $CFLine 0 ([:len $CFLine]-[:len $SentenceContinuation])];
};
};
# if the continuation buffer is empty we have a directive for validation
if ($CFLineBuffer="" && $CFLine!="") do={
:put "$CFLine"
};
};
};
} while (!$EOF);
Code: Select all
;===============================================================================
; config global
;===============================================================================
<global>
; definiciones de rutas a usar como primaria o secundaria
system-routes = vboxnet0, vboxnet1, vboxnet2, vboxnet3
; defaults para directivas en definiciones de cadenas
; si no estan definidos, los valores son los que se indican aqui
; default-route-selector = nth
; default-l7-persistence = no
; default-pcc-persistence = src-address
; default-l7-entry-expiration = 30m
</global>
;===============================================================================
; configuracion del monitor
;===============================================================================
<monitor>
; si el monitor se habilita (o sea se incluye en el scheduler)
enabled = yes
; si se habilita scheduler dinamico (default: no)
dynamic-scheduling = yes
; cantidad de hosts a pingear x ruta (default 10)
tests-per-route = 7
; hosts publicos para realizar tests de ruta
public-hosts = 198.41.0.4,192.228.79.201,192.33.4.12,128.8.10.90,192.203.230.10,\
192.5.5.241,192.112.36.4,128.63.2.53,192.36.148.17,192.58.128.30,\
193.0.14.129,199.7.83.42,202.12.27.33,192.43.244.18,91.189.94.4;
</monitor>
;===============================================================================
; diversos tipos de mensaje a mostrar en consola y logs
;===============================================================================
<debug>
entry-exit = true ; entrada y salida de scripts
locking = true ; info de locks de ejecucion
route-info = true ; info de estado de rutas
rtmon-dynsched = true ; info de scheduling dinamico del monitor
rtmon-tests = true ; info de tests de ruta del monitor
misc = true ; mensajes varios
</debug>
;===============================================================================
; definiciones de cadenas
;===============================================================================
<chain>
name = vboxnet0
local-interfaces = internalnet
lists = vboxnet0
primary-routes = vboxnet0
secondary-routes = any
</chain>
<chain>
name = vboxnet1
local-interfaces = internalnet
lists = vboxnet1
primary-routes = vboxnet1
secondary-routes = any
</chain>
<chain>
name = vboxnet2
local-interfaces = internalnet
lists = vboxnet2
primary-routes = vboxnet2
secondary-routes = any
</chain>
<chain>
name = vboxnet3
local-interfaces = internalnet
lists = vboxnet3
primary-routes = vboxnet3
secondary-routes = any
</chain>
<chain>
name = balance-round-robin
local-interfaces = internalnet
lists = balance-round-robin
primary-routes = vboxnet0, vboxnet1
secondary-routes = any
route-selector = nth
l7-persistence = no
</chain>
<chain>
name = balance-src-random
local-interfaces = internalnet
lists = balance-src-random
primary-routes = vboxnet2, vboxnet3, vboxnet0
secondary-routes = any
route-selector = random
l7-persistence = src-address
l7-entry-expiration = 15m
</chain>
<chain>
name = balance-pcc-dstsrc
local-interfaces = internalnet
lists = balance-pcc-dstsrc
primary-routes = vboxnet1, vboxnet0
secondary-routes = vboxnet2
route-selector = random
l7-persistence = both-addresses-and-ports
</chain>