Parsing nsupdate's output


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting Parsing nsupdate's output
# 1  
Old 01-28-2015
Parsing nsupdate's output

Anybody that's ever used nsupdate knows that it's error management is not very good.

I have a wrapper script that when it's got all the information it needs launches the nsupdate command.

This is my attempt at parsing the output to help support users quickly know if the command succeded or failed (and why it failed). For some reason i cannot see, the code sometimes works and sometime doesn't ... that's with a output of 40 mixed (failed and succeded) records.

If anybody has a clue OR would has a different method of achieving the same goal i would be gratefull. Thanks.

Code:
#!/bin/awk -f

BEGIN {
        print "Start";
        RS="Outgoing update query:"
        FS="\n"
}
{
        print "#### UPDATING CNAME ####"
        # Print what we are doing in clearer form
        split ($12, update, " ")
                print update[1],update[3],update[4],update[5]
        # Print the result of the operation on the DNS server
        if ($0 ~ /opcode/)
                split($0,array," ")
                if (array[6] == "NOERROR,")
                        sub(/,$/,"",array[6]);
                        print "OPERATION:",array[6];
        # If update failed lookup error
        if ($0 ~ "update failed:")
                split($(NF-1), error, " ")
                if (error[3] == "YXRRSET")
                        print "FAIL: CNAME already exist";
        else
                print "UPDATE SUCCEDED";
        print "\n"
}
END {
        print "Done"
}

success data (2 records):
Code:
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;exemple.local. IN   SOA

;; PREREQUISITE SECTION:
foo.exemple.local. 0 NONE A
foo.exemple.local. 0 NONE CNAME

;; UPDATE SECTION:
foo.exemple.local. 3600 IN CNAME realhost1.exemple.local.

Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;exemple.local. IN   SOA

;; PREREQUISITE SECTION:
bar.exemple.local. 0 NONE A
bar.exemple.local. 0 NONE CNAME

;; UPDATE SECTION:
bar.exemple.local. 3600 IN CNAME realhost1.exemple.local.

Failed data (also 2 records):
Code:
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;exemple.local. IN   SOA

;; PREREQUISITE SECTION:
foo.exemple.local. 0 NONE A
foo.exemple.local. 0 NONE CNAME

;; UPDATE SECTION:
foo.exemple.local. 3600 IN CNAME realhost1.exemple.local.

update failed: YXRRSET
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;exemple.local. IN   SOA

;; PREREQUISITE SECTION:
bar.exemple.local. 0 NONE A
bar.exemple.local. 0 NONE CNAME

;; UPDATE SECTION:
bar.exemple.local. 3600 IN CNAME realhost2.exemple.local.

update failed: YXRRSET

Expected output:
Code:
#### UPDATING CNAME ####
foo.exemple.local. IN CNAME realhost1.exemple.local.
OPERATION: NOERROR
UPDATE SUCCEDED

#### UPDATING CNAME ####
bar.exemple.local. IN CNAME realhost2.exemple.local.
OPERATION: NOERROR
FAIL: CNAME already exist

# 2  
Old 01-28-2015
I'm not sure I understand what you're after. Anyhow, some comments:
- The only difference between the two files is
Code:
                                                              > update failed: YXRRSET
bar.exemple.local. 3600 IN CNAME realhost1.exemple.local.     | bar.exemple.local. 3600 IN CNAME realhost2.exemple.local.
                                                              >
                                                              > update failed: YXRRSET

, so that "update failed" is what you need to go for.

- The " OPERATION: NOERROR / FAIL:" text may be misleading, so it might be pointless to look for the string "status".

- You seem to mix up lines, records, and fields in your script. With that field separator "\n", you have a varying field count; 13/14 with OK records, 15 with error records:
Code:
NF: 15
1:  
2:  ;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
3:  ;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
4:  ;; ZONE SECTION:
5:  ;exemple.local. IN   SOA
6:  
7:  ;; PREREQUISITE SECTION:
8:  foo.exemple.local. 0 NONE A
9:  foo.exemple.local. 0 NONE CNAME
10:  
11:  ;; UPDATE SECTION:
12:  foo.exemple.local. 3600 IN CNAME realhost1.exemple.local.
13:  
14:  update failed: YXRRSET
15:

so you might always go for field 14 or (NF-1).

- $0 will always (at least in your samples) hold "opcode", so you always split $0 into array, which will have 43 (OK records) or 46 (fails) elemants, respectively. What for?

- in your script, indentation alludes that you want to have nested ifs, but awk needs the entire branch to be enclosed in {...} if it has more than one command. So all the ifs will be executed at the same hierarchy level; the else branch will rely on an error array set way back even if the actual record is a success, and "OPERATION" wil be printed always no matter if "opcode" present in $0 or not.

Suggestion: check for the relevant fields by either
- for (i=1; i<=NF, i++) if ($i ~ "failed") print $i
or
- if (match ($0, /failed: .*$/)) print substr ($0, RSTART+7, RLENGTH-7)
This User Gave Thanks to RudiC For This Post:
# 3  
Old 01-28-2015
Thanks for your answer!

The status (the NOERROR in this case) actually serves the purpose of saying that the connection to the DNS server is ok. I wanted a way to correctly split this ressource record so i could extract every field if i wanted to.... I could then use this to extract every DNS message NOERROR, NXDOMAIN, SERVFAIL, etc treat them accordingly.

What if i could keep my RS the same BUT use an empty line as a split delimiter?

That way i could have block[1] like this:

Code:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;exemple.local. IN   SOA

And so on and so on so i would know that if block[4] exist i have a failed update?
# 4  
Old 01-28-2015
That wouldn't work with the samples you've given unless you take additional action. Using both files and printing NF and, if available, FAIL:
Code:
Start
0
4
3
0
4
FAIL: CNAME already exist
4
FAIL: CNAME already exist
Done

This User Gave Thanks to RudiC For This Post:
# 5  
Old 01-28-2015
How about this:

Code:
awk '
function getfield(str, pattern, trim) {
    if(!match(str, pattern)) return ""
    return substr(str,RSTART+trim,RLENGTH-trim)
}
function procDNS(rec) {
    if(split(rec,lines,"\\n") < 2) return ""
    updt_line=getfield(rec, "UPDATE SECTION:\n[^\n]+", 16)
    oper=getfield(rec, "status: [^ ,]+", 8)
    fail=getfield(rec, "update failed: [^\n]+", 15)
    print "#### UPDATING CNAME ####"
    print updt_line
    print "OPERATION: " oper
    if(length(fail)) {
       gsub("YXRRSET", "CNAME already exist",fail)
       print "FAIL: " fail
    }
    else print "UPDATE SUCCEDED"
    print ""
    return ""
}
/^Outgoing update/ {A=procDNS(A)}
{A=A "\n" $0}
END{procDNS(A)}
' infile

This User Gave Thanks to Chubler_XL For This Post:
# 6  
Old 01-29-2015
Chubler: It works like a charm but i have to say i have a very hard time understanding the work flow.

Two functions and main loop.

First function is getfield which returns blank if it doesn't match string and pattern and exits the function with a substitute string of the string of the pattern it caught. You are probably using that to only get the pattern you want. Brillant Smilie

Second function splits the record into "lines" array using \n as a delimiter. Then sets up three variables (updt_line, oper and fail) using your first function. Three prints later you are checking the length of fail and if its not 0 sub YXRRSET on the fail chain for CNAME already exist... Else just print UPDATE SUCCEDED.

Main loop is searching for pattern at start of line "Outgoing update" but sets up procDNS function to A?.... then.... i'm lost Smilie Can you help me understand the last part?

Thanks!
# 7  
Old 01-29-2015
Lets look at the main code part first:

Code:
/^Outgoing update/ {A=procDNS(A)}
{A=A "\n" $0}

Line 2 above is building up variable A with your file contents, it does this by appending A with a newline plus the current line's contents for every line of the file.

As for Line 1: function procDNS always returns blank, this is just a small trick that allows us to process the contents of A and then reset A back to the empty string. This could have been written as:

Code:
/^Outgoing update/ {procDNS(A); A=""}

You might prefer the above version for clarity. If you look at your datafile you will note that it starts with an "outgoing update" line. As no other lines have been read yet A will still be blank, procDNS needs to deal with blank input and not output a blank result, that is what the first line of the function is doing with the split command. Another elegant way to deal with this is to test for no updt_line as I guess output without this info is pretty meaningless:

Code:
function procDNS(rec) {
    updt_line=getfield(rec, "UPDATE SECTION:\n[^\n]+", 16)
    if (updt_line == "") return ""

Now remember the input to procDNS is the whole block between the "Outgoing update" lines. Your assessment of getfield() is pretty well spot on, note the value 16 above is the number of characters to discard from the front of the matched string. i.e. "UPDATE SECTION:" plus newline, which leaves the data required. You can easily extract other info you may need from the block using this technique.

Code:
    if(length(fail)) {
       gsub("YXRRSET", "CNAME already exist",fail)
       gsub("REFUSED", "Change refused",fail)
       gsub("SERVFAIL", "Failed to connect to server",fail)
       gsub("NOTAUTH", "Not authorized",fail)
       print "FAIL: " fail
    }
    else print "UPDATE SUCCEDED"

See above how you can add further human readable versions of the error codes returned, I don't know what ones you actually see in the "wild", of course any fail codes not replaced will remain as they are eg "FORMERR", "BADVERS", "NOTZONE", etc.

Finally when you reach the end of the file A will have the last record built up in it but there is no "Outgoing update" line to trigger it's processing, so we call procDNS(A) in the END section (a blank file could will result in a blank block being sent to procDNS but we already know procDNS deals with this fine).
This User Gave Thanks to Chubler_XL For This Post:
Login or Register to Ask a Question

Previous Thread | Next Thread

10 More Discussions You Might Find Interesting

1. Shell Programming and Scripting

Parsing of TOP output

Hi , i am trying to set up an alert, when CPU usage (0.2%us in below output) is more than 40% top | head | grep '^Cpu' Cpu(s): 0.2%us, 0.2%sy, 0.0%ni, 99.1%id, 0.6%wa, 0.0%hi, 0.0%si, 0.0%st using CUT, i pulled the value 0.2 and assigned to CPU (variable) CPU=$(expr `top | head -10... (5 Replies)
Discussion started by: Prateek007
5 Replies

2. Shell Programming and Scripting

Parsing Output of a Variable

i have a log file that contains something similar to this: one two three four five six seven eight nine ten eleven twelve thirteen fourteen one two three four five six seven eight nine ten eleven twelve thirteen fourteen one two three four five six seven eight nine ten eleven twelve... (3 Replies)
Discussion started by: SkySmart
3 Replies

3. Shell Programming and Scripting

parsing output

Can somebody provide a solution to parse the following; cat /tmp/xxx Name: QUE_REQU (o.mtaseast-o.dmart) (MTPost queue) Number of messages: 66446 (Age 686 min; Size 214 mb) Backlog (messages): 0 (Age 0 min) Name: QUE_REQU... (6 Replies)
Discussion started by: BeefStu
6 Replies

4. Shell Programming and Scripting

Parsing the date output

Hi fellows, I need to define a notification for SSL certificate expiration. My Command output is below: (this is the "Expiration Date") Tue Mar 15 09:30:01 2012 So, at 15th Feb (1 month before the expiration), a notification has to be triggered by a script or sth else. How can i set an... (5 Replies)
Discussion started by: oduth
5 Replies

5. UNIX for Advanced & Expert Users

nsupdate failed to update

Hello all, I am new to Unix.. i am trying to implement the TSIG in my BIND and using nsupdate to add record to bind.. this is what i did: generate the TSIG key using : dnssec-keygen HMAC-MD5 -b 128 -n HOST mydns.com forgot to mention: i am using FreeBSD. I then edit the named.conf file... (1 Reply)
Discussion started by: r_de_sousa
1 Replies

6. BSD

nsupdate failed to update

Hello all, I am new to Unix.. i am trying to implement the TSIG in my BIND and using nsupdate to add record to bind.. this is what i did: generate the TSIG key using : dnssec-keygen HMAC-MD5 -b 128 -n HOST mydns.com forgot to mention: i am using FreeBSD. I then edit the named.conf file... (0 Replies)
Discussion started by: r_de_sousa
0 Replies

7. UNIX for Dummies Questions & Answers

nsupdate falied to update

Hello all, I am new to Unix.. i am trying to implement the TSIG in my BIND and using nsupdate to add record to bind.. this is what i did: generate the TSIG key using : dnssec-keygen HMAC-MD5 -b 128 -n HOST mydns.com forgot to mention: i am using FreeBSD. I then edit the named.conf file... (0 Replies)
Discussion started by: r_de_sousa
0 Replies

8. Shell Programming and Scripting

Parsing output

I need to parse the following out put and determine if the USB is a DISK and whether or not it's External. If an HBA line contains "USB" then does the next line contain "DISK" and "External". 0:0,31,0: HBA : (aacraid,1) AAC SCSI 0,0,0: DISK : Adaptec ASR4800SAS Volu0001 ... (6 Replies)
Discussion started by: lochraven
6 Replies

9. AIX

nsupdate - 2 zones are appended to hostname?

Hello, Can someone tell me why I'm getting the following? $sudo nsupdate > update add HOSTNAME.ZONE1 86400 A IP ADDRESS > show Outgoing update query: ;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 0 ;; flags: ; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0 ;; UPDATE SECTION:... (1 Reply)
Discussion started by: ctcuser
1 Replies

10. Shell Programming and Scripting

parsing output

I have a file that contains the output of the ls -iR command, something like this: ./results: 2504641011 result_1410 2500957642 result_525 2504641012 result_1425 2500957643 result_540 ./tests/1: 2500788755 1 2500788743 1000 ./tests/2: 2500788759 3 2500788758 999 ... (6 Replies)
Discussion started by: looza
6 Replies
Login or Register to Ask a Question