A Little More About Lua Scripts

Email Transmission and Table

Now, let's transmit the result of ping via email. For email transmission, use the rt.mail function.

Download [ping5.lua]

function ping(dst)
  flag, outputs = rt.command("ping -c 1 " .. dst)
  if flag then
    first_line = string.match(outputs, "^(.-)\n")
    return first_line
  else
    return "ERROR: ping -c 1 " .. dst
  end
end

text = ""
for i = 1, #arg do
  text = text .. ping(arg[i]) .. "\n"
end

mail_table = {}
mail_table.smtp_address = "mx.example.jp"
mail_table.from = "from@example.jp"
mail_table.to = "to@exapmle.jp" 
mail_table.subject = "PING result" 
mail_table.text = text

ret = rt.mail(mail_table)

if (not ret) then 
  print("Email transmission failed") 
end

First, use the ping function to collect the strings resulted from ping to the IP addresses into the text variable.

text = ""
for i = 1, #arg do
  text = text .. ping(arg[i]) .. "\n"
end

Then, email this by using rt.mail. Pass the argument of rt.mail in the form of table.

mail_table = {}
mail_table.smtp_address = "mx.example.jp"
mail_table.from = "from@example.jp"
mail_table.to = "to@exapmle.jp" 
mail_table.subject = "PING result" 
mail_table.text = text

ret = rt.mail(mail_table)

In the first line, an empty table is substituted into the variable, mail_table. Without this table, the subsequent substitutions of elements will fail.

After that, elements including smtp_address and from are substituted, and at the end, mail_table is passed as the actual argument of rt.mail.

Set smtp_address to the IP address or the host name of the SMTP server used for email transmission, and set from and to to the sender's and the receiver's mail addresses, respectively. These three elements are required.

Set subject to the subject of the email, and text to the body of the email. These are optional, but in normal situations, set these items.

When you pass this table containing these five elements as the argument to rt.mail, then the router transmits an email. The result of the email transmission is returned as the return value of the rt.mail. Substitute this return value into the variable, ret. ret indicates whether or not the email was successfully transmitted. true or false is returned when successful or failed, respectively.

Only in the case of the failure of the email transmission, the script displays an error message as follows:

if (not ret) then 
  print("Email transmission failed") 
end

You can see the keyword, not, in front of the variable, ret. This is called the not operator, meaning that the value of ret is reversed. Therefore, the judgment of this if statement is true when ret is false.

By the way, what is the table described above? You can think of a table as something that gathers multiple data together. In this case, the table that gathers multiple data such as the SMTP server, email addresses, body of the email, and other data required for email transmission is made as the argument of rt.mail.

The table contains multiple data collected together. These multiple data are called elements. An element is a pair of two data, a key and a value. The key is used to identify the location in the table, and the value indicates the evaluation of the element.

        Table

        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+
        |     key    |    value   | <- element
        +-------------------------+

All the keys in the table differ depending on the element. All the data, except nil, are available for the Lua language can be used for the keys, but numerical values or character strings are usually used.

To access the elements in the table, place the square brackets [ ] after the table name, and write the value of the key in the brackets. When a key is a numerical value; for example, to access the element of a key 10 in table tbl, write tbl[10].

   [Note]

The arg mentioned earlier that represents the argument of the Lua command is also a table. This table contains only numerical values as the keys.

When a key is a character string, such as "str", you can write tbl["str"]; you can also write tbl.str using a period ".". Either way of writing it expresses the same meaning.

The ping5.lua sets the elements in the table, mail_table. These elements have the keys of the character string type. This means that you can access these elements by writing as follows:

mail_table.smtp_address <-> mail_table["smtp_address"]
mail_table.from         <-> mail_table["from"]
mail_table.to           <-> mail_table["to"]
mail_table.subject      <-> mail_table["subject"]
mail_table.text         <-> mail_table["text"]

Also, at first, an empty table is substituted into mail_table. An empty table means a table containing no element and is expressed as { }.

mail_table = {}

This { } is called the table constructor, which is used to express not only an empty table but also a table with elements that are written in the curly braces. For example, for the portion where the values are substituted into the elements of mail_table of ping5.lua, you can write as follows:

mail_table = {
  smtp_address = "mx.example.jp",
  from = "from@example.jp",
  to = "to@exapmle.jp", 
  subject = "PING result", 
  text = text
}

In the curly braces, write the comma(,)-separated combinations of the "key = value." If the key is a character string, you can write it as shown above, but if the key is a numerical value, use square brackets.

tbl = { [1] = "Hello", [10] = "World" }

If you write only a value, not in the form of "key = value", the serial numbers starting with 1 are considered as the key.

tbl = { "Hello", "World" } -- The same meaning as { [1] = "Hello", [2] = "World" }

As described above, a table can handle multiple data collectively, and thus can be used for a variety of uses. Like the rt.mail function, you can use a table to collect multiple parameters or to collect a series of data for processing.

   [Note]

When the keys are all numerical values, 1 or more, and they are continuous, that is, when a table contains elements from 1 through the maximum value without any omission for the key, it is called an array. An array is a data structure generally used also for other programming languages. Variety algorithms are implemented using arrays. Therefore, the Lua language may handle an array separately from other tables.


Periodic Execution

Now, ping5.lua can send ping to the specified address and transmit the result via email. Using this script allows you to monitor whether the server is properly operating. By periodically executing this script, say every hour, you can monitor the operational status of the server every hour. So how can you write a script for this?

The first available way is to repeat the processing every hour in the Lua script. To do this, use rt.sleep, which delays the processing for a certain time.

Download [ping6.lua]

function ping(dst)
  flag, outputs = rt.command("ping -c 1 " .. dst)
  if flag then
    first_line = string.match(outputs, "^(.-)\n")
    return first_line
  else
    return "ERROR: ping -c 1 " .. dst
  end
end

mail_table = {}
mail_table.smtp_address = "mx.example.jp"
mail_table.from = "from@example.jp"
mail_table.to = "to@exapmle.jp" 
mail_table.subject = "PING result" 

while true do
  text = ""
  for i = 1, #arg do
    text = text .. ping(arg[i]) .. "\n"
  end

  mail_table.text = text

  ret = rt.mail(mail_table)

  if (not ret) then 
    rt.syslog("info", "mail transmission failed") 
  end

  rt.sleep(3600)
end

In ping6.lua, the portion that sends ping and transmits an email written in ping5.lua is put between while true do and end. Because the conditional expression is true, this loop runs forever. Also, because only the body text changes at each loop, the other parameters for rt.mail are placed outside the loop.

rt.sleep(3600) written at the end of the loop delays the execution by 3600 seconds, which is 1 hour. This allows the ping and email to be transmitted 1 hour after the last transmission. Note that without this rt.sleep statement, the ping and email transmission processing is continuously executed without any waiting time.

Also, when an email failed to be sent, the ping6.lua uses the rt.syslog command to output the information to the log.

As shown above, you can write a Lua script that runs the process infinitely; however, it might cause a problem if the processing does not end. In such a case, the following commands are provided: the show status lua command, which displays the status of the Lua script in progress, and the terminate lua command, which forcibly terminates the Lua script in progress.

# lua /lua/ping6.lua 192.168.100.200
# show status lua
Lua Library Version:            Lua 5.1.4
Lua Script Function Version:    1.0

[running]
Lua Task ID (State): 1  (RUN)
Running Trigger:     executed by 'lua' command
Command Line:        lua /lua/ping6.lua 192.168.100.200
Script File:         /lua/ping6.lua
Starting Date:       2009/09/21 16:41:01
Elapsed Time:        6 seconds
:
# terminate lua 1

Use the show status lua command to display the Lua script being executed, and the terminate lua command to specify the task ID of the task to terminate.


Periodic Execution Using Schedule at Command

Besides an endless Lua script, you can use the schedule at command to periodically run a Lua script. Let's execute the ping5.lua, which executes ping only once, every hour by using the schedule at command.

# schedule at 1 *:00 * lua /lua/ping5.lua 192.168.100.200

In this case, if the ping5.lua enters an infinite loop at some point and does not end, multiple scripts may run simultaneously. Therefore, sufficient tests are required to conduct before registering the schedule with the schedule at command.

As described above, there are two ways of running a script periodically. It cannot be simply determined which of the methods is better, but as one idea, you can use the schedule at command to officially operate the processing, and use the rt.sleep to perform processing temporarily or conduct tests.

Return to Top