4.1. Expression Language

4.1.1. Basics

4.1.1.1. Supported values types

The language supports many value types. Values can be stored inside variables, used in expressions, passed to/returned by functions, ecc.

Some values have no literal rappresentation in the language. This means that you can create/use them (es: as a function return value), but you can not rappresent them as a sequence of character in the expression string).

Base values

Types

Description

Literal rappresentation

Example

numbers

intergers and floats values

number literal

123

booleans

true or false values

boolean literal

True , False

characters

A single unicode point

string literal (ascii characters & escaped)

"w" "n" "xa0"

strings

Sequence of unicode code points.

string literal (ascii characters & escaped)

"world", 'world'

byte strings

Sequence of 8-bit values.

bytes

8-bit values

Special values used when working with SNMP protocol.

Types

Description

Literal rappresentation

Example

OIDs

OID strings (1.3.6.1.2.1.1)

OID literal

1.3.6.1.2.1.1.1

Data structures

Types

Description

Literal rappresentation

Example

arrays

Sequence of other values

[ .... ]

[ 'a', 3.14, "foo" ]

dictionaries

Mapping objects

objects

Objects (with fields and methods)

Objects

TODO

4.1.1.2. Unicode String and Byte strings (arrays)

byte strings consists of sequences of 8-bit unsigned values, while unicode strings consists of sequences of Unicode code points that represent textual characters from human languages.

Examples of byte strings:

b'hello'
b'Intel Corporation 82540EM Gigabit Ethernet Controller'
b'Citt\xc3\xa0'
b'Citt\xe0'

Examples of unicode strings:

'hello'
'Intel Corporation 82540EM Gigabit Ethernet Controller'
'Città'

Note

In Sanet, a byte string cannot be written as literal but can be obtained as result of a OID or a function.

4.1.1.2.1. Conversions

To obtain unicode strings from byte strings you have to decode them using the appropriate encoding.

b'hello'        --> decode ascii      --> 'hello'
b'Citt\xc3\xa0' --> decode utf-8      --> 'Città'
b'Citt\xe0'     --> decode iso-8859-1 --> 'Città'

To obtain byte strings from unicode strings you have to encode them using the appropriate encoding.

'hello'         --> encode ascii      --> b'hello'
'Città'         --> encode utf-8      --> b'Citt\xc3\xa0'
'Città'         --> encode iso-8859-1 --> b'Citt\xe0'

See encode for more details about encode and decode functions.

4.1.1.2.2. Comparisons

Between byte strings

You can compare and use operators like > and + between byte strings but if the byte strings have different encoding maybe you are doing it wrong.

For example, compare with == these 2 byte strings: b'Cittxc3xa0' b'Cittxe0' it probably does not make sense because it will return False even if they both have the same unicode strings representation (if decoded with utf-8 and iso-8859-1 respectively).

b'hello'        == b'hello'        --> True
b'Citt\xc3\xa0' == b'Citt\xc3\xa0' --> True
b'Citt\xc3\xa0' == b'Citt\xe0'     --> False

Between byte strings and unicode strings

In Sanet, byte strings and unicode strings can be used together with operators like >, + or == but with some limitations.

To safetly compare a byte string with a unicode string, Sanet assume that the unicode string is a ascii string. If the unicode string isn't a ascii string and exception will be raised.

To compare a byte string with a unicode string which is not a ascii string you must either explicitly decode the byte string or explicitly encoding the unicode string.

b'hello                          == 'hello'                 --> True
b'Citt\xc3\xa0'                  == 'Città'                 --> ERROR:  Operation not allowed between bytes (b'Citt\xc3\xa0') and non-ascii unicode string ('Città').
decode(b'Citt\xc3\xa0', 'utf-8') == 'Città'                 --> True
b'Citt\xc3\xa0'                  == encode('Città', 'utf-8) --> True

4.1.1.2.3. Functions

In Sanet, byte strings and unicode strings can be used together in functions with backward compatibility, where possible. In that case 'ascii' or 'utf-8' encode could be used as default.

4.1.1.3. Language Literals

4.1.1.3.1. Numbers

Numbers can be expressed as:

Integer values

Sequence of decimal characters starting with a decimal.

Examples:

expression

value

1

1

-1982

-1982

Floating point values

They follow the following sintax:

integer part . decimal part [ (e | E) ( - | + ) exponent ]

Examples:

expression

value

1.0

1.0

3.14

3.14

4.0e+10

40000000000.0

1.0E-3

0.0001

Hexadecimals values

Sequence of hexadecimal characaters with the prefix 0x.

Examples:

expression

value

0x1

1

0xff

255

0xFF

255

0x80000

32768

Octal values

Sequence of octal characters startring with 0.

Examples:

expression

value

01

1

07

7

010

8

011

9

4.1.1.3.2. Booleans

Booleans values can be true or false. These values are represented by the constants: True or False.

Example:

Expression

Value

True

true

False

false

4.1.1.3.3. Strings

A string is a sequence of characters delimited by single quotes or double quotes:

  • 'a', 'b', 'c'

  • "a", "b", "c"

  • "foo", 'bar'

  • "Hello world"

Character escaping

Few special characters can be inserted into a string by prepending the escape character *.

Escape sequence

Character

Description

\t

tab

\b

Back Space

\n

new line

\f

line feed

\r

Carriage Return

\"

"

double quote

\'

'

single quote

\ \

\

backslash

You can use escaped characters inside strings delimited by single (')or double quotes (").

Examples

Input string

Value

"Hello\nWorld"

Hello World

"Hello 'World'"

Hello 'World'

'Hello "World"'

Hello "World"

'Hello \'World\' '

Hello 'World'

"Hello \"World\""

Hello "World"

4.1.1.3.3.1. Disable string escaping

It is possible to disable string escaping by prepending the prefix r to any string.

r " ... "

r ' ... '

Examples

Input string

Value

Description

"Hello\nWorld"

Hello World

"Hello\\World"

Hello\World

"Hello\"World"

Hello"World

r"Hello\nWorld"

Hello\nWorld

unescaped

r"Hello\\nWorld"

Hello\\nWorld

unescaped

r"Hello\"nWorld"

Hello\"World

unescaped

4.1.1.3.4. OIDs

When speaking about SNMP, OIDs (or Object Identifiers) uniquely identify managed objects in a MIB hierarchy.

They are expressed with a sequence of at least three decimal strings separated by a dot ".".

IMPORTANT: An OID are a special kind of literal and can be used only inside SNMP sub-expression. You can not use OID literals inside mathematical expressions or comparisons.

Examples:

Expression

1.2.3

1.3.6.1.2.1.1.1.18.241

4.1.1.4. Vectors

Vectors are sequence values.

[ val1 [ , ... ] ]

Examples:

[]
[ 1, 2, 3, 4, "foo" ]
[ 1.3.6.1.2.1.1.1.0@ , 1.3.6.1.2.1.1.2.0@ ]

Functions and variables can be used to handle vectors and the sintax to access vector elements is:

expression [ n ]

Example:

[ 1, 2, 3, 'a', "foo", ][4]                             -> "foo"
"hello world"[6]                                        -> 'w'
my_function_returns_a_vector()[1] --> second value of the returned vector

Note

Strings are vectors or characters.

4.1.1.5. Dictionaries

A dictionary is an hashtable, a mapping object that maps values to arbitrary objects.

Functions and variables can be used to handle vectors and the sintax to access vector elements is:

*expression* [ *key* ]

Example:

dict(a=1,b="foo")["b"]                      --> "foo"

dict(a=1,b="foo",c2="bar")["c2"]            --> "bar"

my_function_returns_a_dictionary()["key1"]  --> value for the key "key1"

my_function_returns_a_dictionary()[1]       --> value for the key 1

4.1.1.6. Variables

A variable is represented by a initial $ followed by a sequence of alphanumeric characters or _.

Every variable has a value. The value can be a string, a number, a boolean, ecc.

Examples:

  • $foo

  • $BAR

  • $_

  • $foo_Bar

4.1.1.7. Functions

Functions are represented by a sequence of alphanumeric characters or _.

Examples:

  • power

  • round

  • sqrt

  • substr

4.1.1.8. Expressions

4.1.1.8.1. Operators

The language supports the following operators:

Arithmetical

Operator

Meaning

a + b

sum

a - b

subtraction

a / b

division

a % b

module division

a * b

multiplication

+a

positive

-a

negative

Comparison

Operator

Meaning

a == b , a eq b

a != b

a =~ b

a matches regular expression represented by b

a < b

a <= b

a > b

a >= b

BIT operators

Operator

Meaning

a & b

bit and

a | b

bit or

a ^ b

bit xor

~a

one's complement

Logical operators

Operator

Meaning

a && b , a and b

and

a || b , a or b

or

!a , not a

not

Object operators

Operator

Meaning

a . b

get attribute "b" from object "a"

4.1.1.8.2. Operators precedence

Operators listed in descening precedence.

Operators

Description

(), [] , "."

Call, range and attr operator

+,-,~,!,not

unary operator ( +10, -30, !b, not b)

||, or

&&, and


^

&

==, !=, ~= , eq

<, <=, >, >=

*

*, /, %

+, -

Warning

binary logical operators (|, &&, and, or) and binary bit operators (&, |, ^) are not short-circuited.

4.1.1.8.3. Function call

This is the syntax for function call:

function name ( [ param , param , .... ] [, name = param , name = param ] )

expression ( [ param , param , .... ] [, name = param , name = param ] )

You can specify simple parameters, also called positional parameters, and named parameters.

All named parameters can be specified in any order, but must appear after any other positional parameter.

Not all functions support named parameters. Read functions' documentation for more details.

Examples:

myfunction1()

myfunction2( 10 )

myfunction2( "Hello", 'World', 42 )

myfunction3( "Hello", 'World', 42 , timeout=10, shorttries=(3+2))

myfunction4(timeout=10)

myfunction5(probesize=1472, "localhost")  **WRONG PARAMETERS SEQUENCE**

4.1.1.8.4. Object attribute operator

When handling object values, you access to objects's attributes and methods with the "dot" (".") operator.

<obj> . <identifier>

Esample:

$node.__str__

"ciao".encode

(1.3.6.1.2.1.1.1.0@).snmp_port

4.1.1.9. SNMP Sub-expressions

An SNMP sub-expression, or simply SNMP expression, is a special syntax element of the language.

It is used to reference (and retrieve) the value of a managed object (OID) in the MIB hierarchy.

There are two kind of SNMP expressions and they reppresent two kind of values:

  • Current SNMP value

  • Old SNMP value

4.1.1.9.1. Current snmp values

These expressions reppresent the current value of an OID. When the SNMP expression is evaluated the value is read from the network throw SNMP protocol.

The full syntax is:

OID expr [ : [ community expr ] [ : [ version expr ] ] [ : [ timeout expr ] ] [ : [ tries expr ] ] ] @ [ [ node expr ] [ : [ port expr ] ] ]

Examples:

1.3.6.1.2.1.1.1.0@

1.3.6.1.2.1.1.1.0:$community@

1.3.6.1.2.1.1.1.0:$community:$snmpversion@

1.3.6.1.2.1.1.1.0::$snmpversion@

1.3.6.1.2.1.1.1.0@$node

1.3.6.1.2.1.1.1.0@$node:$snmpport

1.3.6.1.2.1.1.1.0@:$snmpport

1.3.6.1.2.1.1.1.0:$community:$snmpversion@$node

1.3.6.1.2.1.1.1.0:$community:$snmpversion@$node:$port

1.3.6.1.2.1.1.1.0@"mason"

1.3.6.1.2.1.1.1.0@$node+"1"

1.3.6.1.2.1.1.1.0@"mason":161

1.3.6.1.2.1.1.1.0@$node:$port

1.3.6.1.2.1.1.1.0:"public"@"mason":161

1.3.6.1.2.1.2.(0+2)."1.1.1":$community@$node:($port+0)

If one of the optional expressions is missing, the default value is taken from the execution context.

The execution context is a set of variables created when the expression evaluation starts. This is a SANET related topic and will be explained later in this document.

Expressions

type

Optional

context's variable name

*OID expr *

string

community expr

string

$community

version expr

integer

$snmpversion

timeout expr

float

$snmpversion

tries expr

integer

$shorttries

node expr

string

$node

port expr

integer

$snmpport

4.1.1.9.2. OID Expression ambiguities

The OID expr is a special type of expression. It must have the following sintax:

OID [ . expression [ . .... ] ]

Example:

1.3.6.1.2.1.(1+0).1.0@

1.3.6.1.2.1.(1+0).$instance.43.2.14@

An OID can contain the dot (".") character. This could lead to ambiguieties in the expression and an error

Examples: with $instance = 666 (integer) then:

1.3.6.1.2.1.$instance.43@
                |
                v
1.3.6.1.2.1.   666   .43@
1.3.6.1.2.1.$instance.43.1.1.oidString("123")@
                     |
                     v
1.3.6.1.2.1. $instance .43.1.1. oidString("123") @
                 |                     |
                 v                     v
1.3.6.1.2.1.    666    .43.1.1.   "3.49.50.51"   @
1.3.6.1.2.1.$instance.oidString("123")@
            |
            v
1.3.6.1.2.1.  $instance.oidString("123")  @
                         |
                         v
                      *ERROR*    can not apply method "upper()" on an value returned by "$instance".
1.3.6.1.2.1.$instance.oidString("123")@
            |
            v
1.3.6.1.2.1.  $instance.upper()  @
                         |
                         v
                      *ERROR*    can not apply method "upper()" on an value returned by "$instance".

4.1.1.9.3. Old snmp values

The syntax is:

OID [ : community ] [ : version ] # [ node [ : port ] ]

These sub-expressions represent the previous value of a managed object inside a MIB.

The term previous value means the value read throw SNMP the last time the expression containing the SNMP sub-expression was evaluated.

This is a SANET related topic and will be explained later in this document.

4.1.2. Statements

4.1.2.1. TRY/CATCH statement

This statement is useful when you need to detect and handle special errors/exceptions execution cases.

Read the section Gestione delle eccezioni nei controlli for more details about known and unknown exceptions types.

The syntax is:

try expression1 catch exception name1 ( expression2 )

When an exception of the specified type expression name1 is raised, the returned value is the value for expression2.

Examples:

try 1 + 1                    catch ValueError (666)                  ->  2

try 1.3.6.1.2.1.1.1.0@       catch ValueError (666)                  -> Linux debiantest 3.2.0-3-486 #1 Thu Jun 28 08:08:24 UTC 2012 i686

try 1/0                      catch ZeroDivisionError (666)           -> 666

try 1/0                      catch TimeoutException (666)            -> *EXCEPTION* -> *UNCHECKABLE*

try 1.3.6.1.2.1.1.1.2.3.4.5@ catch UndefinedSnmpValueException (666) -> 666

try 1.3.6.1.2.1.1.1.2.3.4.5@ catch TimeoutException (666)            -> *EXCEPTION* -> *UNCHECKABLE*

Danger

This statement will catch every exception:
try ..... catch Exception ( ... )

You SHOULD NEVE USE this statement, UNLESS YOU ARE REALLY SURE your expressions never raise dangerous exceptions.

You can omit the expression2 part:

try expression1 catch exception name1 ( )

Executes expression1 and returns the calculated value.

When an exception of the specified type expression name1 is raised, the returned value is a string with the exception description.

Examples:

try 1 + 1                    catch ValueError ()                  ->  2

try 1.3.6.1.2.1.1.1.0@       catch ValueError ()                  -> Linux debiantest 3.2.0-3-486 #1 Thu Jun 28 08:08:24 UTC 2012 i686

try 1/0                      catch ZeroDivisionError ()           -> ZeroDivisionError:division 1 / 0.0division 1 / 0.0
try 1/0                      catch TimeoutException ()            -> *EXCEPTION* -> *UNCHECKABLE*

try 1.3.6.1.2.1.1.1.2.3.4.5@ catch UndefinedSnmpValueException () -> 666
try 1.3.6.1.2.1.1.1.2.3.4.5@ catch TimeoutException ()            -> UndefinedSnmpValueException:Current variable 1.3.6.1.2.1.1.1.2.3.4.5:public@localhost:161 can't be read or is not present (GET)

Danger

This statement will catch every exception:

try ..... catch Exception ()

You SHOULD NEVE USE this statement, UNLESS YOU ARE REALLY SURE your expressions never raise dangerous exceptions.

You can omit the exception name:

try expression1 catch ( expression2 )

Executes expression1 and returns the calculated value.

When any exception is raised, the returned value is the value calculated by expression2.

Warning

any exception raised by expression2 is not catched.

Examples:

try 1 + 1                    catch (666)    ->  2

try 1.3.6.1.2.1.1.1.0@       catch (666)    -> Linux debiantest 3.2.0-3-486 #1 Thu Jun 28 08:08:24 UTC 2012 i686

try 1/0                      catch (666)    -> 666

try 1.3.6.1.2.1.1.1.2.3.4.5@ catch (666)    -> 666

Danger

You SHOULD NEVE USE this statement, UNLESS YOU ARE REALLY SURE your expressions never raise dangerous exceptions.

You can omit both exception name and secondary expression:

try expression1 catch ( )

Executes expression1 and returns the calculated value.

When any exception is raised, the returned value is a string with the exception's description.

Example:

try 1 + 1                    catch ()    ->  2

try 1.3.6.1.2.1.1.1.0@       catch ()    -> Linux debiantest 3.2.0-3-486 #1 Thu Jun 28 08:08:24 UTC 2012 i686

try 1/0                      catch ()    -> ZeroDivisionError:division 1 / 0.0division 1 / 0.0

try 1.3.6.1.2.1.1.1.2.3.4.5@ catch ()    -> UndefinedSnmpValueException:Current variable 1.3.6.1.2.1.1.1.2.3.4.5:public@localhost:161 can't be read or is not present (GET)

Danger

You SHOULD NEVE USE this statement, UNLESS YOU ARE REALLY SURE your expressions never raise dangerous exceptions.

4.1.2.1.1. Handle multiple exceptions

You can catch multiple exceptions with the same expression:

try expression1 catch ... catch ... catch ...

The statement will try to catch any exception in order.

Examples:

try  1/0          catch ZeroDivisionExcption ( "a" )  catch ExecutionError ("b")  catch ("c")    -> "a"

try  xyz()        catch ZeroDivisionExcption ( "a" )  catch ExecutionError ("b")  catch ("c")    -> "b"

try  pollerror()  catch ZeroDivisionExcption ( "a" )  catch ExecutionError ("b")  catch ("c")    -> "c"

Attention: never use the generic catch before other catches because they will be ignored.

try  1/0   catch ("c")  catch ZeroDivisionExcption ( "a" )  catch ExecutionError ("b")     -> "c"

4.1.2.2. IF statement

The syntax is:

if expression1 then expression2 else expression3

The statement calculate the value for the expression1. If the computed value is true, expression2 is calculated, otherwise expressione3.

Example:

if 1 + 1 >= 2                            then "Hello"             else "Goodbye"

if try 1.3.6.1.2.1.1.1.10@ catch (False) then  1.3.6.1.2.1.1.1.0@ else "Goodbye"

Attention

this statement is dangerous when used with Old snmp values. If you do not write your statement properly, some SNMP variables could never be fetched.

if 1 + 1 >= 2 then 1.3.6.1.2.1.1.2.3.4.0# else (1.3.6.1.2.1.1.2.3.4.0@ - 1.3.6.1.2.1.1.2.3.4.0#)

4.1.2.3. FOR statement

The syntax is:

for symbol [, ...] in range expression

[ if test ]

do body

Iterates the values calculated by range expression, assign them to symbol and excecutes body at each iteration; returns a list of all values calculated by body.

The scope of symbol is only inside the test or body expression.

When test expression is specified, body is calculated only when test is not False.

Warning

When body is executed, symbol variable will override any other variable with the same name in the current execution context.

Example:

The range expression must calculate a list of values. Each element of the list can be a tuple of elements; in this case

for $a, $b in list( list(1,1) , list(2,5), list(0,3))
    do
        $a+$b

4.1.2.3.1. Set statement

The set statement is a special expression that assign to symbol the value of expression and returns the value itself.

set symbol as expression

Example 1:

with
     $tot as 0
     ,
     $temp as (
         for $row in list(list('a',1),list('b',99))
             do
                 set $tot as $tot + $row[1]
     )
  do
     $tot

Example 2:

with
     $vals as (
         for $row in list(list('a',1),list('b',99))
             do
                 $row[1]
     )
  do
     sum($vals)
100

4.1.2.4. WITH statement

The syntax is:

with

$symbol1 as expr1

[, ... ]

do

body expr

Update execution context with every $symbol1 defined by expr1 expressions and executes body expr.

Example:

with
     $a as 1,
     $b as 2
  do
     $a + $b
3

Example 2:

with
    $sysdescr as 1.3.6.1.2.1.1.1.0@
  do
    $sysdescr
b'Linux debian.....'

4.1.2.4.1. Complex Example

Check if all "admin enabled" ifaces are active:

export EXPR='

with
     $num_ifaces as 1.3.6.1.2.1.2.1.0@
     ,
     $enabled_ifaces as (
         for $n in range($num_ifaces)
             if
                 1.3.6.1.2.1.2.2.1.7.($n+1)@ == 1
             do
                 $n+1
     )
     ,
     $active_ifaces as (
         for $ifidx in $enabled_ifaces
             if
                 1.3.6.1.2.1.2.2.1.8.$ifidx@ == 1
             do
                 $ifidx
     )
  do
     len($enabled_ifaces) == len($active_ifaces)
'

sanet-manage exec_expr localhost "$EXPR"

4.1.2.5. Function value definition

You can define new "inline" functions and pass/use them as values in your expression.

The syntax is:

fun ( param1 [, ...] ) ( expr )

Defines a new inline-function value that can be passed to other functions as a value.

Warning

the final result of an expression can not return a inline-function. The execution will raise an exception.

Example 1: define a new inline function

fun(a)($a+1)
*EXCEPTION* (Expression value is a function)

Example 2:

( fun(a)($a+1) ) (3)
4

Examples: define a new inline function, assign it to a variable and call it with call operator '()':

with
     $f as fun(a)($a+1)
  do
     $f(5)
6

Example: define a function and pass as value to an other function:

map( fun(a)($a + 1.5), list(1,2,3) )
[2.5, 3.5, 4.5]