Extract Nagios object definitions from a monolithic configuration file to individual files

501 views Asked by At

I am porting some old Nagios configuration data across from Nagios Core to Nagios XI. Part of this work means that I need to extract some object definitions and place them into individual files named by hostname (example to follow). I can see a number of ways to do it, possibly by writing a script (Perl/Python/PHP - the Nagios XI scripting seems to all be done in PHP). However I was wondering if there was a simpler way to do this, perhaps on the command line using awk or similar? It strikes me that awk can extract lines of text between two delimiting patterns easily enough (e.g. /define host \{/ and /\}/) but I need the output separating into individual files named by the contents of the host_name field.

What is the best approach to this? Am I best off writing a script, or is there a neat awk command (or similar) that can be run from the bash shell on the Nagios XI machines?

Example monolithic file:

define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
define service {
    use     servtemplate1
    host_name   testhost1
    service_groups  +All
    service_description  A Test Service
}
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
}

Desired output:

# cat testhost1.cfg
define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
# cat testhost2.cfg
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
 }

Now for example I can run a command like this which seems fairly widely used for line extraction:

# gawk ' /define host / {flag=1;next} /}/{flag=0} flag { print }' example.cfg

This chops off the define host and } but that's a relatively easy fix - however it's outputting the data as one stream in the shell.

Is there some clever trick I can implement to do all this including the splitting into individual configuration files from a one liner on the shell, or should I write a script?

2

There are 2 answers

2
Akshay Hegde On BEST ANSWER

Using awk:

One-liner

awk '/^define host/{f=1;str=$0;next}/host_name/{h=$NF".cfg"}f{str=str ORS $0}f && /^\}/{print "#"h>h; print str>h; f=""; close(h)}' file

Explanation

awk '
      /^define host/{                # look for line start with define host
                      f=1            # set variable f to 1
                      str=$0         # set variable str = current line/row/record 
                      next           # go to next line
      } 
      /host_name/{                   # look for line with hostname
                     h=$NF".cfg"     # set variable h with last field value plus ".cfg"
      }
      f{                             # if f was true or 1 then
                     str=str ORS $0  # concatenate variable str with current record 
      }
      f && /^\}/{                    # if f is true and line starts with } then
                     print "#"h > h  # write # hostname to file
                     print str > h   # write the content of variable str to file
                     f=""            # nullify variable
                     close(h)        # close file 
      }
    ' file

Test Results

Input:

$ cat file 
define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
define service {
    use     servtemplate1
    host_name   testhost1
    service_groups  +All
    service_description  A Test Service
}
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
}

Execution:

$ awk '/^define host/{f=1;str=$0;next}/host_name/{h=$NF".cfg"}f{str=str ORS $0}f && /^\}/{print "#"h>h; print str>h; f=""; close(h)}' file

Files generated:

$ cat *.cfg
#testhost1.cfg
define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
#testhost2.cfg
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
}

In PHP

$ cat test.php
<?php
preg_match_all('~define host\s+?{[^}]*}~', file_get_contents('file'), $match);
foreach($match[0] as $config)
{
    if(preg_match('~host_name\s+([^\s]*)~', $config, $host))
    {
        $file = $host[1].".cfg";
        file_put_contents($file, '#'.$file.PHP_EOL.$config.PHP_EOL);
    }
}
?>

$ php test.php 
$ ls *.cfg
testhost1.cfg  testhost2.cfg
1
RavinderSingh13 On

A warm welcome to Stack overflow site, I hope you will enjoy learning and sharing knowledge here, could you please try following and let me know if this helps you.

awk '/^}$/ && flag{print > file;flag="";close(file);next} /define host/{flag=1;val=$0;next} flag && /host_name/{file=$2".cfg";print val ORS $0 > file;next} flag{print > file}'   Input_file

EDIT: Adding a non-one liner form of solution too now.

awk '
/^}$/ && flag{             ##Looking for a line which starts from } and ends with same only + checking if variable flag is NOT null.
   print > file;           ##printing the current line to variable file(which will be having values like testhost1, testhost2.cfg etc etc
   flag="";                ##Nullifying the variable flag here.
   close(file)             ##Closing the file because in case of Input_file is huge so in backend these files(testconfig1 etc etc) could be opened and which may cause issues in case they all are opened, so it is good practice to close them.
   next                    ##Using next keyword of awk will skip all the further statements for the current line.
}
/define host/{             ##Searching for a line which has string define_host in it.
   flag=1;                 ##Making variable flag value to TRUE now, to keep track that we have seen string define_host in Input_file.
   val=$0;                 ##Storing this current line to a variable named val now.
   next                    ##Using next keyword will skip all further statements for the current line.
}
flag && /host_name/{       ##Checking if flag is TRUE and current line has string host_name in it then do following.
   file=$2".cfg";          ##Creating a variable named file who has value of 2nd field and string .cfg, this is actually the file names where we will save the contents
   print val ORS $0 > file;##printing variable named val then output record separator and current line and redirecting them to variable file which will make sure it should be saved in file named testhost etc etc .cfg.
   next                    ##As mentioned before too, next will skip all further statements.
}
flag{                      ##Checking if variable flag is TRUE or NOT NULL here.
   print > file            ##Printing the current line into the file then.
}
'  Input_file              ##Mentioning the Input_file here.