Monday, January 31, 2011

Extracting IP addresses from DiG output for a bunch of hosts

In my Grails server inventory app, I had to account for future IP space refactoring, where a bunch of hosts would change their IP addresses. All users of the system will expect to see new IPs in all runbooks, so something had to be done to keep them in sync.

So, I decided to add a Quartz job that would

1. Query all existing hosts with a valid host name
2. Call a 'dig' on that host
3. Parse an IP address from dig response

So, let's say we have a Host object that looks something like this:

class Host {
    ...
    String hostName
    String ipAddress
    ...
}

Adding a Quartz job is a trivial task that can be easily accomplished using grails Quartz plugin. One advice: use the Quartz plugin instead of manually adding a TimerTask bean using resources.groovy. Apparently, Grails application's most vital aspects, such as autowiring of services and GORM Hibernate session are not available at construction time for beans defined in resources.groovy, because those beans are read before anything in grails-app directory. So, here's how my Quartz job looks in a Grails 1.3.4 application:

import org.codehaus.groovy.grails.commons.ApplicationHolder

class IpSynchJob {
    def hostService
    def startDelay
    def timeout

    public IpSynchJob() {
        startDelay = 0
        timeout = ApplicationHolder.application.config.timer.ipsynch.interval
    }


    def execute() {
        def hosts = Host.createCriteria().list {
            isNotNull("hostName")
        }

        hosts.each { host ->
            log.info "synching IP for hostname $host.hostName"
            synchIp(host)
        }
    }

    void synchIp(host) {
        def dig = digServer(host.hostName)
        def ipAddress = parseIpFromDigOutput(dig)
        host.ipAddress = ipAddress
        host.save()
    }

    String parseIpFromDigOutput(digOutput) {

        // looking for IP address in dig's answer section using positive lookahead
        def ipInDigPtn = /(ANSWER SECTION\:)(?=\n.*IN\s+A\s+(\d+\.\d+\.\d+\.\d+))/
        def ip
        digOutput.eachMatch(ipInDigPtn) { match ->
            ip = match[2]
        }
        return ip
    }

    String digServer(hostName) {
        Process p = "dig $hostName".execute()
        int initCapacity = 4096
        StringBuffer out = new StringBuffer(initCapacity)
        p.consumeProcessOutput(out, out)
        // maxing out wait at 20 seconds
        p.waitForOrKill(20000)

        return out.toString()
    }
}

No comments:

Post a Comment