#!/usr/bin/perl # # Version control... now using git my $ID = ":Id: Wed, 23 Oct 2013 11:56:57 -0700 Geoff H ::"; my $REV = ":Revision: r3.80 ::"; # # just do it, and get it over with $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; # (c) Copyright: Geoff horne, SLC 2001-2012 # All rights reserved # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. So there... # # THIS TICKET IS SOLD SUBJECT TO THE SELLERS CONDITIONS OF SALE OF COPY # WHICH IS AVAILABLE FOR INSPECTION AT TIME OF PURCHASE AND/OR CONDITIONS # OF THIS TICKET AND ALSO (TO THE EXTENT THAT THEY ARE NOT INCONSISTENt # WITH THE SELLERS CONDITIONS OF SALE) THE FOLLOWING: # # 1: A CHANGE IN CONDITIONS OF THE PURCHASER DOES NOT ENTITLE THE PURCHASER # TO RETURN OR EXCHANGE TICKET. # # 2: THE SELLER RESERVES THE RIGHT TO: # - ADD, WITHDRAW OR SUBSTITUTE ARTISTS # - VARY ADVERTISED PROGRAMMES, SEATING ARRANGEMENT AND AUDIENCE # CAPACITY , AND: # - REFUSE ADMISSION WITH REASONABLE CAUSE # # 3: CAMERAS AND TAPE RECORDERS MAY NOT BE PERMITTED. LATE ARRIVALS MAY # RESULT IN NON-ADMITTANCE UNTIL A SUITABLE BREAK IN PERFORMANCE. THE # SELLER REQUESTS THAT ALL PAGERS AND MOBILE PHONES BE TURNED OFF BEFORE # ENTRY. # # 4: IF AN OUTDOOR PERFORMANCE IS CANCELLED DUE TO ADVERSE WEATHER OR ANY # OTHER CAUSE REASONABLY BEYOND THE SELLERS CONTROL, THERE IS NO RIGHT # TO A REFUND OR EXCHANGE, AND THE SELLER IS NOT OBLIGED TO ARRANGE A # SUBSTITUTE SERVICE, EVENT OR PERFORMANCE # # StreetLevel Communications # # support@slc.com.au # ph: +1-650-483-0998 # ############################################################################### # Perl CGI common chunks # use strict ; no strict "refs" ; no strict "subs" ; # work out where i am use FindBin qw($Bin $Script); my ($BASE,$NAME)=($Bin,$Script) ; use lib "$FindBin::Bin" ; use lib "$FindBin::Bin/lib" ; use lib "lib" ; # GDH, moved to an internal routine # use Term::EditLine qw(CC_EOF CC_REFRESH CC_REDISPLAY); use Text::Abbrev ; use Getopt::Long; use Encode; use Infoblox; # globals use Data::Dumper ; $Data::Dumper::Sortkeys = 1 ; ### --- GLOBAL PRE canned regexes --- ### # # we can force syntax HERE by matching args to regexes # # quoted comments my $REcomm = '("([^"]+)")|(\S+)' ; # zones my $REzone = '(\w|\-|\.)+'; # a complete IP address my $REip = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'; # a complete n.n.n.n/mm my $REcidr = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}'; # MAC address my $REmac = '[\w:]+'; # IPv6 addresses (same as a mac address) my $REvsix = $REmac ; # incomplete form of an IP address # match 'N' then praps '.N' then praps '.' or '.N' # with optional wildcards # wow - that took a while to work out # my $REpartip = '\d{1,3}(\.\d{1,3}){0,2}(\*|\.|\.\d{1,3})+'; my $REpartip = '(\d{1,3})(\.[\d*]{1,3}){0,2}(\.[\d*]{1,3})'; # this is a hash of regexes to use for special keywords my %SPECOPS = ( '' => "^$REmac\$", '' => "^$REmac\$", '' => '^\d+$', '' => '^\S+$', '' => '^\S+$', '' => '^\S+$', '' => '^\S+$', '' => '^\S+$', '' => '^\S+$', '' => '^\S+$', '' => '^\S+=\S+$', '' => '^\S+$', '' => "^$REcomm\$", '' => "^$REzone,*($REpartip)*\$", '' => "$REpartip|,", # '' => "^($REpartip)\$|($REpartip,)+($REpartip)\$", '' => "^$REpartip\$", '' => "^$REpartip\$", '' => "^$REpartip\$", '' => "^$REcomm\$", # incomple n.n.n.n/mm '' => "^($REpartip|$REip\\\/|$REcidr)\$", '' => "^($REpartip|$REip\\\/|$REcidr)\$", '' => '^\/\d{1,2}$', '' => '^\S+$', '' => '^\d+$', '' => '^\d+$', '' => '^\d+$', ); # Alternate words are set of a global lookup table # [ ] GDH - not implemented my %ALTERNATES = ( "configure" => "set", ); ### --- end ------------------ ### # [-] connected my $ONLINE ; my $SESSION ; my $MASTER_IP ; my $DEBUG = 0 ; my $LIST_COMM = 0 ; my $LIST_USAGE = 0 ; # $SDB is the hash of a local database backup my %SDB ; my $SDBCOUNT ; my $SDB_FILE ; # we have a schema that is set when we load a file my %SCHEMA ; my @PATH ; my $CWD ; my $COT ; # current object type my ( $M_ID , $S_ID , $S_PASS , $S_USER ) ; my $EXEC ; my $CGI ; GetOptions ( "s=s" => \$S_ID , "p=s" => \$S_PASS , "u=s" => \$S_USER , "m|master=s" => \$M_ID , "e=s" => \$EXEC , "w" => \$CGI , "b|f=s" => \$SDB_FILE , "h|help" => \$LIST_USAGE , "l+" => \$LIST_COMM , "V|version+" => sub { print "\n$ID\n$REV\n\n"; exit ; }, "d=s" => \$DEBUG, ); if ( $LIST_USAGE ) { print " Usage $NAME [-s -u -p ] [file.cf] -s # The name of the DNS One you are poking at -u # The username -p # That user's password -m # the Grid MASTER IP address (when using the MGMT port) -b # load a backup file -e # run a single command -d N # The debug level -V # show the version # -t # show the command table -h|help # Print out help \n"; exit ; } ########################################################## # now we can initialise # set the prompt ; my $PROMPT = 'server ? > '; # initialise the CLI table ( have to do this first or we can't dump them ) my %ALIASES ; my %COMMANDS = init_commands() ; # initialise editline ( or not ) and return a handler # use Term::EditLine qw(CC_EOF CC_REFRESH CC_REDISPLAY); my $USE_EL = 0 ; my $el ; # define some constants for Editline # ( I hope to hell these don't change because they are supposed to be # imported from the module ) use constant CC_REDISPLAY => 8 ; use constant CC_REFRESH => 4 ; use constant CC_EOF => 2 ; # continue initialising # my $el = &init_editline ; ( $USE_EL , $el ) = &init_editline ; ########################################################## # bailout if we just want commands if ( $LIST_COMM ) { foreach my $comm ( sort keys %COMMANDS ) { print "$comm\n"; } exit ; } ########################################################## # preload a backup file if ( $SDB_FILE ) { cli_load_db( "configure file load $SDB_FILE" ); } ########################################################## # If we didn't get a password, but did get a server and user, # prompt for the pw # if ( $S_ID and $S_USER ) { unless ( $S_PASS ) { system "stty -echo"; print "Password: "; chomp($S_PASS = ); print "\n"; system "stty echo"; exit unless $S_PASS; } } # otherwise - we initialise and get going # do a connection from the git go ? if ( $S_ID and $S_PASS and $S_USER ) { cli_add_server( "configure server $S_ID user $S_USER password $S_PASS" ) ; } # switch the master IP for MGMT port connections if ( $M_ID ) { &cli_add_master ( "configure master $M_ID" ) ; } # GET/store the API version, after we've re-load the right perl modules # [ ] broken in later revs of code my $host = Infoblox::DNS::Host->new ("name" => "name"); my $CLIENT_REV = $host->{__version} ; unless ( $CLIENT_REV ) { $CLIENT_REV = "6.1.0.0" } ; # main loop # CGI mode, drop a cgi header # [ ] do we want any authorisation ? if ( $CGI ) { print "Content-Type: text/plain; charset=ISO-8859-1\n\n"; } # load an initial config file # this is just batch mode sans the echo my $conf = ".$NAME.cf"; if ( -f $conf ) { open ( CF , $conf ) ; while () { # skip comments, blanks next if /^\s*#/; next if /^$/; &process_line ( $_ ) ; } } # exec mode # run a single command and exit if ( $EXEC ) { $PROMPT = "" ; # print "$EXEC\n"; print "$EXEC\n"; &process_line ( $EXEC ) ; exit ; } if ( @ARGV ) { # we are in batch mode # scrub the prompt $PROMPT = "" ; # read the file while (<>) { # skip comments, blanks # next if /^\s*#/; if ( /^\s*#/ ) { print ; next ; } next if /^$/; # $PROMPT = "read line $.:" ; # print "$PROMPT $_"; print "read line $.: $_"; &process_line ( $_ ) ; } exit ; } else { # print a startup message print " ##################################################################### # # the Infoblox CLI $REV (this is beta code, ymmv) # ##################################################################### ( press for help ) "; if ( $USE_EL ) { # we can load the editline routines and use them while (defined($_ = $el->gets())) { # only history non-blank lines chomp ( my ( $in ) = $el->line ) ; if ( $in ) { # save the line WITH the cr. $el->history_enter($_); } # now do something chomp ; # cleanup the line, do something, and redraw # we can only do redraws on a 'return()' # so everything is in a sub{} &process_line ( $_ ) ; # now flush the buffer $el->deletestr( length ( $in ) ); } } else { # no editline (winders) go old skool print "Warning : Can't find Term::EditLine, TAB completion is disabled\n"; # unbuffer IO $| = 1 ; print "$PROMPT"; # read lines 1 per while(<>){ # exit conditions etc that /were/ in editline if ( /^quit|q$/ ) { &cli_exit ; } # help if ( /\?\s*$/ ) { # so a simple substitution $_ = "help\n" ; } # skip comments, blanks # next if /^\s*#/; # next if /^$/; # snarf the line chomp ; &process_line ( $_ ) ; # lastly, reprompt ? print "$PROMPT"; } } } ##################################################################### # perldoc goes here =head1 Ibcli The INFOBLOX CLI, a users guide =head1 Synopsis *A guide for commandline junkies* =head1 Description The Ibcli is a simple command line tool that lets you do some (mostly) powerful things. It leverages the Infoblox api but provides a wrapper to the more complex (and detailed) API calls that uses more simple command line type calls. It also lets you create batch scripts that can configure an Infoblox SDB without actually writing any perl. So why do this, what is this CLI for ? Well there were a few primary reasons: =over =item Quick changes are now easy, You don't need to wait for a GUI to start or get the right version of java functional, this allows you to quickly get into the database and make some simple and efficient changes =item Abstract command set Now you don't need to know the intricacies of the API, a few simple commands will achieve the same effect, if you are a user that doesn't know PERL you can still drive the CLI and get the results you need. This may not be an advantage if you are reading data from another system to import into an Infoblox unit (since you still need to write software to parse the data, eg PERL) but it may still help and end user that can convert this data in to the CLI command set but not know enough to write PERL. =item Customers wanted it Need I say more ? =back Lastly, why is this called 'ibcli' and not 'DNScli' ? Well, this is just a wrapper to any API we like, it is not really DNS specific. =head1 Quickstart Simple example, adding a zone Start ibcli ibcli -s 10.0.1.114 -u admin -p infoblox Then run some commands admin@10.0.1.224 > conf zone add infoblox.com admin@10.0.1.224 > conf zone add sales.infoblox.com A more detailed example admin@10.0.1.224 > conf zone add com admin@10.0.1.224 > conf zone add foo.com admin@10.0.1.224 > conf zone foo.com add host test 1.2.3.4 admin@10.0.1.224 > conf zone delete bar.com =head1 Installation It should run 'as is' from a uniz machine. If you have a windows OS then you will have a few hurdles to cross: =over =item * Rename the script to 'ibcli.pl'. I still don't understand why Windows uses file suffix to type its data rather than doing something intelligent like looking at the contents of the file, but then, we are dealing with people who thought 8.3 was all you'd ever need as a file namespace and 640K for ram... =item * Make sure you have the necessary libraries installed, you'll need: o Getopt::Long o Text::Abbrev o Term::EditLine (good luck with that) =back =head1 Getting started Run the ibcli - it is a perl script, you will get a prompt % ./ibcli server ? > Ideally, you should now connect the CLI to a server somewhere, to do this you give it the server and a user and a password, you can do these in either order, it just tries to eventually connect when it has enough data. server ? > conf server 10.0.1.224 user admin password infoblox admin@10.0.1.224 > Or you can also do this from the initial invocation: % ./ibcli -s 10.0.1.230 -u admin -p infoblox "HOLD ON A SEC", I hear you ask, "Why am i connecting to a server, isn't this CLI running on a console somewhere ?". No, Grasshopper, the CLI is just a wrapper to the Infoblox.pm so you can run it from anywhere in the world. This gives you the added advantage of being able to work on MANY servers from the one CLI. =head1 Setting the debug level server ? > conf debug 3 server ? > Or you can also do this from the initial invocation: % ./ibcli -d 3 =head1 Command line options =over =item -s Connect to this server =item -u Connect as this user =item -p Connect with this password =item '-w' Print a CGI header instead of the normal header =item -e Run a single command and exit. The command must be quoted (for obvious reasons). For example % ./ibcli -e 'show zone' =item -b|f load a database backup file for browsing =back =head2 config file If ibcli finds a file in the current working dircetory called '.ibcli.cf' it will silently load and run the commands in that file. This is useful for doing things like auto connection to a server (in CGI mode). =head1 Other Gritty Details OK, so what can I do from here ? =head2 Members and secondaries By default all stuff is added to just the Grid Master, the API guesses this from the SERVER setting. If you explicitly want to add stuff to certain members you need to append the members to the command arg : conf zone add foo.com member 1.2.3.4 conf zone add foo.com member 1.2.3.4 member 1.2.3.5 =head2 Laziness The command parser accepts abbreviations co z a foo.com is the same as conf zone add foo.com =head2 TAB completion and other commands The cli uses a full line editor with history and the usual bells and whistles. Command completion can be achieved by pressing To clear the line press '-u' or '\' To exit the cli, press '-D' (not -c) =head2 BATCH processing You can process command in batch by just writing them to a file and adding it to the command line thus : ibcli -s 10.64.128.50 -u admin -p infoblox demo.cf =cut ######################################## # subs ######################################## sub cli_history { # [ ] History my $hsize = ${ $el->history_get_size } ; # my $hobj = $el->history_get_curr ; my $he = ${ $el->history_get_last } ; print Dumper ( $he ) ; print "$hsize entries\n"; print "--HISTORY not supported--\n"; } sub print_help { #TODO [ ] contextual help if ( $USE_EL ) { # [ ] I'm not sure why I call this here... cmpctl(); my ( $context ) = $el->line ; chomp $context; $context =~ s/\s*$//; # only print help if we are at the beginning of the line if ( $context eq 'help' or ! $context ) { # print "\nhelp for [$context] TBD print " press : for options and word completion '\\' to clear the line 'q' to quit\n\n"; } return CC_REDISPLAY ; } else { print "\nNo help available (Editline disabled)\n\n"; } }; sub print_perldoc { # dumpmyself to perldoc # print "doc : $0 \n"; system ( "perldoc $0 | less" ); return CC_REDISPLAY }; sub process_line { # expand the line, drop errors my ( $line ) = @_ ; # protect special chars $line =~ s/\*/\\\*/g; # [ ] replace the line with any aliases # this may work for substitution matches foreach my $amatch ( keys %ALIASES ) { if ( $line =~ /^$amatch/ ) { # replace the alias with the real string my $arep = $ALIASES{$amatch} ; $line =~ s/^$amatch/$arep/; } } &debug_cli (1,""); my ( $expanded , $error , $matchline ) = &expand_line ( $line ) ; &debug_cli (2,"linec m[$matchline]"); &debug_cli (2,"linec e[$expanded]"); if ( $error ) { print "$error\n" } else { # now go do it my $func = $COMMANDS{ $matchline }{"func"} ; &debug_cli (2,"linec call ($func) ($expanded)"); if ( $func ) { # catch undefined arrays if ( defined ( &$func ) ) { &$func ( $expanded ) ; } else { print " NOT implemented ($func)\n"; } } else { if ( $line ) { my $words = $COMMANDS{ $matchline }{"words"} ; # cleanup some syntaxes $words =~ s/=/ /g; print " Incomplete : $expanded ($words)\n"; } } } # may need a re-display here... # return CC_REDISPLAY return CC_REFRESH } sub expand_line { # if a user hasn't been presing we may have valid syntax that # still needs to be pumped through the abbreviation parser. # # this is a tad tricky because each word needs to be walked through # the command tree to make sure it is in context # # but given input : # # co z foo.com a # # we need to see that this is BOTH : # # configure zone foo.com add (expanded) # configure zone add (used for matching) # # we do this by returning $expline and $matchline my ( $line ) = @_ ; # remove protection of special chars # that was inserted by the shell $line =~ s/\\\*/\*/; # this is just for debugging &debug_cli (1,"ibcli $REV"); if ( $SESSION ) { my $rev = $SESSION->server_version(); &debug_cli (1,"server version [$rev]"); } &debug_cli (3,"linex input [$line]"); # protect stuff in quotes by urlencoding it # do this for all comments in the arg line my @commentargs = $line =~ /"([^"]*)"?/g; foreach my $ori_comment ( @commentargs ) { # make a copy of the string for encoding my $enc_comment = $ori_comment ; # now URL encode it $enc_comment =~ s/(\W)/sprintf ("%%%02X", ord ($1))/ge ; # protect the original string for our regex $ori_comment =~ s/(\W)/\\$1/g; # and re-write the line by swapping the comment chunk # with a URLencoded version if ($ori_comment) { # $line =~ s/$ori_comment/$enc_comment/; $line =~ s/"$ori_comment"/"$enc_comment"/; } &debug_cli (3,"linex enc-comment [$ori_comment][$enc_comment]"); } &debug_cli (3,"linex cinput [$line]"); # walk each word ( split on ) my $expline = "" ; my $matchline = "" ; my $error = "" ; foreach my $thisarg ( split ( /\s+/ , $line ) ) { &debug_cli (3,"linex ----START arg-----"); my $origarg = $thisarg ; # convert the arg to lowercase, # but this also LC's all RHS statements, so not the best idea # unless ( $thisarg =~ /::/ # or $thisarg =~ /=/ ) { # $thisarg = lc($thisarg); # } &debug_cli (3,"linex arg a($thisarg)<-($origarg)"); # &debug_cli (3,"linex partial e[$expline]"); &debug_cli (3,"linex partial m[$matchline]"); # we now have the short word, # we have to look at the matchline to get some context # then we can expand the word # we need the context so we know what the next word is my ( @words ) = get_context( $matchline ) ; my ($done,$sptype,$expword,@matches) = &expand_word ($thisarg,@words) ; &debug_cli (3,"linex match [$done][$sptype] exp($expword)or[@matches]"); &debug_cli (3,"linex exp [$thisarg]->[$expword]"); if ( $expword and @matches ) { # ambiguous line, drop an error, exit $error = " Ambiguous : $expline(@matches)"; # break out of the arg reading loop.. return ( $expline , $error , $matchline) ; } elsif ( $sptype ) { # we're a special, record this for regex matching $expline .= "$expword " ; $matchline .= "$sptype " ; } elsif ( ! $expword ) { # NO MATCH, Bad word $expline .= "$thisarg" ; $matchline .= "$thisarg" ; # find out where the error was.. (kinda tricky) # the bad word will be at the end of $expanded ( my $good = $line ) =~ s/(.*)$thisarg.*$/$1/; # # and unclean protected strings # $good =~ s/%20s/ /g ; # and use this to put a marker at the right place my $slen = length ( $PROMPT) + length ( $good ) ; my $spc = sprintf ( "%*s^" , $slen , " " ) ; &debug_cli (3,"linex estrip [$thisarg] from [$good] [$spc]"); # create the error string. # $error = "$spc\n Unknown argument at '^'"; $error = "$spc--- Unknown argument at marker"; return ( $expline , $error , $matchline) ; } else { # standard word $expline .= "$expword" ; $matchline .= "$expword" ; } } # now clean it up $expline =~ s/\s+$//; $matchline =~ s/\s+$//; # and unclean comments # $expline =~ s/%20s/ /g; # $matchline =~ s/%20s/ /g; return ( $expline , $error , $matchline ) ; } sub lastarg { # given a string, determine what the last arg is. my ($line) = @_ ; # we need the last 2 words to get context # - last complete word # - last incomplete word # this string can be any combination of : # '' # '' # ' ' # '.. ' # '.. ' # if there is trailing space - that is the last arg # if there is no trailing space, the last word is the lastarg # we also return the previous word, as this is useful sometimes &debug_cli (4,"lastarg line l($line)"); my ( $prearg , $lastarg ) = $line =~ /(\S+)\s+(\S+)$/ ; if ( $line =~ /(\S+)\s+$/ ) { $prearg = $1 , $lastarg = "" }; if ( $line =~ /^(\S+)$/ ) { $prearg = "NULL" , $lastarg = $1 }; if ( $line =~ /^$/ ) { $prearg = "NULL" , $lastarg = $1 }; return ( $prearg , $lastarg ) ; } sub add_context { # shove an new context into the hash my ( $context , $words , $add_func , $copyfrom ) = @_ ; # only create NEW structures # unless ( $COMMANDS{ $context }{"words"} ) { unless ( $COMMANDS{ $context } ) { $COMMANDS{ $context }{"words"} = $words ; # there should already be a context for $line # so we may choose to copy the function # ( we DON'T do this for things like the lhs of = ) my $func = $COMMANDS{ $copyfrom }{"func"} || "" ; if ( $add_func ) { $COMMANDS{ $context }{"func"} = $COMMANDS{ $copyfrom }{"func"} ; } &debug_cli (4,"addcon f($func) ($context) [$words]"); } } sub get_context { # we are passed a string which SHOULD be the whole line EXCEPT for # THE LAST ARG, we use this to get some sort of context and the next # list of possible words my ( $line , $lastarg ) = @_ ; $line =~ s/\s+$//; &debug_cli (3,"contxt line [$line]"); # we need to check is we're at the beginnning of the line if ( ! $line ) { $line = "NULL" } ; # the hash is $COMMANDS { "" } { "words|func|help" } # $COMMANDS { "" } { "words" } # # Can't use an undefined value as an ARRAY reference # now find a matching line of context in the commend hash. my @words ; if ( $COMMANDS{"$line"}{'words'} ) { my $wordlist = $COMMANDS{"$line"}{'words'} ; # We need to clean up some of the odd syntaxes the list # of words, so we do neat things before creating an array # 'word=' pair : if that's the case we have to get a # tad clever and dynamically add new matches to the context array. # this is easier that generating all permutations at compile time # we'll just add new contextes at run time. # walk the words one at a time foreach my $cw ( split ( /\s+/ , $wordlist ) ) { if ( $cw =~ /\|/ ) { # word|word|word : # add an additional context with the same arg list foreach my $alt ( split /\|/ , $cw ) { # now check for 'word=|word=|word' : if ( $alt =~ /(\S+)=(<\S+>)/ ) { push ( @words , $1 ) ; # add a context for lhs (with no func) &add_context ( "$line $1" , $2 , 0 , 0 ) ; # and the rhs with the original list &add_context ( "$line $1 $2" , $wordlist , 1 , $line ) ; } else { # just add the word push ( @words , $alt ) ; # add a context for the altword &add_context ( "$line $alt" , $wordlist , 1 , $line ) ; } } } elsif ( $cw =~ /(\S+)=(<\S+>)/ ) { # word= : # drop '=' , add an additional context push ( @words , $1 ) ; # add a context for lhs &add_context ( "$line $1" , $2 , 0 , 0 ) ; # add a terminating context for rhs &add_context ( "$line $1 $2" , "" , 1 , $line ) ; } else { # normal word push ( @words , $cw ) ; } } } else { @words = ( "" ) ; } &debug_cli (4,"contxt words c(@words) "); return ( sort @words ) ; } ################ # TAB completion # This is complex in its simplicity ################ sub cmpctl { # completion control... called when we print "\n" if $DEBUG ; # first, we have to get the last input my ( $line , $icur , $ilast ) = $el->line ; chomp $line ; # Q) how do you handle a line that has been edited ? # like you press before an existing word ? # A) you ignore it, and don't complete the word # now we have to expand this line and parse it # this seems redundant, but there may be words in the line that were # not previously completed. my ( $expanded , $exerr , $matchline ) = &expand_line ( $line ) ; &debug_cli (3,"cmpctl -------"); &debug_cli (3,"cmpctl e[$expanded]"); &debug_cli (3,"cmpctl m[$matchline]"); # [-] now - expandline may drop an error if we made a typo # EARLIER in the wordlist. # if so we should STOP and report the syntax error if ( $exerr =~ /unknown/i ) { # find out where the error was.. (kinda tricky) # the bad word will be at the end of $expanded print "\n$exerr\n"; return CC_REDISPLAY ; } # so we now have expanded the line, we can use this to get the # context (again), but we have to rip out the last arg from it my ( $prearg , $lastarg ) = &lastarg ($line ) ; &debug_cli (3,"cmpctl lastarg p($prearg) l($lastarg)"); my $checkline = $matchline ; if ( $prearg eq "NULL" ) { $checkline = "NULL" } elsif ($lastarg ) { # now we so some tricky substitution # putting the last arg back as it was $checkline =~ s/\s($lastarg)\S*$/ /; # or cleansing special markers ( etc ) $checkline =~ s/\s<\S*>$/ /; } &debug_cli (3,"cmpctl chkline [$checkline]"); # we pass the last arg to get_context anyway, as we may need it my ( @words ) = get_context ( $checkline , $lastarg ) ; # then we call expand_word() which will find a match and return # - the expanded word (unique match) # - no change ( a special word ) # - no change ( ambiguous , list of matches ) # - nothing ( no match , list of matches ) my ($done,$sptype,$expword,@matches) = &expand_word ($lastarg,@words) ; &debug_cli (3,"cmpctl match [$done][$sptype] ($expword) alt[@matches]"); # based on this, do something useful if ( $expword and $done ) { # finish the line, add the missing charagters &debug_cli (3,"cmpctl uniq ($lastarg)+[$expword]"); ( my $append = $expword ) =~ s/^$lastarg//i ; # BUT only do it if the cursor is in the right place if ( $icur = $ilast ) { $el->push("$append"); } } elsif ( $expword and @matches ) { # print matching options &debug_cli (3,"cmpctl ambig ($expword) [@matches]"); print "\n @matches\n"; } elsif ( $expword ) { # do nothing (NOTE, specials match AFTER uniqs ) &debug_cli (3,"cmpctl spec ($expword) [@matches]"); } # [-] i don't think we ever get here # no, it gets caught by errors with expandline elsif ( ! $expword ) { # delete the string , print all options my $len = length ( $lastarg ) ; &debug_cli (3,"cmpctl cut ($expword)-[$len]"); $el->deletestr( $len ); print "\n @matches\n"; } # Redisplay entire input line return CC_REDISPLAY ; } sub expand_word { # given an input and a list of matches, return either # - the bit to append word (unique match) # - no change ( a special word ) # - no change ( ambiguous , list of matches ) # - nothing ( no match ) my ( $inword , @words ) = @_ ; # then force an error if there are illegal characters # by not making the regex match anything # ( this catches words like '' ) $inword =~ s/^ $expword .= " "; &debug_cli (4,"wordx uniq ($inword) -> [$expword]"); $completed = 1 ; } else { # no match , resolve ambiguities @matches = sort grep ( /^$regex/ , @words ) ; my @specials = sort grep ( /^ ($expword)"); @matches = sort @words ; } } } # WGDH-- # now we UNDO the alternate words by substuting it back # and we have to match on the expansion # if ( $expword =~ /^$altword/ ) { # # do these in the right order... # $expword =~ s/$altword/$altreal/; # # &debug_cli (4,"wordx alt ($inword)->[$expword]"); # } # --WGDH # return the results &debug_cli (4,"wordx word ($inword)->[$expword]"); return ( $completed , $spectype , $expword , @matches ) ; } ########################################### # # call an infoblox function, ONLY if we're connected # sub do_func { # assume we are passed a single object # my ( $method , @data ) = @_ ; &debug_cli (2,"session method = ($method)"); &debug_cli (3,"scheduled at = ($data[1])"); # if we are doing an add or a modify, we should be passing a single # object, and if so we need to do some post processing cleanup my $obj = $data[0] ; if ( ref($obj) ) { # this is an object and not a search # we can't call a method if it isn't valid for an object # so we test if the method is there, then, do something with it if ( exists $obj->{comment} ) { # clean up the comment my $c = $obj->comment(); $c = decode_comment( $c ) ; # and save it $obj->comment( $c ) } if ( exists $obj->{extensible_attributes} ) { # url and utf encode these as well # before we pass the object to $session # we can use a ref to re-set the values directly my $exts = $obj->extensible_attributes(); foreach my $field ( keys %{ $exts } ) { # get and url/utf re-code the value my $val = $exts->{$field}; # but be careful with LIST values.. unless ( $val =~ /ARRAY/ ) { $val = decode_comment( $val ) ; # save the value $exts->{$field} = $val; } } # save the new EA data $obj->extensible_attributes ( $exts ) ; } # re-save the object $data[0] = $obj ; # and debug if ( $DEBUG >= 3 ) { print Dumper ( $obj ) ; } } else { # must be a search if ( $DEBUG >= 2 ) { # coerce it back into a hash for easier reporting my %h = @data ; print Dumper ( \%h ) ; } } my @ret_objects ; # actually try and run it if ( $ONLINE ) { # call by indirection @ret_objects = $SESSION->$method ( @data ) ; &debug_cli (3,"obj out -----------"); print Dumper ( \@ret_objects ) if $DEBUG >= 3 ; # get errors my $result = $SESSION->status_code(); my $response = $SESSION->status_detail(); if ( $result > 0 ) { # We got error... $response =~ s/\s*\n\s*(Code:.*)//; print " Error : API returned : $response ($result)\n"; } } else { print " CANNOT EXECUTE - no connection is in place\n"; } return ( @ret_objects ) ; } ########################################### # # actual API calling functions. # ########################################### =head1 GENERAL COMMAND REFERENCE The syntax mostly matches the API syntax. Also, since this is based on the API (duh), the same restrictions about required arguments applies. Don't expect the cli to work around requiring dumb extra arguments. When you look deep into the API you realise why they are there. =cut =head1 DNS Configuration =head2 Add a view to dns conf zone add view internal Add a view disabled : conf zone add view internal disabled You can also modify views conf zone modify view internal set match_clients=[1.1.1.1,2.2.2.2] And enable/disable it conf zone modify view internal enabled conf zone modify view internal disabled =cut sub cli_add_view { my ( $line ) = @_ ; # get the args from the line my ( $type, $view ) = $line =~/ (add|modify) view (\S+)/ ; my ( $disable ) = $line =~/ (disabled)/ ; my ( $enable ) = $line =~/ (enabled)/ ; my @ipam = $line =~/ info (\S+)/g ; my @settings = $line =~/ set (\S+)/g ; # get and de-urlencode the comment string my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; $comment = decode_comment( $comment ) ; $view = decode_comment( $view ) ; &debug_cli(1,"addv v($view) c($comment)"); my $obj = Infoblox::DNS::View->new ( "name" => "$view", ); # if we are in modify mode, get the view first... if ( $type eq "modify" ) { # this might become a subroutine my %opthash = ( "object" => "Infoblox::DNS::View", "name" => $view ); my @views = &do_func("get", %opthash ); if ( $#views > 0 ) { # we found too many my $list = &dump_summ ( "name" , @views ); print " Error : $view : More than 1 views found : $list\n"; # exit here return } elsif ( ! @views ) { # we found too few print " Error : $view : No views found\n"; # exit here return } else { $obj = shift @views ; } } # now add IPAM info $obj = add_ipam( $obj , @ipam ) ; if ( $comment ) { $obj->comment ( $comment ) } if ( $disable ) { $obj->disable ( "TRUE" ) } if ( $enable ) { $obj->enable ( "FALSE" ) } if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # addit &do_func($type,$obj); } =head2 Remove a view from dns conf zone delete view internal =cut sub cli_del_view { my ( $line ) = @_ ; # get the args from the line my ( $view ) = $line =~/ view (\S+)/ ; $view = decode_comment( $view ) ; &debug_cli(1,"delv v($view)"); my ( $obj ) = &do_func("get", ( "object" => "Infoblox::DNS::View", "name" => $view )); # trashit &do_func("remove",$obj) if $obj; } # # shared record groups # =head2 Add a shared record group conf zone add shared_record_group my_group conf zone delete shared_record_group my_group =cut sub cli_add_shared_group { my ( $line ) = @_ ; require_api( "4.2r1-0" ) or return; # get the args from the line my ( $name ) = $line =~/shared_record_group (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; &debug_cli(1,"addv v($name) c($comment)"); my $obj = Infoblox::DNS::SRG->new ( "name" => "$name", ); if ( $comment ) { $obj->comment ( $comment ) } # addit &do_func("add",$obj); } sub cli_del_shared_group { my ( $line ) = @_ ; # version specific... require_api( "4.2r1-0" ) or return; # get the args from the line my ( $name ) = $line =~/shared_record_group (\S+)/ ; &debug_cli(1,"delv v($name)"); my ( $obj ) = &do_func("get", ( "object" => "Infoblox::DNS::SRG", "name" => $name )); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a Zone Zones can be added as either forward or reverse. If you add a zone of the form n.n.n.n/mm it will assume it is a reverse zone Add a forward zone conf zone add foo.com Add a reverse zone conf zone add 10.0.1.0/24 conf zone add 10.in-addr.arpa Add a zone to specific members conf zone add foo.com primary 1.2.3.4 secondary 1.2.3.5 sec 1.2.3.6 Add a zone to a specific view conf zone add foo.com view internal Add a zone with extensible attributes You can add multiple attributes by having multiple 'info' pairs conf zone add foo.com ... info = Add a zone with external primary conf zone add foo.com ext_primary ns1.foo.com,1.2.3.4 Add a zone with stealth primary or secondary conf zone add foo.com stealth_secondary 1.2.3.20 conf zone add foo.com stealth_ext_secondary ns1.foo.com,1.2.3.4 conf zone add foo.com stealth_ext_primary ns0.foo.com,1.2.3.4 Add a zone with an nsgroup conf zone add foo.com ns_group mygroup Add a shared record group to a zone conf zone modify foo.com shared_record_group mygroup conf zone add foo.com shared_record_group mygroup Add a forwarding zone (and assign it to members) conf zone add fwd.foo.com forward_to ns1.x.com,2.2.2.2 forward_to ns2.x.com,3.3.3.3 member 10.0.0.20 Add a delegated zone configure zone add deleg.foo.com delegate_to ns1.s.foo.com,1.2.3.4 Add a stub zone (and assign it to members) conf zone add foo.com stub_from ns0.foo.com,1.2.3.4 mem 2.2.2.2 =head2 Import a zone Zones can be imported as either forward or reverse. You just add an extra argument of the import source Import a forward zone conf zone add foo.com import 45.0.1.220 Import a reverse zone conf zone add 10.0.1.0/24 import 45.0.1.220 Auto Generate hosts You can also just post process a zone and create host records conf zone mod foo.com generate_hosts Set a random API method on a zone conf zone add foo set = conf zone add foo set disable_forwarding=FALSE When setting arrays you need special syntax "[ ]" and seperate your values by a ',' (This mostly seems to work) ... set allow_update="[10.0.0.0/8,168.147.0.0/24,192.168.114.0/24]" =cut sub cli_add_zone { # essentially all options are passed as name value pairs # my ( $line ) = @_ ; # get the args from the line my ($type , $zone) = $line =~/ zone (add|modify) (\S+)/ ; my ($import) = $line =~/ import (\S+)/ ; my ($genhost) = $line =~/\b(generate_hosts)\b/ ; my ($nonauthorative) = $line =~ /\bnonauthorative\b/ ; my ($pri) = $line =~/ primary (\S+)/ ; my ($stealth_pri) = $line =~/ stealth_primary (\S+)/ ; my ($nsgroup) = $line =~/ ns_group (\S+)/ ; my ($email) = $line =~/ email (\S+)/ ; my ($serial) = $line =~/ serial (\S+)/ ; my ($mname) = $line =~/ mname (\S+)/ ; my ($prefix) = $line =~/ prefix (\S+)/ ; my (@ext_pri) = $line =~/ ext_primary (\S+)/g ; my (@forwarders) = $line =~/ forward_to (\S+)/g ; my (@delegates) = $line =~/ delegate_to (\S+)/g ; my (@stubs) = $line =~/ stub_from (\S+)/g ; my (@sec) = $line =~/ secondary (\S+)/g ; my (@stealth_sec) = $line =~/ stealth_secondary (\S+)/g ; my (@mems) = $line =~/ member (\S+)/g ; my (@ext_sec) = $line =~/ ext_secondary (\S+)/g ; my (@stealth_ext_sec) = $line =~/ stealth_ext_secondary (\S+)/g ; my (@views) = $line =~/ view (\S+)/g ; my (@shared_groups) = $line =~/ shared_record_group (\S+)/g ; my @ipam = $line =~/ info (\S+)/g ; my @settings = $line =~/ set (\S+)/g ; # de-urlencode the name string # if ( $comment ) { $obj->comment ( $comment ) } my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; $comment = decode_comment( $comment ) ; # hack for in-addr.arpa syntax if ( $zone =~ /.in-addr.arpa$/i ) { $zone = arpa_to_net ( $zone ) ; } &debug_cli (1,"addz z($zone) i($import) p($pri) ep(@ext_pri) s(@sec) es(@ext_sec) se(@settings)"); &debug_cli (1,"modz z($zone) gh($genhost)"); # create the zone... my $obj; if ( $type eq "modify" ) { # get the zone my %opthash = ( "object" => "Infoblox::DNS::Zone", "name" => $zone ); # check the view if ( @views ) { my $view = $views[0] ; $view = decode_comment ( $view ) ; $opthash{'view'} = $view } my @zones = &do_func("get", %opthash ); if ( $#zones > 0 ) { # we found too many my $list = &dump_summ ( "name" , @zones ); print " More than 1 zone found : $list\n"; # exit here return } elsif ( ! @zones ) { # we found too few print " No zone found\n"; # exit here return } else { $obj = shift @zones ; } } else { $obj = Infoblox::DNS::Zone->new ( "name" => $zone, ); } if ( $comment ) { $obj->comment ( $comment ) } if ( $prefix ) { $obj->prefix ( $prefix ) } # [ ] make this a function... if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # find a member to be the primary (external or otherwise) # or is it a forward zone... if ( @forwarders ) { # external primaries my @fwd_svrs ; foreach my $fwdr ( @forwarders ) { my ( $fwname , $fwd_ip ) = split ( /,/ , $fwdr ) ; push @fwd_svrs , Infoblox::DNS::Nameserver->new( name => $fwname, "ipv4addr" => $fwd_ip , ); &debug_cli (1,"addz z($zone) fwd($fwd_ip)"); } $obj->forward_to( \@fwd_svrs ) ; # [x] and add the right members... my @memlist ; foreach my $mem ( @mems ) { # normaal members push @memlist , Infoblox::DNS::Member->new( # "name" => $value , "ipv4addr" => $mem , ); &debug_cli (1,"addz z($zone) mem($mem)"); } $obj->members ( \@memlist ) ; } # end forward type zones # are we delegating ? elsif ( @delegates ) { my @del_svrs ; foreach my $ns ( @delegates ) { # get the name , ip pair my ( $nsname , $ip ) = split ( /,/ , $ns ) ; &debug_cli (1,"addz z($zone) deleg($nsname)($ip)"); push @del_svrs , Infoblox::DNS::Nameserver->new( 'name' => $nsname, 'ipv4addr' => $ip, ); } $obj->delegate_to ( \@del_svrs ) ; } # end delegated elsif ( @stubs ) { my @stu_svrs ; foreach my $ns ( @stubs ) { # get the name , ip pair my ( $nsname , $ip ) = split ( /,/ , $ns ) ; &debug_cli (1,"addz z($zone) stub($nsname)($ip)"); push @stu_svrs , Infoblox::DNS::Nameserver->new( 'name' => $nsname, 'ipv4addr' => $ip, ); } $obj->stub_from ( \@stu_svrs ) ; # [x] and add the right members... my @memlist ; foreach my $mem ( @mems ) { # normal members push @memlist , Infoblox::DNS::Member->new( # "name" => $value , "ipv4addr" => $mem , ); &debug_cli (1,"addz z($zone) mem($mem)"); } $obj->stub_members ( \@memlist ) ; } # end type stub # using ns groups ? elsif ( $nsgroup ) { $nsgroup = decode_comment( $nsgroup ) ; $obj->ns_group( $nsgroup ) ; } # or normal nameservers ? else { if ( $stealth_pri ) { # member primary my $pri_ns = Infoblox::DNS::Member->new ( 'stealth' => "TRUE" , 'ipv4addr' => $stealth_pri, ); $obj->primary( $pri_ns ) ; &debug_cli (2,"addz z($zone) mem-p($pri)"); } elsif ( $pri ) { # member primary my $pri_ns = Infoblox::DNS::Member->new ( 'ipv4addr' => $pri, ); $obj->primary( $pri_ns ) ; &debug_cli (2,"addz z($zone) mem-p($pri)"); } elsif ( @ext_pri ) { # external primaries my @ext_svrs ; foreach my $external ( @ext_pri ) { # we override with an external primary my ( $nsname , $ip ) = split ( /,/ , $external ) ; &debug_cli (2,"addz z($zone) ext-p($nsname)($ip)"); my $primary = Infoblox::DNS::Nameserver->new ( 'name' => $nsname, 'ipv4addr' => $ip, ); push @ext_svrs , $primary ; } $obj->primary( \@ext_svrs ) ; } # don't apply any nameservers if it is nonauth # don't apply the GM nameserver if we are modifying elsif ( $MASTER_IP and !$nonauthorative and $type eq "add" ) { # use the server as the primary my $pri_ns = Infoblox::DNS::Member->new ( 'ipv4addr' => $MASTER_IP, ); $obj->primary( $pri_ns ) ; &debug_cli (2,"addz z($zone) mas-p($MASTER_IP)"); } # find secondaries my @sec_svrs ; foreach my $sec_ip ( @sec ) { # normaal members push @sec_svrs , Infoblox::DNS::Member->new( # "name" => $value , "ipv4addr" => $sec_ip , ); &debug_cli (2,"addz z($zone) sec($sec_ip)"); } # lead secondaries my ($lead) = $line =~/ lead (\S+)/ ; if ( $lead ) { push @sec_svrs , Infoblox::DNS::Member->new( "ipv4addr" => $lead , 'lead' => "TRUE" , ); &debug_cli (2,"addz n($zone) l-sec($lead)"); } # stealth secondaries foreach my $sec_ip ( @stealth_sec ) { # normaal members push @sec_svrs , Infoblox::DNS::Member->new( # "name" => $value , "ipv4addr" => $sec_ip , 'stealth' => "TRUE" , ); &debug_cli (2,"addz z($zone) s-sec($sec_ip)"); } # external secondaries foreach my $ext_ip ( @ext_sec ) { # external secondaries my ( $nsname , $ip ) = split ( /,/ , $ext_ip ) ; # IP is optional (kinda) if ( ! $ip ) { $ip = "255.255.255.255" } my $secondary = Infoblox::DNS::Nameserver->new ( 'name' => $nsname, 'ipv4addr' => $ip, ); push @sec_svrs , $secondary ; &debug_cli (2,"addz z($zone) ext-s($nsname)($ip)"); } # stealth external secondaries foreach my $sext_ip ( @stealth_ext_sec ) { # external secondaries my ( $nsname , $ip ) = split ( /,/ , $sext_ip ) ; # IP is optional (kinda) if ( ! $ip ) { $ip = "255.255.255.255" } my $secondary = Infoblox::DNS::Nameserver->new ( 'name' => $nsname, 'ipv4addr' => $ip, 'stealth' => "TRUE" , ); push @sec_svrs , $secondary ; &debug_cli (2,"addz z($zone) s-ext-s($nsname)($ip)"); } # add any secondaries we found parsing the args if ( @sec_svrs ) { $obj->secondaries ( \@sec_svrs ) ; } } # end normal zone # do we import ? if ( $import ) { $obj->import_from ( $import ) ; &debug_cli (2,"addz z($zone) imp($import)"); } # do we do a host abstraction only if ( $genhost ) { if ( require_api ( "5.1r1-2" ) ) { # we need some value for import_from unless ( $import ) { $obj->import_from ( "255.255.255.255" ) ; } $obj->do_host_abstraction ( "true" ) ; } } # do we add it to a view ? if ( @views ) { my @view_objs ; foreach my $vname ( @views ) { $vname = decode_comment ( $vname ) ; my $view = Infoblox::DNS::View->new ( "name" => $vname ); push @view_objs , $view ; } $obj->views ( \@view_objs ) ; } # now add IPAM info $obj = add_ipam( $obj , @ipam ) ; # do we add it a shared record group ? if ( @shared_groups ) { $obj->shared_record_groups ( \@shared_groups ) ; } # email if ( $email ) { $obj->email ( $email ) ; &debug_cli (2,"addz z($zone) email($email)"); } if ( $serial ) { $obj->soa_serial_number ( $serial ) ; $obj->override_serial_number ( "TRUE" ) ; } if ( $mname ) { $obj->soa_mname ( $mname ) ; $obj->override_soa_mname ( "TRUE" ) ; } # now add the damn thing # ( the method is defined in $type ); &do_func($type,$obj); } =head2 Remove a zone Forward or reverse, the syntax follows the same logic as add zone Remove a forward zone conf zone del foo.com Remove a reverse zone conf zone del 10.0.1.0/24 =cut sub cli_delete_zone { my ( $line ) = @_ ; # get the args from the line ( my $zone) = $line =~/ zone delete (\S+)/ ; ( my $view) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"remz n($zone) v($view)"); my %opthash = ( object => "Infoblox::DNS::Zone", name => $zone, ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); &do_func("remove",$obj) if $obj; } =head2 Copy a zone to another view you need the source and destination zone and views conf zone copy view to dest_view conf zone copy zone.com view int to newzone.com dest_view external =cut sub cli_copy_zone { my ( $line ) = @_ ; # get the args from the line ( my $zone) = $line =~/ zone copy (\S+)/ ; ( my $view) = $line =~/ view (\S+)/ ; ( my $dst_zone) = $line =~/ to (\S+)/ ; ( my $dst_view) = $line =~/ dest_view (\S+)/ ; &debug_cli(1,"copz n($zone) v($view) t($dst_zone) dv($dst_view)"); my %opthash = ( zone => $zone, view => $view, dest_view => $dst_view, dest_zone => $dst_zone, ); &do_func("copy_records",%opthash); } =head2 Add a NS group conf zone add ns_group internal primary 45.0.12.20 secondary 45.0.128.20 You can also call this as a grid command conf grid Infoblox dns add ns_group internal ... If you want to modify a ns_group, secondaries and external_secondaries will get ADDED to the current list, (instead of replacing the current list) conf zone mod ns_group Internal secondary 45.0.128.20 =cut sub cli_add_nsgroup { # essentially all options are passed as name value pairs # need : # name # primary # secondaries # my ( $line ) = @_ ; # get the args from the line my ($type , $name) = $line =~/ (add|modify) ns_group (\S+)/ ; my ($pri) = $line =~/ primary (\S+)/ ; my ($stealth_pri) = $line =~/ stealth_primary (\S+)/ ; my (@ext_pri) = $line =~/ ext_primary (\S+)/g ; my (@sec) = $line =~/ secondary (\S+)/g ; my (@ext_sec) = $line =~/ ext_secondary (\S+)/g ; my (@stealth_sec) = $line =~/ stealth_secondary (\S+)/g ; my (@stealth_ext_sec) = $line =~/ stealth_ext_secondary (\S+)/g ; &debug_cli (1,"addnsg t($type) n($name) p($pri) ep(@ext_pri) s(@sec) es(@ext_sec)"); # create the zone... my $obj; my @sec_svrs ; if ( $type eq "modify" ) { # get the zone my %opthash = ( "object" => "Infoblox::Grid::DNS::Nsgroup", "name" => $name ); my @groups = &do_func("get", %opthash ); if ( $#groups > 0 ) { # we found too many my $list = &dump_summ ( "name" , @groups ); print " More than 1 nsgroup found : $list\n"; # exit here return } elsif ( ! @groups ) { # we found too few print " No nsgroup found\n"; # exit here return } else { $obj = shift @groups ; } # set some lists @sec_svrs = @{ $obj->secondaries() }; } else { $obj = Infoblox::Grid::DNS::Nsgroup->new ( "name" => $name, ); } # find a member to be the primary (external or otherwise) # or normal nameservers ? if ( $stealth_pri ) { # member primary my $pri_ns = Infoblox::DNS::Member->new ( 'stealth' => "TRUE" , 'ipv4addr' => $stealth_pri, ); $obj->primary( $pri_ns ) ; &debug_cli (2,"addnsg n($name) mem-p($pri)"); } elsif ( $pri ) { # member primary my $pri_ns = Infoblox::DNS::Member->new ( 'ipv4addr' => $pri, ); $obj->primary( $pri_ns ) ; &debug_cli (2,"addnsg n($name) mem-p($pri)"); } elsif ( @ext_pri ) { # external primaries my @ext_svrs ; foreach my $external ( @ext_pri ) { # we override with an external primary my ( $nsname , $ip ) = split ( /,/ , $external ) ; &debug_cli (2,"addnsg n($name) ext-p($nsname)($ip)"); my $primary = Infoblox::DNS::Nameserver->new ( 'name' => $nsname, 'ipv4addr' => $ip, ); push @ext_svrs , $primary ; } $obj->primary( \@ext_svrs ) ; } elsif ( $MASTER_IP and $type eq "add" ) { # use the server as the primary # don't apply any nameservers if it is nonauth # don't apply the GM nameserver if we are modifying my $pri_ns = Infoblox::DNS::Member->new ( 'ipv4addr' => $MASTER_IP, ); $obj->primary( $pri_ns ) ; &debug_cli (2,"addnsg n($name) mas-p($MASTER_IP)"); } # find secondaries foreach my $sec_ip ( @sec ) { # normaal members push @sec_svrs , Infoblox::DNS::Member->new( "ipv4addr" => $sec_ip , ); &debug_cli (2,"addnsg n($name) sec($sec_ip)"); } # lead secondaries my ($lead) = $line =~/ lead (\S+)/ ; if ( $lead ) { push @sec_svrs , Infoblox::DNS::Member->new( "ipv4addr" => $lead , 'lead' => "TRUE" , ); &debug_cli (2,"addnsg n($name) l-sec($lead)"); } # stealth secondaries foreach my $sec_ip ( @stealth_sec ) { # normaal members push @sec_svrs , Infoblox::DNS::Member->new( "ipv4addr" => $sec_ip , 'stealth' => "TRUE" , ); &debug_cli (2,"addnsg n($name) s-sec($sec_ip)"); } # external secondaries foreach my $ext_ip ( @ext_sec ) { # external secondaries my ( $nsname , $ip ) = split ( /,/ , $ext_ip ) ; # IP is optional (kinda) if ( ! $ip ) { $ip = "255.255.255.255" } my $secondary = Infoblox::DNS::Nameserver->new ( 'name' => $nsname, 'ipv4addr' => $ip, ); push @sec_svrs , $secondary ; &debug_cli (2,"addnsg n($name) ext-s($nsname)($ip)"); } # stealth external secondaries foreach my $sext_ip ( @stealth_ext_sec ) { # external secondaries my ( $nsname , $ip ) = split ( /,/ , $sext_ip ) ; # IP is optional (kinda) if ( ! $ip ) { $ip = "255.255.255.255" } my $secondary = Infoblox::DNS::Nameserver->new ( 'name' => $nsname, 'ipv4addr' => $ip, 'stealth' => "TRUE" , ); push @sec_svrs , $secondary ; &debug_cli (2,"addnsg n($name) s-ext-s($nsname)($ip)"); } # add any secondaries we found parsing the args # print Dumper ( \@sec_svrs ) ; if ( @sec_svrs ) { $obj->secondaries ( \@sec_svrs ) ; } # now add the damn thing # ( the method is defined in $type ); &do_func($type,$obj); } =head2 Delete a NS group conf zone del ns_group internal You can also call this as a grid command conf grid Infoblox dns del ns_group internal =cut sub cli_del_nsgroup { my ( $line ) = @_ ; # get the args from the line ( my $name) = $line =~/ zone delete ns_group (\S+)/ ; &debug_cli(1,"remnsg n($name)"); my %opthash = ( object => "Infoblox::Grid::DNS::Nsgroup", name => $name, ); my ( $obj ) = &do_func("get",%opthash); &do_func("remove",$obj) if $obj; } =head2 Add a host to a zone conf zone foo.com add host test 1.2.3.4 conf zone foo.com add host test 1.2.3.4 comment "this is a comment" If you don't know the zone, then just put in a blank and use a FQDN conf zone "" add host test.foo.com 1.2.3.4 Add a host to a specific view conf zone "" add host test.foo.com 1.2.3.4 view internal Add hosts with multiple ip addresses. seperate the values with a ',' conf zone foo.com add host test 1.2.3.4,1.2.3.5 Add hosts with multiple ip addresses and mac addresses, append the mac address with ':' to the ipaddress conf zone foo.com add host test 1.2.3.4:00:FE:00:01:02:03,1.2.3.5 To add hosts with fixed addresses, and a fixed addr template conf zone foo.com add host test 1.2.3.4:00:FE:00:01:02:03 template mytemp Add Hosts with Fixed addreses and settings, The settings could be on the fixed address, or the host itself. So you have 2 directives - to set something on the Fixed Address : conf zone add host ... set = - to set something on the Host Address : conf zone add host ... set_host = Add hosts with fixed addresses and options. only the fixed addr can take options, so use the option directive conf zone add host ... option = Add hosts with aliases (CNAMES) conf zone foo.com add host test 1.2.3.4 alias www alias ftp Adding hosts with IPAM or Extensible Attributes You add multiple fields with additional 'info ' pairs conf zone info.com add host pc1 2.3.3.3 info Asset=23456-06 Quotes are required of your value has spaces in it : conf zone info.com add host pc2 2.3.3.3 info Custom1="Room 207" configuring a host for no dns (disable for dns) conf zone info.com add host pc2 2.3.3.3 ... nodns =head2 Modifying hosts conf zone foo.com modify host test 1.2.3.4 conf zone foo.com modify host test 1.2.3.4 name test2 conf zone foo.com modify host test 1.2.3.4 comment "this is a comment" =cut sub cli_add_host { my ( $line ) = @_ ; # get the args from the line # my ( $host , $ip ) = $line =~/add host (\S+) (\S+)/ ; my ( $type , $host ) = $line =~/(\S+) host (\S+)/ ; my ( $j , $ip ) = $line =~/(add|modify) host \S+ ($REip\S*)/ ; my ( $rename ) = $line =~/ name (\S+)/; my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $vname ) = $line =~/ view (\S+)/ ; my ( $mac ) = $line =~/mac (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; my ( $disable ) = $line =~/ host \S+.*\b(disabled)\b/ ; my ( $nodns ) = $line =~/ host \S+.*\b(nodns)\b/ ; my ( $ttl ) = $line =~/ ttl (\d+)/ ; my ( $template ) = $line =~/ template (\d+)/ ; my @settings = $line =~/ set (\S+)/g ; my @aliases = $line =~/ alias (\S+)/g ; my @ipam = $line =~/ info (\S+)/g ; my @options = $line =~/ option (\S+)/g ; # check the operation type, if this is an insert we # need to track this my $insert ; if ( $type eq "insert" ) { $type = "modify"; $insert ++; } # de-urlencode the name string $comment = decode_comment( $comment ) ; $vname = decode_comment( $vname ) ; my $name = fqdn ( $host , $zone ) ; &debug_cli(1,"addh t($type) n($name) ip($ip) d($disable) nodns($nodns) c($comment) v($vname)"); my $obj ; # create the object if ( $type eq "modify" ) { # this might become a subroutine # get the host my %opthash = ( "object" => "Infoblox::DNS::Host", "name" => $name ); if ( $vname ) { $opthash{'view'} = $vname; } my @hosts = &do_func("get", %opthash ); if ( $#hosts > 0 ) { # we found too many my $list = &dump_summ ( "name" , @hosts ); print " Error : $name : More than 1 host found : $list\n"; # exit here return } elsif ( ! @hosts ) { # we found too few print " Error : $name : No host found\n"; # exit here return } else { $obj = shift @hosts ; } } else { # new host object $obj = Infoblox::DNS::Host->new ( "name" => $name, ); # force the type $type = "add" ; } # add the ip addresses my @ips = split(',', $ip) ; # look for fixed addresses in the iplist my @iplist ; foreach my $ipv ( @ips ) { if ( $ipv =~ /:/ ) { # we need to make a fixed host my ( $fip , $mac ) = $ipv =~ /($REip):(\S+)/ ; my $fobj = Infoblox::DHCP::FixedAddr->new ( "ipv4addr" => $fip , "mac" => $mac , ); # add setting to the fixed host if ( @settings ) { $fobj = add_settings ( $fobj , @settings ) } # fixed addr templates if ( $template ) { $obj->template ( $template ) } # fixed addr options (CBJ) if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the ',' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addfix o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; # only add options we recognise if ( $opt_obj ) { push @opt_list , $opt_obj ; } } $fobj->options ( \@opt_list ) ; } # add the ip to the list push @iplist , $fobj ; } else { # add the ip to the list push @iplist , $ipv ; } } # print "ip[$ip] [@ips]\n"; # everything here is conditional cos we may # be in modify mode if ( @ips ) { # print "[@ips]\n"; # [ ] if we are in insert mode, we need to add additional # IP addrs to the list ... $obj->ipv4addrs ( \@iplist ); &debug_cli(1,"insh ($insert)"); } if ( $comment ) { $obj->comment ( $comment ) } if ( $disable ) { $obj->disable ( "TRUE" ) } if ( $nodns ) { $obj->configure_for_dns ( "FALSE" ) } if ( $ttl ) { $obj->ttl ( $ttl ) } if ( @aliases ) { $obj->aliases ( \@aliases ) } # set any values my @host_settings = $line =~/ host_set (\S+)/g ; if ( @host_settings ) { $obj = add_settings ( $obj , @host_settings ) } # views if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } # rename-ing a host requires some finesse if the name is # not a FQDN. (and we can't use the 'fqdn()' method) # &debug_cli(1,"addrn ($rename)"); if ( $rename ) { if ( $zone ) { $rename = "$rename.$zone" unless $rename =~ /\./; } # be verbose print " rename host to: $rename\n"; $obj->name ( $rename ) } # now add IPAM info $obj = add_ipam( $obj , @ipam ) ; # addit ( the method is defined in $type ); # make it schedulable my ( $at ) = $line =~/\bat (\S+)/ ; if ( $at ) { &debug_cli(1,"addh at($at)"); &do_func($type,$obj, scheduled_at => $at); } else { &do_func($type,$obj); } } =head2 Remove a host from a zone conf zone foo.com delete host test =cut # take a hostname and a zone and create a FQDN sub fqdn { my ( $host , $zone ) = @_ ; $host =~ s/"//g; $zone =~ s/"//g; my $name ; if ( $host and $zone ) { $name = "$host.$zone"; } elsif ( $host and ! $zone ) { $name = "$host"; } else { $name = $zone } return ( $name ) ; } sub cli_del_host { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host ) = $line =~/host (\S+)/ ; my ( $vname ) = $line =~/ view (\S+)/ ; $vname = 'default' unless $vname ; $vname = decode_comment( $vname ); my $name = fqdn ( $host , $zone ) ; &debug_cli(1,"delh n($name) "); my %opthash = ( object => "Infoblox::DNS::Host", name => $name, ); $opthash{'view'} = $vname if $vname ; my ( $obj ) = &do_func("get",%opthash); &do_func("remove",$obj) if $obj; } =head2 Add an A record to a zone conf zone foo.com add a_record test 1.2.3.4 Add it to a view : conf zone foo.com add a_record test 1.2.3.4 view my_view To add the record to a shared record group (zone must be blank) conf zone "" add a_record test 1.2.3.4 shared_record_group mygroup You can also set values ... set param=val And EAs ... info EA=value =cut # add an A record to a shared record group # we can use the same utility as for adding an A record sub cli_add_a_rec { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host , $ip ) = $line =~/ a_record (\S+) (\S+)/ ; my ( $shared ) = $line =~/shared_record_group (\S+)/ ; my ( $disable ) = $line =~/add a_record \S+.* \b(disabled)\b/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; my ( $ttl ) = $line =~/ ttl (\d+)/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; my $name = fqdn ( $host , $zone ) ; my $obj = Infoblox::DNS::Record::A->new ( "name" => "$name", "ipv4addr" => "$ip", ); # overrwrite this for shared record groups if ( $shared ) { # version specific... require_api( "4.2r1-0" ) or return; $obj = Infoblox::DNS::SharedRecord::A->new ( "name" => "$name", "ipv4addr" => "$ip", "shared_record_group" => "$shared", ); } if ( $ttl ) { $obj->ttl ( $ttl ) } if ( $disable ) { $obj->disable ( "TRUE" ) } # set any values my @settings = $line =~/ set (\S+)/g ; if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # now add IPAM info my @ipam = $line =~/ info (\S+)/g ; $obj = add_ipam( $obj , @ipam ) ; # views my ( $view ) = $line =~/ view (\S+)/ ; if ( $view ) { my $view = Infoblox::DNS::View->new ( name => $view, ); $obj->views ( [ $view ] ) ; } if ( $comment ) { $obj->comment ( $comment ) } &debug_cli(1,"add_a n($host) n($ip) c($comment) sh($shared)"); # addit &do_func("add",$obj); } =head2 Remove An A record from a zone conf zone foo.com delete a_rec test 1.2.3.4 =cut sub cli_del_a_rec { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host , $ip ) = $line =~/ a_record (\S+) (\S+)/ ; my $name = fqdn ( $host , $zone ) ; # views my ( $view ) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"del_a n($name) n($ip) v($view)"); # find and remove my %opthash = ( object => "Infoblox::DNS::Record::A", name => $name, ipv4addr => $ip ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); &do_func("remove",$obj) if $obj; } =head2 Add an AAAA record to a zone conf zone foo.com add AAAA test fe80::0001 To add the record to a shared record group (zone must be blank) conf zone "" add AAAA test ad::2007 shared_record_group mygroup =cut sub cli_add_aaaa { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $disable ) = $line =~/add aaaa \S+.* \b(disabled)\b/ ; my ( $host , $ip ) = $line =~/ aaaa (\S+) (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; my $name = fqdn ( $host , $zone ) ; my $obj = Infoblox::DNS::Record::AAAA->new ( "name" => "$name", "ipv6addr" => "$ip", ); # override this for shared records... my ( $shared ) = $line =~/shared_record_group (\S+)/ ; if ( $shared ) { # version specific... require_api( "4.2r1-0" ) or return; $obj = Infoblox::DNS::SharedRecord::AAAA->new ( "name" => "$name", "ipv6addr" => "$ip", "shared_record_group" => "$shared", ); } # views my ( $vname ) = $line =~/ view (\S+)/ ; if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } if ( $disable ) { $obj->disable ( "TRUE" ) } if ( $comment ) { $obj->comment ( $comment ) } &debug_cli(1,"add_4a n($host) n($ip) d($disable) c($comment)"); # addit &do_func("add",$obj); } =head2 Remove An AAAA record from a zone conf zone foo.com delete AAAA test fe80::0001 =cut sub cli_del_aaaa { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host , $ip ) = $line =~/ aaaa (\S+) (\S+)/ ; my $name = fqdn ( $host , $zone ) ; my ( $view ) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"del_a n($name) n($ip) v($view)"); # find and remove my %opthash = ( object => "Infoblox::DNS::Record::AAAA", name => $name, ipvvaddr => $ip ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); &do_func("remove",$obj) if $obj; } =head2 Add an MX record to a zone conf zone foo.com add mx mail 10 mail.bar.com To add the record to a shared record group (zone must be blank) conf zone "" add mx mail 10 mail.bar.com shared_record_group mygroup =cut sub cli_add_mx { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host , $pref , $dest ) = $line =~/ mx (\S+) (\S+) (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; my $name = fqdn( $host , $zone ) ; # $host =~ s/"//g; # if ( $host ) { $host = "$host.$zone"; } # else { $host = $zone } my $obj = Infoblox::DNS::Record::MX->new ( "name" => $name, "pref" => $pref , "exchanger" => $dest , "comment" => $comment, ); # override this for shared records... my ( $shared ) = $line =~/shared_record_group (\S+)/ ; if ( $shared ) { # version specific... require_api( "4.2r1-0" ) or return; $obj = Infoblox::DNS::SharedRecord::MX->new ( "name" => $name, "pref" => $pref , "exchanger" => $dest , "comment" => $comment, "shared_record_group" => "$shared", ); } &debug_cli(1,"add_mx n($host) p($pref) c($comment)"); if ( $comment ) { $obj->comment ( $comment ) } # views my ( $vname ) = $line =~/ view (\S+)/ ; if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } # set any values my @settings = $line =~/ set (\S+)/g ; if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # addit &do_func("add",$obj); } =head2 Remove An MX record from a zone conf zone foo.com del mx mail 10 mail.bar.com =cut sub cli_del_mx { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host , $pref , $dest ) = $line =~/ mx (\S+) (\S+) (\S+)/ ; my $name = fqdn( $host , $zone ) ; my %opthash = ( "object" => "Infoblox::DNS::Record::MX", "name" => $name, "pref" => $pref , "exchanger" => $dest , ); my ( $view ) = $line =~/ view (\S+)/ ; if ( $view ) { $opthash{'network_view'} = $view ; } my ( $obj ) = &do_func("get", %opthash ); &debug_cli(1,"del_mx n($host) p($pref) "); # addit if ( $obj ) { &do_func("remove",$obj); } } =head2 Add an SRV record to a zone conf zone foo.com add SRV conf zone foo.com add SRV _ldap._tcp 0 100 3268 dc01.foo.com To add the record to a shared record group (zone must be blank) conf zone "" add srv ... shared_record_group mygroup =cut # CBUIJS: Added add_srv sub cli_add_srv_rec { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $host , $priority , $weight , $port , $target ) = $line =~/ srv (\S+) (\S+) (\S+) (\S+) (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $shared ) = $line =~/shared_record_group (\S+)/ ; my ( $disable ) = $line =~/add srv \S+.* \b(disabled)\b/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; # scrub the host/zone for blanks my $name = fqdn ( $host , $zone ) ; my $obj = Infoblox::DNS::Record::SRV->new ( "name" => "$name", "port" => "$port", "priority" => "$priority", "target" => "$target", "weight" => "$weight", ); # overrwrite this for shared record groups if ( $shared ) { # version specific... require_api( "4.2r1-0" ) or return; $obj = Infoblox::DNS::SharedRecord::SRV->new ( "name" => "$name", "priority" => "$priority", "weight" => "$weight", "port" => "$port", "target" => "$target", ); } &debug_cli(1,"add_srv n($host) n($priority) n($weight) n($port)"); &debug_cli(1,"add_srv t($target) c($comment) sh($shared)"); if ( $disable ) { $obj->disable ( "TRUE" ) } if ( $view ) { my $view = Infoblox::DNS::View->new ( name => $view, ); $obj->views ( [ $view ] ) ; } if ( $comment ) { $obj->comment ( $comment ) } # set any values my @settings = $line =~/ set (\S+)/g ; if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # addit &do_func("add",$obj); } =head2 Add a bulk host to a zone conf zone foo.com add bulkhost my_prefix 1.2.3.10 1.2.3.20 conf zone foo.com add bulkhost my_prefix 1.2.3.10 1.2.3.20 addreverse conf zone foo.com add bulkhost my_prefix 1.2.3.10 1.2.3.20 view foo conf zone foo.com add bulkhost my_prefix 1.2.3.10 1.2.3.20 comment "bah" =cut sub cli_add_bulk { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $prefix , $fip , $lip ) = $line =~/ bulkhost (\S+) (\S+) (\S+)/ ; my ( $addrev ) = $line =~/ addreverse/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; my ( $vname ) = $line =~/ view (\S+)/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; &debug_cli(1,"addbh z($zone) p($prefix) n($fip,$lip) c($comment)"); my $obj = Infoblox::DNS::BulkHost->new ( "zone" => $zone , "prefix" => $prefix , "start_addr" => $fip , "end_addr" => $lip , "reverse" => "FALSE" ); if ( $comment ) { $obj->comment ( $comment ) } if ( $addrev ) { $obj->reverse ( "TRUE" ) } if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } # addit &do_func("add",$obj); } =head2 Remove a bulk host from a zone conf zone foo.com del bulkhost my_prefix 1.2.3.10 1.2.3.20 =cut sub cli_del_bulk { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $prefix , $fip , $lip ) = $line =~/ bulkhost (\S+) (\S+) (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"delbh z($zone) v($view) p($prefix) n($fip,$lip) "); # find and remove my %opthash = ( object => "Infoblox::DNS::BulkHost", zone => $zone, "prefix" => $prefix , "start_addr" => $fip , "end_addr" => $lip , ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a CNAME to a zone conf zone foo.com add CNAME alias real.foo.com =cut sub cli_add_cname { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $alias , $cname ) = $line =~/ cname (\S+) (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; my ( $ttl ) = $line =~/ ttl (\d+)/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; my $name = fqdn( $alias , $zone ) ; &debug_cli(1,"addcn z($zone) a($name) n($name) cn($cname) c($comment)"); my $obj = Infoblox::DNS::Record::CNAME->new ( "name" => "$name", "canonical" => "$cname", ); if ( $ttl ) { $obj->ttl ( $ttl ) } # views my ( $vname ) = $line =~/ view (\S+)/ ; if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } if ( $comment ) { $obj->comment ( $comment ) } # set any values my @settings = $line =~/ set (\S+)/g ; if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # addit &do_func("add",$obj); } =head2 Remove a CNAME from a zone conf zone foo.com delete CNAME alias =cut sub cli_del_cname { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $alias , $cname ) = $line =~/ cname (\S+) (\S+)/ ; my $name = fqdn( $alias , $zone ) ; my ( $view ) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"delcn n($name) z($zone) v($view) a($alias) cn($cname)"); # find and remove my %opthash = ( object => "Infoblox::DNS::Record::CNAME", # "name" => "$alias.$zone", "name" => "$name", "canonical" => "$cname", ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a TXT record to a zone conf zone foo.com add TXT alias real.foo.com When adding SPF refords or other things with quotes in them, you have to protect the quotes by using ('') instead of (") conf zone foo.com add TXT spf "''v=spf1'' ''+ip4:1.2.3.4''" =cut sub cli_add_txt { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $name , $txt ) = $line =~/ txt (\S+) (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; my ( $shared ) = $line =~/shared_record_group (\S+)/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; $txt = decode_comment( $txt ) ; $name = fqdn( $name , $zone ) ; # there is a 'bug/feature' in the api and individual words # get quotes indvidually. To stop this, you have to wrap # the string in single quotes # This example make it multiple strings: # text => 'v=spf1 mx ip4:193.15.4.5 ~all', # # This example makes it single string: # text => '"v=spf1 mx ip4:193.15.4.5 ~all"', # but some inputs WANT it quoted (arrg) so be smarter $txt = "\"$txt\"" unless $txt =~ /".*"/ ; &debug_cli(1,"addtxt z($zone) n($name) t($txt) c($comment)"); my $obj = Infoblox::DNS::Record::TXT->new ( "name" => "$name", "text" => "$txt", ); # overrwrite this for shared record groups if ( $shared ) { # version specific... require_api( "4.2r1-0" ) or return; $obj = Infoblox::DNS::SharedRecord::TXT->new ( "name" => "$name", "text" => "$txt", "shared_record_group" => "$shared", ); } # views my ( $vname ) = $line =~/ view (\S+)/ ; if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } if ( $comment ) { $obj->comment ( $comment ) } # addit &do_func("add",$obj); } =head2 Remove a TXT Record from a zone conf zone foo.com delete txt name =cut sub cli_del_txt { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $name , $txt ) = $line =~/ txt (\S+) (\S+)/ ; $name = fqdn( $name , $zone ) ; $txt = decode_comment( $txt ) ; my ( $view ) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"addtxt z($zone) v($view) n($name) t($txt)"); # find and remove my %opthash = ( object => "Infoblox::DNS::Record::TXT", "name" => "$name", "text" => "$txt", ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a PTR to a zone conf zone 10.0.0.0/24 add PTR 10.0.0.20 ns2.foo.com Add a PTR with comments conf zone 10.0.0.0/24 add PTR 10.0.0.20 ns2.foo.com comment "string" =cut sub cli_add_ptr { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $ip , $host ) = $line =~/ ptr_record (\S+) (\S+)/ ; my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; my ( $ttl ) = $line =~/ ttl (\d+)/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; &debug_cli(1,"addptr n($zone) ip($ip) h($host) c($comment)"); my $obj = Infoblox::DNS::Record::PTR->new ( "ipv4addr" => $ip , "ptrdname" => "$host", ); # views my ( $vname ) = $line =~/ view (\S+)/ ; if ( $vname ) { my $view = Infoblox::DNS::View->new ( name => $vname, ); $obj->views ( [ $view ] ) ; } if ( $comment ) { $obj->comment ( $comment ) } if ( $ttl ) { $obj->ttl ( $ttl ) } # addit &do_func("add",$obj); } =head2 Remove a PTR from a zone conf zone 10.0.0.0/24 del PTR 10.0.0.20 ns2.foo.com =cut sub cli_del_ptr { my ( $line ) = @_ ; # get the args from the line my ( $zone ) = $line =~/ zone (\S+)/ ; my ( $ip , $host ) = $line =~/ ptr_record (\S+) (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; $view = 'default' unless $view ; &debug_cli(1,"delptr n($zone) v($view) ip($ip) h($host)"); # find and remove my %opthash = ( object => "Infoblox::DNS::Record::PTR", ipv4addr => $ip, ptrdname => $host, ); $opthash{'view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head1 DHCP Configuration =head2 Add a network view conf net add view internal You can also modify existing views conf net modify view internal set external_ddns_primaries=1.1.1.1,2.2.2.2 =cut sub cli_add_network_view { my ( $line ) = @_ ; # get the args from the line my ( $type, $view ) = $line =~/ (add|modify) view (\S+)/ ; my ( $disable ) = $line =~/ (disabled)/ ; my @settings = $line =~/ set (\S+)/g ; # get and de-urlencode the comment string my ( $comment ) = $line =~/comment (\S+)/ ; $comment = decode_comment( $comment ) ; &debug_cli(1,"addnv v($view) c($comment)"); my $obj = Infoblox::DHCP::View->new ( "name" => "$view", ); # if we are in modify mode, get the view first... if ( $type eq "modify" ) { # this might become a subroutine my %opthash = ( "object" => "Infoblox::DHCP::View", "name" => $view ); my @views = &do_func("get", %opthash ); if ( $#views > 0 ) { # we found too many my $list = &dump_summ ( "name" , @views ); print " Error : $view : More than 1 views found : $list\n"; # exit here return } elsif ( ! @views ) { # we found too few print " Error : $view : No views found\n"; # exit here return } else { $obj = shift @views ; } } if ( $comment ) { $obj->comment ( $comment ) } if ( $disable ) { $obj->disable ( "TRUE" ) } if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # addit &do_func($type,$obj); } =head2 Add a network conf network add 1.1.1.0/24 Add a network witwith a reverse zone conf network add 1.1.1.0/24 addreverse Add a network to a network_view conf network add 1.1.1.0/24 view default Add a network with members conf network add 1.1.1.0/24 member 10.1.1.20 member 10.1.1.40 If no members are specified, the network will be added to the Grid master. If you don't want to assign any members use '0.0.0.0' conf network add 1.1.1.0/24 member 0.0.0.0 Add a network using a template conf network add 1.1.1.0/24 template MyTemplate Add a network with options conf network add 1.1.1.0/24 option 82="some data" Add a network with vendor class options (class.option) conf network add 1.1.1.0/24 option SunW.bootsvr="some data" Add a network with comments (comments must come last) conf network add 1.1.1.0/24 comment "comment string" Add a network with IPAM info conf network add 1.1.1.0/24 info ="" Add a network and set any additional parameters. (the setting must match a method in the API) conf network add 1.1.1.0/24 set conf network add 1.1.1.0/24 set enable_ddns=FALSE If the method requires an ARRAY you have to specify this in the value by putting the values onside '[]' : conf network ... set some-method="[10.216.2.8,10.204.3.162]" even if you only have a single value, you still need to pass the value as an array : conf network ... set some-method="[10.216.2.8]" =head2 Modifying Networks You cannot renumber a network, only modify the contents of it. conf network modify 1.1.1.0/24 set conf network modify 1.1.1.0/24 option 82="some data" You also can't change the network_view, but you will have to specify the view to find the network to modify conf network modify 1.1.1.0/24 view default If you change things like options arrays, ALL the existing values will be replaced by the new list. So if you want to just append a new option to an existing list, (or change one of the current options you can use 'addoption'. This will KEEP the current options array intact. conf network modify 1.1.1.0/24 addoption 82="some data" =cut # direct options : # [-] lease_time , domain_name , etc etc # let the serverside handle this or i will end up hardcoding keywords # in too many places =head2 Add a network container conf network add container 1.1.1.0/24 conf network modify container 1.1.1.0/24 comment "new comment" Network containers can only have a view, comment, Extensible Attributes, or be disabled. You probably only need this function when you want to add a container OVER some existing networks. In most other cases (with NIOS 5x) you can just add networks and the right thing will happen. =cut sub cli_add_network_container { my ( $line ) = @_ ; my ( $type , $j , $network ) = $line =~/ network (add|modify) (parent|container) (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $disable ) = $line =~/\b(disabled)\b/ ; my ( $comment ) = $line =~/comment (\S+)/ ; my @ipam = $line =~/ info (\S+)/g ; # members are not required for containers # cleanup some stuff $comment = decode_comment( $comment ) ; &debug_cli (1,"addnetc t($type) n($network) v($view) c($comment)"); my $net ; if ( $type eq "modify" ) { # get the current network my %opthash = ( "object" => "Infoblox::DHCP::NetworkContainer", "network" => $network, ); if ( $view ) { $opthash{'network_view'} = $view ; } ( $net ) = &do_func("get", %opthash ); # check errors if ( ! $net ) { print " network container $network not found\n"; return ; } } else { # create the object $net = Infoblox::DHCP::NetworkContainer->new ( "network" => $network , ); }; # comment if ( $comment ) { $net->comment( $comment ); } # disabled if ( $disable ) { $net->disable ( "TRUE" ) } # network view if ( $view ) { my $viewobj = Infoblox::DHCP::View->new("name"=>$view); $net->network_view ( $viewobj ) ; } # add ipam, EA stuff $net = add_ipam( $net , @ipam ) ; # add the container &do_func($type,$net); } sub cli_add_network { # essentially all options are passed as name value pairs # my ( $line ) = @_ ; # get the args from the line my ( $type , $network ) = $line =~/ network (add|modify) (\S+)/ ; # bug in 4.3r3, $2 needs to be reset to NULL... my ( $netmask ) = $line =~/ netmask (\S+)/ ; my ( $parent ) = $line =~/ parent (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $template ) = $line =~/ template (\S+)/ ; # my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; my ( $comment ) = $line =~/comment (\S+)/ ; my ( $addrev ) = $line =~/ (addreverse)/ ; my ( $disable ) = $line =~/\b(disabled)\b/ ; my @options = $line =~/ option (\S+)/g ; my @addoptions = $line =~/ addoption (\S+)/g ; my @settings = $line =~/ set (\S+)/g ; my @members = $line =~/ member (\S+)/g ; # de-urlencode the comment string $comment = decode_comment( $comment ) ; $view = decode_comment( $view ) ; &debug_cli (1,"addnet n($network) p($parent) m(@members) o(@options) c($comment)"); # create the network object : # coerce a netmask into a cidr if ( $netmask ) { my $cidr = mask_to_cidr ( $netmask ) ; $network = "$network/$cidr"; } # add/get the network my $net ; if ( $type eq "modify" ) { # get the current network my %opthash = ( "object" => "Infoblox::DHCP::Network", "network" => $network ); if ( $view ) { $opthash{'network_view'} = $view ; } ( $net ) = &do_func("get", %opthash ); # check errors if ( ! $net ) { print " network $network not found\n"; return ; } } else { # create the object $net = Infoblox::DHCP::Network->new ( "network" => $network , # "network_container" => $parent , "auto_create_reversezone" => "FALSE" ); } if ( $comment ) { $net->comment( $comment ); } # parenting ? # fixed in 4.1r3 if ( $parent ) { $net->network_container( $parent ); } # templated ? if ( $template ) { $net->template( $template ); # If we are using templates, we have a different syntax my ( $ip , $cidr ) = split ( /\// , $network ); my $mask = cidr_to_mask ( $cidr ); $net->ipv4addr( $ip ); $net->netmask( $mask ); } # auto create reverses ? if ( $addrev ) { $net->auto_create_reversezone ( "TRUE" ); } # disabled if ( $disable ) { $net->disable ( "TRUE" ) } # views if ( $view ) { my $viewobj = Infoblox::DHCP::View->new("name"=>$view); $net->network_view ( $viewobj ) ; } # members : if ( @members ) { my @memsvrs ; foreach my $memval ( @members ) { # special hack for NO members next if ($memval =~ /0.0.0.0/); # split on ',' get all the IPs &debug_cli(2,"addnet mem($memval)"); push @memsvrs, Infoblox::DHCP::Member->new("ipv4addr"=>$memval); } # and add them to the object $net->members ( \@memsvrs ) ; } elsif ( $MASTER_IP and $type eq "add" ) { # just add the grid master by default to NEW networks &debug_cli(2,"addnet mem($MASTER_IP)"); my $DHMASTER = Infoblox::DHCP::Member->new ('ipv4addr' => $MASTER_IP); # my @memsvrs = ( $DHMASTER ) ; # $net->members ( \@memsvrs ) ; $net->members ( [ $DHMASTER ] ) ; } # options # my @options = $line =~/ option (\S+)/g ; if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the '=' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addnet o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; # only add options we recognise if ( $opt_obj ) { push @opt_list , $opt_obj ; } } $net->options ( \@opt_list ) ; } # This should be a subroutine $net = add_options( $net , @addoptions ) ; # now add IPAM info my @ipam = $line =~/ info (\S+)/g ; $net = add_ipam( $net , @ipam ) ; # settings - these are additional fields that the API supports if ( @settings ) { $net = add_settings ( $net , @settings ) } # add the network &do_func($type,$net); } sub add_options { my ( $obj , @options ) = @_ ; # don't do things unless we have to... unless ( @options ) { return $obj } # add or replace DHCP options on this object # i have to MERGE to arrays that have no key or index, Grr. # this is actually quite painful. It is easier # to create a hashed index and swap out some things # then write a new array... # 1) get the CURRENT options. # (BTW, you can do thin in 1 line with map) my %opt_hash ; foreach my $opt ( @{ $obj->options() } ) { my $name = $opt->name(); # save it $opt_hash{$name} = $opt ; } # 2) walk the list we sent and insert the new values to the hash # possibly overrwriting existing values (hence the HASH) foreach my $optval ( @options ) { # split on the '=' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; # turn this into an option object my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; &debug_cli(2,"addopt o($opt) v($val) k($key)"); # only add the option to the hash, if we found a match if ( $opt_obj ) { $opt_hash{$key} = $opt_obj ; } } # 3) turn the hash into an array # ( [ ] can we do this with 'values()' ?? ) my @new_list ; foreach my $okey ( keys %opt_hash ) { push @new_list , $opt_hash{$okey} ; } # 4) and save on the object $obj->options( \@new_list ) ; return $obj ; } =head2 Remove a network conf network del 1.1.1.0/24 =cut sub cli_delete_network { my ( $line ) = @_ ; # get the args from the line my ( $network ) = $line =~/ network delete (\S+)/ ; my ( $netmask ) = $line =~/ netmask (\S+)/ ; # coerce a netmask into a cidr if ( $netmask ) { my $cidr = mask_to_cidr ( $netmask ) ; $network = "$network/$cidr"; } my ( $view ) = $line =~/ view (\S+)/ ; # $view = 'default' unless $view ; &debug_cli(1,"delnet n($network) v($view)"); # find and remove print "Remove: Infoblox::DHCP::Network\n"; my %opthash = ( object => "Infoblox::DHCP::Network", network => $network, ); $opthash{'network_view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # see if it was a container instead unless ( $obj ) { print "Remove: Infoblox::DHCP::NetworkContainer\n"; $opthash{'object'} = "Infoblox::DHCP::NetworkContainer"; ( $obj ) = &do_func("get",%opthash); } # trashit &do_func("remove",$obj) if $obj; } =head2 Remove a network Template conf template del network my_template =cut sub cli_del_network_template { my ( $line ) = @_ ; my ( $name ) = $line =~/ delete network (\S+)/ ; &debug_cli(2,"delnett n($name)"); # find and remove my %opthash = ( object => "Infoblox::DHCP::NetworkTemplate", name => $name, ); my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Move Networks Move a network to a different member, or move a network onto members to match a failover association. The ranges inside the network will also be moved. Moving a network to a single member : configure network move 45.0.0.0/24 member 2.2.2.2 Moving a network to a multiple members : configure network move 45.0.0.0/24 member 2.2.2.2 member 3.3.3.3 Moving a network to use DHCP failover : configure network move 45.0.0.0/24 failover box1-boxb =cut sub cli_move_network { # move a network from 1 member to another # or move a network to support failover associations # # also move the ranges inside the network my ( $line ) = @_ ; # get the args from the line my ( $network ) = $line =~/ network move (\S+)/ ; my ( $failover ) = $line =~/ failover (\S+)/ ; my @members = $line =~/ member (\S+)/g ; $failover = decode_comment( $failover ) ; &debug_cli(1,"movnet n($network) f($failover) m(@members)"); unless ( $failover or @members ) { print " Error : member or failover is required\n"; return ; } # get the current network my %opthash = ( "object" => "Infoblox::DHCP::Network", "network" => $network ); my @nets = &do_func("get", %opthash ); my $nobj ; # check errors # get the current network : if ( $#nets > 0 ) { # we found too many my $list = &dump_summ ( "network" , @nets ); print " Error : $network : More than 1 network found : $list\n"; # exit here return } elsif ( ! @nets ) { print " network $network not found\n"; return ; } else { $nobj = shift @nets ; } # get the failover members my @fmembers ; if ( $failover ) { my ( $fobj ) = &do_func("get", ( "object" => "Infoblox::DHCP::FailOver", "name" => $failover, ) ); if ( $fobj ) { push @fmembers , $fobj->primary->ipv4addr(); push @fmembers , $fobj->secondary->ipv4addr(); } else { print " Error : failover assoc $failover not found\n"; exit ; } &debug_cli(2,"movnet f/o m(@fmembers)"); } # get all the ranges, unset their membership my @ranges = &do_func("search", ( "object" => "Infoblox::DHCP::Range", "network" => &escape_dots($network), "start_addr" => ".*", ) ) ; foreach my $robj ( @ranges ) { my $fip = $robj->start_addr(); print " clear range $fip membership\n"; # remove failover and/or membership $robj->member ( undef ); $robj->failover_assoc ( undef ); my ( $res ) = &do_func("modify", $robj); } # set the member for the ranges my $rmem = $members[0] ; my $rmemobj = Infoblox::DHCP::Member->new("ipv4addr"=>$rmem); # if we are using failover, we set the membershiplist # to the failover list if ( $failover ) { @members = @fmembers ; $rmem = $failover ; } # move the network to the new members if ( @members ) { my @memsvrs ; foreach my $memval ( @members ) { # special hack for NO members next if ($memval =~ /0.0.0.0/); # split on ',' get all the IPs &debug_cli(2,"addnet mem($memval)"); push @memsvrs, Infoblox::DHCP::Member->new("ipv4addr"=>$memval); } $nobj->members ( \@memsvrs ) ; # and add them to the object print " set network $network membership @members\n"; my ( $res ) = &do_func("modify", $nobj); # set the ranges to the new members unless ( $rmem =~ /0.0.0.0/ ) { # walk each range foreach my $robj ( @ranges ) { my $fip = $robj->start_addr(); print " set range $fip membership $rmem\n"; if ( $failover ) { $robj->failover_assoc ( $rmem ); } else { $robj->member ( $rmemobj ); } # and save it my ( $res ) = &do_func("modify", $robj); } } } } =head2 Join Networks ** This feature will soon change, use with caution ** conf network 1.1.1.0/23 join /23 net 1.1.2.0/24 # this will copy all the ranges etc from the network # and create a new network =cut sub cli_join_network { # get the line my ( $line ) = @_ ; # get the args from the line my ( $cidr ) = $line =~/ join \/(\S+)/ ; my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; my @subnets = $line =~/ network (\S+)/g ; # de-urlencode the comment string $comment = decode_comment( $comment ) ; # work out the new network name my $basenet = $subnets[0]; my $newnet = $basenet ; $newnet =~ s/\/\d+/\/$cidr/; &debug_cli (1,"joinnet new($newnet) j($cidr) m(@subnets)"); my @orig_ranges ; my @orig_fixed ; foreach my $snet ( @subnets ) { # get all the ranges my %opthash = ( "object" => "Infoblox::DHCP::Range", "network" => $snet, "start_addr" => ".*", ); # get what we need my @robjs = &do_func("search", %opthash ); push @orig_fixed , @robjs ; # get all the fixed addresses my %fopthash = ( "object" => "Infoblox::DHCP::FixedAddr", "network" => $snet, "ipv4addr" => ".*", ); # get what we need my @fobjs = &do_func("search", %fopthash ); push @orig_fixed , @fobjs ; } # make a copy of all the ranges and fixed addrs my @newobjs = ( @orig_ranges , @orig_fixed ) ; foreach my $robj ( @newobjs ) { # change the parent network for the ranges etc $robj->network ( $newnet ) ; # remove the gateway address from all range options # remove the gateway address from all fixed options my $options = $robj->options(); my @new_options ; foreach my $oobj ( @{ $options } ) { unless ( $oobj->type() eq "routers" ) { push @new_options , $oobj; } } # replace the options if ( @new_options ) { $robj->options( \@new_options ); } else { $robj->options( undef ); } } # get the current first network my %opthash = ( "object" => "Infoblox::DHCP::Network", "network" => $basenet, ); my ( $nobj ) = &do_func("get", %opthash ); # modify it with the new subnet mask if ( $nobj ) { $nobj->network ( $newnet ) ; } # get the current membership; my $memsvrs = $nobj->members(); # remove the child networks # remove the joined network foreach my $snet ( @subnets ) { my $net = Infoblox::DHCP::Network->new ( "network" => $snet , ); &do_func("remove",$net); } # add the joined network # if it fails, add add in the old networks etc.. my $net = Infoblox::DHCP::Network->new ( "network" => $newnet , ); $net->members ( $memsvrs ) ; &do_func("add",$net); # add the ranges # add the fixed addresses foreach my $obj ( @newobjs ) { &do_func("add",$obj); } } =head2 Add a shared network conf network add shared my_shared child_network 1.1.1.0/24 child 1.1.2.0/24 Add a shared network with options conf network add shared foo option 82="some data" Add a shared network with comments conf network add shared my_shared ... comment "comment string" =cut # direct options : # [-] lease_time , domain_name , etc etc # let the serverside handle this or i will end up hardcoding keywords # in too many places sub cli_add_shared { # essentially all options are passed as name value pairs # my ( $line ) = @_ ; # get the args from the line # the shared network name could be quoted and have spaces # what we have to do here is some nested matches from $REcomm $line =~/ add shared ($REcomm)/ ; my $name = $+ ; # de-urlencode the name string $name = decode_comment( $name ) ; $line =~/ comment "?([^"]+)"?/ ; my $comment = $+ ; # de-urlencode the comment string $comment = decode_comment( $comment ) ; my @children = $line =~/ child_network (\S+)/g ; my @options = $line =~/ option (\S+)/g ; # members are assigned at the network level # my @members = $line =~/ member (\S+)/g ; &debug_cli (1,"addsha s($name) n(@children) o(@options) c($comment)"); # children : my @netlist ; if ( @children ) { foreach my $net ( @children ) { # split on ',' get all the IPs &debug_cli(2,"addsha chi($net)"); push @netlist, Infoblox::DHCP::Network->new("network"=>$net); } # and add them to the object # $net->networks ( \@netlist ) ; } # create the network object : my $net = Infoblox::DHCP::SharedNetwork->new ( "name" => $name , "comment" => $comment , "networks" => \@netlist , ); # options if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the '=' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addnet o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; # only add options we recognise if ( $opt_obj ) { push @opt_list , $opt_obj ; } } $net->options ( \@opt_list ) ; } # add the network &do_func("add",$net); } =head2 Remove a shared network conf network del shared my_shared =cut sub cli_delete_shared_network { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ delete shared (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; # $view = 'default' unless $view ; &debug_cli(1,"delsha n($name) v($view)"); my %opthash = ( object => "Infoblox::DHCP::SharedNetwork", name => $name, ); $opthash{'network_view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Split a network You need to specify the netmaks of the children networks when you are doing a split. So to split a /16 into /20 networks : conf network 1.1.0.0/16 split /24 Split a network and add all children conf network 1.1.0.0/16 split /24 all =cut sub cli_split_network { # # splitting either creates ALL networks or only the ones # we have to have (which could be none) my ( $line ) = @_ ; # get the args from the line my ( $network ) = $line =~/ network (\S+)/ ; my ( $ncidr ) = $line =~/ split \/(\S+)/ ; my ( $type ) = $line =~/ split \/\S+ (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; &debug_cli(1,"splitnet n($network) s($ncidr) t($type)"); # create the split object my $obj = Infoblox::DHCP::SplitNetwork->new ( "network" => $network , "prefix" => $ncidr , ); # views if ( $view ) { my $viewobj = Infoblox::DHCP::View->new("name"=>$view); $obj->network_view ( $viewobj ) ; } # check the type if ( $type eq "required" ) { $obj->add_all_subnetworks ( "false" ) ; } if ( $type eq "all" ) { $obj->add_all_subnetworks ( "true" ) ; } &do_func("add",$obj); } =head2 Adding network to parents When you split a network and want to add children you just add them to the parent network. All other options are the same as for adding networks conf network 1.1.0.0/16 add 1.1.4.0/22 =cut sub cli_add_sub_network { my ( $line ) = @_ ; # we hack this by change the arg list to the correct syntax # and just calling cli_add_network my ( $par , $sub ) = $line =~/ network (\S+) add network (\S+)/ ; $line =~ s/ network $par add network $sub/ network add $sub parent $par/; &debug_cli(2,"addsn pn($par) cn($sub)"); &debug_cli(2,"addsn l[$line]"); &cli_add_network ( $line ) ; } =head2 Add a failover association conf net add failover AtoB primary 45.0.12.20 secondary 45.0.128.30 =cut sub cli_add_failover { # essentially all options are passed as name value pairs # my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/add failover (\S+)/ ; my ( $primary ) = $line =~/ primary (\S+)/ ; my ( $secondary ) = $line =~/ secondary (\S+)/ ; my @settings = $line =~/ set (\S+)/g ; &debug_cli (1,"addfail n($name) p($primary) s($secondary)"); # create the members my $pobj = Infoblox::DHCP::Member->new("ipv4addr"=>$primary); my $sobj = Infoblox::DHCP::Member->new("ipv4addr"=>$secondary); # create the object my $fobj = Infoblox::DHCP::FailOver->new ( "name" => $name , "primary" => $pobj , "secondary" => $sobj , ); # settings - these are additional fields that the API supports if ( @settings ) { $fobj = add_settings ( $fobj , @settings ) } # add the network &do_func("add",$fobj); } =head2 Add a fixed address conf network 10.0.1.0/24 add fixed 10.0.1.4 aa:bb:cc:11:22:33 The 'network' is actually optional, this syntax also works : conf network add fixed 10.0.1.4 aa:bb:cc:11:22:33 To add the fixed addr with specific options conf network add fixed ... option = [opt =value>] conf network add fixed 10.0.1.4 .. option 82="some data" To add the fixed addr to a specific view conf network add fixed 10.0.1.4 aa:bb:cc:11:22:33 view default Add a fixed address with a Fixed Address Template conf network 10.0.1.0/24 add fixed 10.0.1.4 aa:bb:cc:11:22:33 template mytemp Add a fixed address with comments conf net 10.0.1.0/24 add fixed 10.0.1042 aa:bb:cc:11:22:33 comment "comment string" =head2 Add a roaming address (that has no IP addr) You will need to specify the name instead of the IP address conf network add fixed myroamer aa:bb:cc:11:22:33 =head2 Modify a fixed address You can also modify existing addresses with the same syntax conf network 10.0.1.0/24 modify fixed 10.0.1.4 aa:bb:cc:11:22:33 ... You also can't change the network_view, but you will have to specify the view to find the range to modify conf network modify fixed 10.0.1.4 view default =cut sub cli_add_fixed_address { my ( $line ) = @_ ; my ( $network ) = $line =~/configure network ($REcidr)/ ; my ( $type , $ip , $mac ) = $line =~/ (add|modify) fixed (\S+) (\S+)/ ; my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; my ( $template ) = $line =~/ template "?([^"]+)"?/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $name ) = $line =~/ name (\S+)/ ; my @settings = $line =~/ set (\S+)/g ; my @options = $line =~/ option (\S+)/g ; # de-urlencode some strings $comment = decode_comment( $comment ) ; $name = decode_comment( $name ) ; $view = decode_comment( $view ) ; # fix mac addresses that are missing ':' unless ( $mac =~ /:/ ) { $mac =~ s/(..)/$1:/g; $mac =~ s/:$//; } &debug_cli(1,"addfix n($network) ip($ip) mac($mac) o(@options) c($comment)"); my $obj ; # get or add ? if ( $type eq "modify" ) { # do we have a valid IP address, or are we a roaming host ? my %opthash = ( "object" => "Infoblox::DHCP::FixedAddr", ); if ( $mac ) { $opthash{'mac'} = $mac ; } if ( $ip =~ /^$REip$/ ) { $opthash{'ipv4addr'} = $ip ; } else { # roaming host? # $opthash{'roaming_host_name'} = $ip ; $opthash{'name'} = $ip ; $opthash{'object'} = "Infoblox::DHCP::RoamingHost"; } if ( $view ) { $opthash{'network_view'} = $view ; } ( $obj ) = &do_func("get", %opthash ) ; # ( $obj ) = &do_func("get", # ( # "object" => "Infoblox::DHCP::FixedAddr", # "ipv4addr" => $ip , # "mac" => $mac , # ) # ) ; unless ( $obj ) { print " Error : $ip : No fixed addr found\n"; return ; } } else { # roamer or not ? if ( $ip =~ /^$REip$/ ) { $obj = Infoblox::DHCP::FixedAddr->new ( # "network" => $network , "ipv4addr" => $ip , "mac" => $mac , ); } else { # $obj = Infoblox::DHCP::FixedAddr->new ( # "dynamic" => "true", # "network" => $network , # "roaming_host_name" => $ip , # "mac" => $mac , # ); $obj = Infoblox::DHCP::RoamingHost->new ( # "dynamic" => "true", # "network" => $network , "name" => $ip , "mac" => $mac , ); } } # hack around adding fixed addrs with out a parent network... if ( $network ) { unless ( $network eq "0.0.0.0/00" ) { $obj->network ( $network ) ; } } if ( $name ) { $obj->name ( $name ) } if ( $comment ) { $obj->comment ( $comment ) } if ( $template ) { $obj->template ( $template ) } # network view if ( $view ) { my $viewobj = Infoblox::DHCP::View->new("name"=>$view); $obj->network_view ( $viewobj ) ; } # options if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the ',' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addfix o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; # only add options we recognise if ( $opt_obj ) { &debug_cli(2,"goodfix o($opt) v($val)"); push @opt_list , $opt_obj ; } # and some special cases... if ( $opt =~ /next.*server/ ) { $obj->nextserver( decode_comment($val) ) ; } } $obj->options ( \@opt_list ) ; } # now add IPAM info my @ipam = $line =~/ info (\S+)/g ; $obj = add_ipam( $obj , @ipam ) ; # settings - these are additional fields that the API supports if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # print Dumper ( $obj ) ; # addit &do_func($type,$obj); } =head2 Add a fixed address Template conf template add fixed offset ... conf template add fixed router-a offset 1 Add a fixed address with comments conf template add fixed ... comment "use this for routers" =head2 Modify a fixed address You can also modify existing addresses with the same syntax conf template modify fixed ... =cut sub cli_add_fixed_template { my ( $line ) = @_ ; my ( $type , $name ) = $line =~/ (add|modify) fixed (\S+)/ ; my ( $comment ) = $line =~/ comment (\S+)/ ; my ( $offset ) = $line =~/ offset (\S+)/; my @settings = $line =~/ set (\S+)/g ; my @options = $line =~/ option (\S+)/g ; # de-urlencode the name string $comment = decode_comment( $comment ) ; &debug_cli(1,"addfixt n($name) of($offset) o(@options) c($comment)"); my $obj ; # get or add ? if ( $type eq "modify" ) { # do we have a valid template my %opthash = ( "object" => "Infoblox::DHCP::FixedAddrTemplate", "name" => $name , ); ( $obj ) = &do_func("get", %opthash ) ; unless ( $obj ) { print " Error : $name : No fixed template found\n"; return ; } } else { $obj = Infoblox::DHCP::FixedAddrTemplate->new ( "name" => $name , number_of_addresses => 1, ); } if ( $comment ) { $obj->comment ( $comment ) } if ( $offset ) { $obj->offset ( $offset ) } # options if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the ',' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addfix o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; # only add options we recognise if ( $opt_obj ) { push @opt_list , $opt_obj ; } } $obj->options ( \@opt_list ) ; } # now add IPAM info my @ipam = $line =~/ info (\S+)/g ; $obj = add_ipam( $obj , @ipam ) ; # settings - these are additional fields that the API supports if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # addit &do_func($type,$obj); } =head2 Remove a fixed address For some clever reason, you don't really need to specify the parent network to remove a fixed address, but we support it here to keep people sane (In reality, any bogus string for the network name will work) conf network 10.0.1.0 delete fixed 1.2.3.4 or conf network delete fixed 1.2.3.4 =cut sub cli_del_fixed_address { my ( $line ) = @_ ; my ( $network ) = $line =~/configure network ($REcidr)/ ; my ( $ip ) = $line =~/ fixed (\S+)/ ; my ( $mac ) = $line =~/ fixed \S+ (\S+)/ ; unless ( $ip =~ /$REip/ ) { print " invalid IP address\n"; return; } # fix mac addresses that are missing ':' unless ( $mac =~ /:/ ) { $mac =~ s/(..)/$1:/g; $mac =~ s/:$//; } &debug_cli(2,"delfix ip($ip) mac($mac)"); # use get() instead of new, because it is safer my %opthash = ( "object" => "Infoblox::DHCP::FixedAddr", "ipv4addr" => $ip, ); # network views... my ( $view ) = $line =~/ view (\S+)/ ; $view = decode_comment( $view ) ; if ( $view ) { $opthash{'network_view'} = $view ; } # get the faddr, (which also reports an error if not found) my ( $obj ) = &do_func("get", %opthash ); # trash it if ( $obj ) { &do_func("remove",$obj); } } sub cli_del_fixed_template { my ( $line ) = @_ ; my ( $name ) = $line =~/ delete fixed (\S+)/ ; &debug_cli(2,"delfixt n($name)"); my %opthash = ( object => "Infoblox::DHCP::FixedAddrTemplate", name => $name, ); my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a dhcp range to a network Dhcp ranges, by default, will get added to the Grid master unless you override that setting with the 'member ' syntax conf network add range 10.1.1.20 10.1.1.40 Add a range to a network view conf network add range 10.1.1.20 10.1.1.40 view default Failover associations are an additional argument conf net add range 10... failover "my_peering" As are adding the member conf net add range 10... member 1.1.1.2 If no members are specified, the range will be added to the Grid master. If you don't want to assign any members use '0.0.0.0' conf net add range 10... member 0.0.0.0 You can also add exclusions conf net add range 10... exclude 1.1.1.2,1.1.2.5 You can also add filters to a range (multiples are allowed) conf net add range 10... macfilter = And add new filters to an existing range, you only have to specify start addr conf net mod range 10.1.1.20 macfilter = conf net add range 10.1.1.20 10.1.1.40 filter_option = =head2 Modifying Ranges You cannot renumber a range, only modify the contents of it. conf network modify range 10.. set conf network modify range 10.. option 82="some data" You also can't change the network_view, but you will have to specify the view to find the range to modify conf network modify range 10.. view default ... If you change things like options arrays, ALL the existing values will be replaced by the new list. 'modify' does not append to existing lists, it does a complete replacement of it. =cut sub cli_add_dhcp_range { # essentially all options are passed as name value pairs # my ( $line ) = @_ ; # get the args from the line, note that endip is only required for add my ( $type, $fip, $lip ); ( $type , $fip , $lip ) = $line =~/ network (add) range (\S+) (\S+)/ ; ( $type , $fip ) = $line =~/ network (modify) range (\S+)/ unless $type; my ( $failover ) = $line =~/ failover (\S+)/ ; my ( $member ) = $line =~/ member (\S+)/ ; my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; my ( $view ) = $line =~/ view (\S+)/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; $view = decode_comment( $view ) ; # arrays my @exclude = $line =~/ exclude (\S+)/g ; my @options = $line =~/ option (\S+)/g ; my @macfilters = $line =~/ macfilter (\S+)/g ; my @optfilters = $line =~/ filter_option (\S+)/g ; &debug_cli (1,"addrng s($fip) e($lip) o(@options)"); &debug_cli (1,"addrng m($member) c($comment) f($failover)"); &debug_cli (1,"addrng e(@exclude)"); my $obj ; if ( $type eq "modify" ) { # get the current network my %opthash = ( "object" => "Infoblox::DHCP::Range", "start_addr" => $fip , ); if ( $view ) { $opthash{'network_view'} = $view ; } ( $obj ) = &do_func("get", %opthash ); # check errors if ( ! $obj ) { print " range with startip $fip not found\n"; return ; } } else { # create the object $obj = Infoblox::DHCP::Range->new ( "start_addr" => $fip , "end_addr" => $lip , # "comment" => $comment , # "options" => \@opt_list , # "member" => $member , ); } # views if ( $view ) { my $viewobj = Infoblox::DHCP::View->new("name"=>$view); $obj->network_view ( $viewobj ) ; } # add the optional extras if ( $comment ) { $obj->comment ( $comment ) } # disabled my ( $disable ) = $line =~/\b(disabled)\b/ ; if ( $disable ) { $obj->disable ( "TRUE" ) } # now add IPAM info my @ipam = $line =~/ info (\S+)/g ; $obj = add_ipam( $obj , @ipam ) ; # you can't assign failover and members, you have to choose one. if ( $failover ) { # support spaces in the failover name $failover = decode_comment( $failover ) ; $obj->failover_assoc ( $failover ) } else { # find a member to put this range on if ( $member ) { if ( $member !~ /0.0.0.0/ ) { # not the grid master &debug_cli(2,"addrng mem($member)"); my $memobj = Infoblox::DHCP::Member->new("ipv4addr"=>$member); $obj->member ( $memobj ) ; } } elsif ( $MASTER_IP ) { &debug_cli(2,"addrng mem($MASTER_IP)"); my $DHMASTER = Infoblox::DHCP::Member->new ('ipv4addr'=>$MASTER_IP); $obj->member ( $DHMASTER ) ; } } # exclusions if ( @exclude ) { my @exc_list ; foreach my $excval ( @exclude ) { # set some exclusions; # split on the ',' my ( $efip , $elip ) = $excval =~ /^(.*?),(.*)/ ; &debug_cli(2,"addrng e($efip) v($elip)"); my $exc_obj = Infoblox::DHCP::ExclusionRange->new( start_address => $efip, end_address => $elip, ); push @exc_list , $exc_obj ; } $obj->exclude ( \@exc_list ) ; } # filters if ( @macfilters ) { my @filters ; # get the current list if ( $type eq 'modify' ) { @filters = @{ $obj->filters() } } foreach my $filter ( @macfilters ) { # split on the '=' my ( $fname , $perm ) = $filter =~ /^(.*?)=(.*)/ ; $fname = decode_comment( $fname ) ; if ( $perm =~ /allow/i ) { $perm = "grant" } &debug_cli(2,"addfil f($fname) p($perm)"); my $fil_obj = Infoblox::DHCP::FilterRule::MAC->new( filter_name => $fname, permission => $perm, ); push @filters , $fil_obj ; } $obj->filters ( \@filters ) ; } # optfilters - SBE quick & dirty copy/paste of the mac filter # current behavior with modify is to append filter to existing ones # future enhancement could be to have a replace & a modify if ( @optfilters ) { my @ofilters ; # get the current list if ( $type eq 'modify' ) { @ofilters = @{ $obj->filters() } } foreach my $filter ( @optfilters ) { # split on the '=' my ( $fname , $perm ) = $filter =~ /^(.*?)=(.*)/ ; if ( $perm =~ /allow/i ) { $perm = "grant" } &debug_cli(2,"addfil f($fname) p($perm)"); my $ofil_obj = Infoblox::DHCP::FilterRule::Option->new( filter_name => $fname, permission => $perm, ); push @ofilters , $ofil_obj ; } $obj->filters ( \@ofilters ) ; } # options if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the '=' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addrng o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; push @opt_list , $opt_obj ; } $obj->options ( \@opt_list ) ; } # settings - these are additional fields that the API supports my @settings = $line =~/ set (\S+)/g ; if ( @settings ) { $obj = add_settings ( $obj , @settings ) } # add the range &do_func($type,$obj); } =head2 Remove a dhcp range conf net delete range 1.1.1.20 1.1.1.40 =cut sub cli_del_dhcp_range { my ( $line ) = @_ ; # get the args from the line my ( $fip, $lip ) = $line =~/ network delete range (\S+) (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; $view = decode_comment( $view ) ; $view = 'default' unless $view ; &debug_cli (1,"delrng v($view) s($fip) v($view)"); my %opthash = ( object => "Infoblox::DHCP::Range", "start_addr" => $fip , "end_addr" => $lip , ); $opthash{'network_view'} = $view if $view ; my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a DHCP filter Filters can take many forms, so you need to be specific conf network add macfilter my_filter =cut sub cli_add_macfilter { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ macfilter (\S+)/ ; $name = decode_comment( $name ) ; &debug_cli (1,"addmfil n($name)"); #[ ] what about vendor prefix ? my $obj = Infoblox::DHCP::Filter::MAC->new ( "name" => $name , ); &do_func("add",$obj); } =head2 Remove a DHCP filter conf network del macfilter my_filter =cut sub cli_del_macfilter { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ macfilter (\S+)/ ; &debug_cli (1,"delmfil n($name)"); #[ ] what about vendor prefix ? my %opthash = ( object => "Infoblox::DHCP::Filter::MAC", "name" => $name , ); my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } =head2 Add a mac address to a filter conf network filter my_filter add macaddress aa:bb:cc:11:22:33 Add a mac address to a filter with comments conf network filter my_filter add mac aa:bb:cc:11:22:33 comment "my comment" Modify a filter entry conf network filter my_filter modify mac aa:bb:cc:11:22:33 comment "my comment" =cut sub cli_add_macfilteraddr { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ filter (\S+)/ ; my ( $type, $mac ) = $line =~/ (add|modify) macaddress (\S+)/ ; my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; $name = decode_comment( $name ) ; # fix mac addresses that are missing ':' unless ( $mac =~ /:/ ) { $mac =~ s/(..)/$1:/g; $mac =~ s/:$//; } &debug_cli (1,"addmaddr f($name) m($mac) c($comment)"); # get or add ? my $obj ; if ( $type eq 'modify' ) { # get the current network my %opthash = ( "object" => "Infoblox::DHCP::MAC", "filter" => $name , "mac" => $mac , ); ( $obj ) = &do_func("get", %opthash ); # check errors if ( ! $obj ) { print " mac $mac is not in the filter $name\n"; return ; } } else { $obj = Infoblox::DHCP::MAC->new ( "filter" => $name , "mac" => $mac , # "comment" => $comment , ); } if ( $comment ) { $obj->comment ( $comment ) } # make the change &do_func($type,$obj); } =head2 remove a mac address from a filter conf network filter my_filter delete macaddress aa:bb:cc:11:22:33 =cut sub cli_del_macfilteraddr { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ filter (\S+)/ ; my ( $mac ) = $line =~/ macaddress (\S+)/ ; # fix mac addresses that are missing ':' unless ( $mac =~ /:/ ) { $mac =~ s/(..)/$1:/g; $mac =~ s/:$//; } &debug_cli (1,"delmaddr f($name) m($mac)"); my %opthash = ( object => "Infoblox::DHCP::MAC", "filter" => $name , "mac" => $mac , ); my ( $obj ) = &do_func("get",%opthash); # trashit &do_func("remove",$obj) if $obj; } # IPAM and metadata functions =head1 IPAM Configuration =head2 Show information about an IP address show ipam address 1.2.3.4 =cut sub cli_show_ipam_address { my ( $line ) = @_ ; my ( $ip ) = $line =~/ address (\S+)/ ; # get address my ( $tobj ) = &do_func("get" , "object" => "Infoblox::IPAM::Address", address => $ip , ); if ( $tobj ) { dump_object( $tobj ) ; } else { print "No IP found\n"; } } =head2 Add a network to a discovery job Each network gets added to the current discovery job configure ipam discovery add network 1.2.3.0/24 And you also need to set the discovery member (you can only do this once, each member will overwrite the last one) configure ipam discovery add member 192.168.1.2 =cut sub cli_add_discovery { my ( $line ) = @_ ; # get the args from the line my ( $net ) = $line =~/ add network (\S+)/ ; my ( $mem ) = $line =~/ add member (\S+)/ ; # get the current discovery task my ( $dobj ) = &do_func("get" , "object" => "Infoblox::IPAM::DiscoveryTask", ); unless ( $dobj ) { print " Error : No Discovery task found\n"; return ; } # check if a task is already running if ( $dobj->status() =~ /In Progress|Paused/i ) { print " Error : a job is already running\n"; return ; } # set up some config options if ( $mem ) { # set the disco member my $mobj = Infoblox::DHCP::Member->new("ipv4addr"=>$mem); if ( $mobj ) { # bad member assignments are ignored $dobj->member( $mobj ) ; } } if ( $net ) { # create the network opject handle my $nobj = Infoblox::DHCP::Network->new ( "network" => $net ); # append the subnet my @networks = @{ $dobj->networks() } ; push @networks , $nobj ; $dobj->networks( \@networks ) ; } # now save the change &do_func("modify",$dobj); } =head2 Show a discovery job show ipam discovery =cut sub cli_show_discovery { my ( $line ) = @_ ; # # get the args from the line # my ( $net ) = $line =~/ add network (\S+)/ ; # get the current discovery task my ( $tobj ) = &do_func("get" , "object" => "Infoblox::IPAM::DiscoveryTask", ); if ( $tobj ) { dump_object( $tobj ) ; } else { print "No Discovery task found\n"; } } =head2 Adding custom metadata fields Add a device type configure grid add device_type Add a device type with new custom labels configure grid add device_type label = Add a device type with multiple custom labels ... device_type label = label = For 4.3 or later you have to add and define Extensible attributes configure grid add attribute Set Values and type configure grid add attribute value [ value type create a list of elements configure grid add attribute type list value one value two Set the attribute to be a multiple or a required item configure grid add attribute required configure grid add attribute multiple =cut sub cli_add_attribute { my ( $line ) = @_ ; # get the args from the line my ( $attr ) = $line =~/ attribute (\S+)/ ; my ( $type ) = $line =~/ type (\S+)/ ; my ( $required ) = $line =~/ required\b/ ; my ( $multi ) = $line =~/ multiple\b/ ; my @values = $line =~/ value (\S+)/g ; # remove '%20' crud from dev types with spaces in them $attr = decode_comment( $attr ) ; &debug_cli (1,"addattr d($attr) m($multi) r($required) t($type) v[@values]"); # create the new device_type my ( $dobj ) = Infoblox::Grid::ExtensibleAttributeDef->new( name => $attr, ); if ( $multi ) { $dobj->multiple ('true') ; } if ( $required ) { $dobj->required ('true') ; } if ( $type ) { $dobj->type ( $type ) ; } # set any values if ( @values ) { if ( $type =~ /list/ ) { # there is a new API call to create list values. my @vlist ; foreach my $v ( @values ) { $v = decode_comment($v); my $lval = Infoblox::Grid::ExtensibleAttributeDef::ListValue->new( value => $v, ); push @vlist , $lval ; } # print Dumper ( \@vlist ) ; $dobj->list_values( \@vlist ) ; } else { # just add the first value (decoded) $dobj->default_value( decode_comment($values[0]) ) ; } } # add it ! &do_func("add",$dobj); } sub cli_add_device_types { my ( $line ) = @_ ; # get the args from the line my ( $dev ) = $line =~/ device_type (\S+)/ ; my @labels = $line =~/ label (\S+)/g ; # remove '%20' crud from dev types with spaces in them $dev = decode_comment( $dev ) ; &debug_cli (1,"adddev d($dev) l(@labels)"); # create the new device_type my ( $dobj ) = Infoblox::DHCP::DeviceType->new( name => $dev, ); # now walk and create any custom labels foreach my $clabel ( @labels ) { my ( $orig , $new ) = $clabel =~ /(.*)=(.*)/; $dobj->$orig ( $new ) ; } # add it ! &do_func("add",$dobj); } =head2 Showing attributes types show grid attribute show grid attribute =cut sub cli_show_attributes { my ( $line ) = @_ ; # get the args from the line my ( $dev ) = $line =~/ attribute (\S+)/ ; &debug_cli (1,"shatt d($dev)"); # are we getting all or just some devices my $regex = ".*"; if ( $dev ) { $regex = $dev } # create the search my %opthash = ( object => "Infoblox::Grid::ExtensibleAttributeDef", name => $regex, ); # get what we need my @dobjs = &do_func("search", %opthash ); if ( $dev ) { &dump_object ( @dobjs ) ; } else { # just the list of types print map ( " " . $_->name() ." : " . $_->type() . "\n" , @dobjs ) ; }; } =head2 Showing device types show grid device_type show grid device_type =cut sub cli_show_device_types { my ( $line ) = @_ ; # get the args from the line my ( $dev ) = $line =~/ device_type (\S+)/ ; &debug_cli (1,"shdev d($dev)"); # are we getting all or just some devices my $regex = ".*"; if ( $dev ) { $regex = $dev } # create the search my %opthash = ( object => "Infoblox::DHCP::DeviceType", name => $regex, ); # get what we need my @dobjs = &do_func("search", %opthash ); if ( $dev ) { &dump_object ( @dobjs ) ; } else { # just the list of types print map ( " " . $_->name() . "\n" , @dobjs ) ; }; } =head2 Showing definitions for a device type [ ] TBD =cut =head2 Showing scheduled updates show grid schedule =cut sub cli_show_schedule { my ( $line ) = @_ ; # are we getting all or just some devices my $regex = ".*"; # create the search my %opthash = ( object => "Infoblox::Grid::ScheduledTask", submitter => $regex, ); # get what we need my @dobjs = &do_func("search", %opthash ); &dump_object ( @dobjs ) ; } =head2 Deleting scheduled updates conf grid schedule delete =cut sub cli_del_schedule { my ( $line ) = @_ ; my ( $id ) = $line =~/ delete (\d+)/ ; if ( $id ) { # create the search my %opthash = ( object => "Infoblox::Grid::ScheduledTask", task_id => $id, ); # get what we need my ( $task ) = &do_func("get", %opthash ); if ( $task ) { &do_func("remove",$task); } } } # Radius functions =head1 RADIUS Configuration =head2 Adding Radius users configure radius add user bob password changeme =cut sub cli_add_radius_user { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ user (\S+)/ ; my ( $pass ) = $line =~/ password (\S+)/ ; my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; &debug_cli (1,"addRuser n($name) p($pass) c($comment)"); my $obj = Infoblox::RADIUS::User->new ( "name" => $name , "password" => $pass , "comment" => $comment , ); &do_func("add",$obj); } =head2 Deleting Radius users configure radius del user bob =cut sub cli_del_radius_user { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ user (\S+)/ ; &debug_cli (1,"delRuser n($name)"); my $obj = Infoblox::RADIUS::User->new ( "name" => $name , ); &do_func("remove",$obj); } =head2 Showing Radius users show radius user bob =cut sub cli_show_radius_user { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ user (\S+)/ ; &debug_cli(2,"showu n($name)"); my @users ; if ( $name ) { # get the user my %opthash = ( "object" => "Infoblox::RADIUS::User", "name" => $name ); @users = &do_func("get", %opthash ); } else { # get ALL users # get the user my %opthash = ( "object" => "Infoblox::RADIUS::User", "name" => ".*", ); @users = &do_func("search", %opthash ); } &dump_object ( @users ) ; } =head2 Adding Radius devices You MUST specify a member and a Shared secret conf radius add device member 1.2.3.4 shared_secret xxxx You can also add a comment conf radius add device ... comment "Test AP" =cut sub cli_add_radius_device { # Infoblox::RADIUS::NAS my ( $line ) = @_ ; # get the args from the line # [ ] need to support multiple members my ( $name, $ip ) = $line =~/ add device (\S+) (\S+)/ ; my ( $shared ) = $line =~/ shared_secret (\S+)/ ; my ( $member ) = $line =~/ member (\S+)/ ; # # assign a member unless ( $member ) { $member = $MASTER_IP } &debug_cli (1,"adddev n($name) i($ip) m($member) s($shared)"); my $mem = Infoblox::RADIUS::Member->new ( secret => $shared, ipv4addr => $member ); my $obj = Infoblox::RADIUS::NAS->new ( ipv4addr => $ip , name => $name , secret => $shared , # member => $member , # members => [ $mem ] , ); # create the device # [ ] the whole membership thing is broken # # if you have 4.2r4 you need to use member objects # if you have 4.2r3 you use 'member => ...' my $niosrev ; if ( $ONLINE ) { $niosrev = $SESSION->server_version(); } if ( $niosrev =~ /4.2r[4-9]/ or $niosrev =~ /4.[3-9]/ ) { $obj->members( [ $mem ] ) ; } else { $obj->member( $member ) ; } if ( $obj ) { # comments ?? my ( $comment ) = $line =~/ comment "?([^"]+)"?/ ; # de-urlencode the name string $comment = decode_comment( $comment ) ; if ( $comment ) { $obj->comment ( $comment ) } &do_func("add",$obj); } } =head2 Deleting Radius devices conf radius del device member 1.2.3.4 shared_secret xxxx =cut sub cli_del_radius_device { my ( $line ) = @_ ; require_api( "4.2r4-1" ) or return; # get the args from the line my ( $ip , $name ) = $line =~/ delete device (\S+)/ ; my ( $shared ) = $line =~/ shared_secret (\S+)/ ; my ( $member ) = $line =~/ member (\S+)/ ; # only works for 4.2r4 or later unless ( $member ) { $member = $MASTER_IP } &debug_cli (1,"deldev n($name) i($ip) m($member) s($shared)"); # # if you have 4.2r4 you need to use member objects # my $mem = Infoblox::RADIUS::Member->new ( # secret => $shared, # ipv4addr => $member ); # we just need IP my $obj = Infoblox::RADIUS::NAS->new ( ipv4addr => $ip , # name => $name , # secret => $shared , # members => [ $mem ] , ); &do_func("remove",$obj); } =head2 Showing Radius users show radius device bob show radius device =cut sub cli_show_radius_device { my ( $line ) = @_ ; # get the args from the line my ( $ip ) = $line =~/ device (\S+)/ ; &debug_cli(2,"showdev i($ip)"); my @users ; if ( $ip ) { # get the user my %opthash = ( "object" => "Infoblox::RADIUS::NAS", "ipv4addr" => $ip ); @users = &do_func("get", %opthash ); } else { # get ALL users # get the user my %opthash = ( "object" => "Infoblox::RADIUS::NAS", "ipv4addr" => ".*", ); @users = &do_func("search", %opthash ); } &dump_object ( @users ) ; } # admins and users =head1 Grid Configuration =head2 Adding Administrator groups configure admin add admin_group locals make them superuser configure admin add admin_group locals superuser Add roles to the group configure admin add admin_group locals role "DNS Admin" You can also modify admin groups (to add roles or perms) This will ADD to the current list(s), not replace it configure admin modify admin_group locals role "DNS Admin" =cut sub cli_add_admin_group { my ( $line ) = @_ ; # get the args from the line my ( $type , $name ) = $line =~/ (add|modify) admin_group (\S+)/ ; my ( $su ) = $line =~/\b(superuser)\b/ ; my ( $disable ) = $line =~/\b(disabled)\b$/ ; # spaces allowed for names $name = decode_comment( $name ); &debug_cli (1,"addgrp g($name)"); my $obj ; if ( $type eq "modify" ) { # this might become a subroutine # get the object my %opthash = ( "object" => "Infoblox::Grid::Admin::Group", "name" => $name ); ( $obj ) = &do_func("get", %opthash ); unless ( $obj ) { print " Error : $name : No group found\n"; # exit here return } } else { $obj = Infoblox::Grid::Admin::Group->new ( "name" => $name , ); } # de-urlencode the comment string # and add an optional comment my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; $comment = decode_comment( $comment ) ; if ( $comment ) { $obj->comment ( $comment ) } if ( $su ) { $obj->superuser ( "TRUE" ) } if ( $disable ) { $obj->disabled ( "TRUE" ) } # set some roles my ( @roles ) = $line =~/\brole\s+(\S+)/g ; if ( @roles ) { my @rvalues ; # get current settings if ( $type eq 'modify' ) { @rvalues = @{ $obj->roles() }; } foreach my $rstring ( @roles ) { push @rvalues , decode_comment( $rstring ) ; } $obj->roles( \@rvalues ) ; } &do_func($type,$obj); } =head2 Adding Administrator Roles configure admin add role locals =cut sub cli_add_admin_role { my ( $line ) = @_ ; # get the args from the line my ( $type , $name ) = $line =~/ (add|modify) role (\S+)/ ; $name = decode_comment( $name ) ; &debug_cli (1,"addrole n($name)"); my $obj = Infoblox::Grid::Admin::Role->new ( "name" => $name ); my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; $comment = decode_comment( $comment ) ; if ( $comment ) { $obj->comment ( $comment ) } &do_func($type,$obj); } =head2 Adding users You need the admin group name and a password configure admin add user bob group locals password changeme =cut sub cli_add_user { my ( $line ) = @_ ; # get the args from the line my ( $type , $user ) = $line =~/ (add|modify) user (\S+)/ ; my ( $pass ) = $line =~/ password (\S+)/ ; my ( $group ) = $line =~/ group (\S+)/ ; my ( $email ) = $line =~/ email (\S+)/ ; my ( $disable ) = $line =~/\b(disabled)\b$/ ; # need multi-word groups $group = &decode_comment( $group ); &debug_cli (1,"adduser u($user) p($pass) g($group)"); my $obj ; if ( $type eq "modify" ) { # this might become a subroutine # get the object my %opthash = ( "object" => "Infoblox::Grid::Admin::User", "name" => $user ); ( $obj ) = &do_func("get", %opthash ); unless ( $obj ) { print " Error : $user : No user found\n"; # exit here return } } else { unless ( $user and $pass and $group ) { print " Error : user, password and group are required\n"; return ; } $obj = Infoblox::Grid::Admin::User->new ( "name" => $user , "password" => $pass , "admin_group" => $group , ); } # set some variables, these seem redundant because of # the 'modify' behaviour if ( $group ) { $obj->admin_group ( $group ) } if ( $pass ) { $obj->password ( $pass ) } if ( $email ) { $obj->email ( $email ) } if ( $disable ) { $obj->disabled ( "TRUE" ) } # de-urlencode the comment string # and add an optional comment my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; $comment = decode_comment( $comment ) ; if ( $comment ) { $obj->comment ( $comment ) } &do_func($type,$obj); } =head2 Adding permissions Permissions can be any of 'read' 'write' or 'deny', and are applied to an admin group or an admin or an admin_role conf admin add permission group conf admin add perm write zone foo.com group dns_admins conf admin add perm read network 45.0.0.0/24 group dhcp_guys conf admin add perm read range 45.0.0.10-45.0.0.20 group dhcp_guys (ranges actually only need the 'start_addr' ) conf admin add perm read range 45.0.0.10 group dhcp_guys Add permissions to a role conf admin add perm write zone foo.com role "AAA Admin" You can also add resource_types : conf admin add perm write type "All DHCP Templates" group dhcp_guys conf admin add perm write type "All DHCP Templates" role "DHCP Admin" In some cases you can also do this when configuring the object : conf zone add permission group conf zone test.com add permission read group ops conf net 45.0.0.0/24 add permission read group ops conf member ns1.test.com add permission read group ops =cut sub cli_add_permission { my ( $line ) = @_ ; # sometimes zone and view can be transposed, so we reset them here # to get the right otype # 'view External zone foo.com' or 'zone foo.com view External' $line =~ s/\b(view \S+)\s+(zone \S+)/$2 $1/i; # get the args from the line my ( $perm ) = $line =~/permission\s+(read|write|deny)\s/ ; my ( $otype, $oname ) = $line =~/ permission \S+ (\S+) (\S+)/ ; my ( $group ) = $line =~/ group (\S+)/ ; my ( $role ) = $line =~/ role (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $sub ) = $line =~/ sub_object (\S+)/ ; $oname = decode_comment( $oname ) ; $role = decode_comment( $role ) ; &debug_cli (1,"addperm t($otype) n($oname) p($perm) r($role) g($group) s($sub)"); # make this generic for different object types my $pobj ; if ( $otype eq "zone" ) { # create a zone handle $pobj = Infoblox::DNS::Zone->new( name => $oname, ); if ( $view ) { $pobj->views ([ Infoblox::DNS::View->new( "name" => $view ) ]); } } if ( $otype eq "view" ) { # create a zone handle $pobj = Infoblox::DNS::View->new( name => $oname, ); } if ( $otype eq "filter" ) { $pobj = Infoblox::DHCP::Filter::MAC->new( name => $oname, ); } if ( $otype eq "network" ) { $pobj = Infoblox::DHCP::Network->new( network => $oname, ); } if ( $otype eq "range" ) { # create a range handle # only start addr is required (yay!!) my ( $fip,$lip ) = split ( /-/ , $oname ) ; $pobj = Infoblox::DHCP::Range->new( start_addr => $fip, # end_addr => $lip, ); } if ( $otype eq "member" ) { # get a member handle (tis easier than creating one # because we need more params to call new() ( $pobj ) = &do_func("get", ( object => "Infoblox::Grid::Member", name => $oname, # ipv4addr => $oname, ) ); } if ( $otype eq "lease" and $oname eq "history" ) { # fake out some syntax shorteing $otype = "type"; $oname = "Lease History Access"; } # try an make the permissions my $permission ; if ( $otype eq "type" ) { # we set resource_type permissions if ( $group ) { $permission = Infoblox::Grid::Admin::Permission->new( admin_group => $group, resource_type => $oname, permission => $perm, ); } elsif ( $role ) { $permission = Infoblox::Grid::Admin::Permission->new( role => $role, resource_type => $oname, permission => $perm, ); } } elsif ( $pobj ) { # we set resource_object permissions if ( $group ) { $permission = Infoblox::Grid::Admin::Permission->new( admin_group => $group, resource_object => $pobj, permission => $perm, ); } elsif ( $role ) { $permission = Infoblox::Grid::Admin::Permission->new( role => $role, resource_object => $pobj, permission => $perm, ); } } else { print " Error, can't create resource_object ($otype) ($oname)\n"; } # if we have a sub object, we need to shove it in here as well, aka # # resource_object => $zone, # resource_type => "All A Records", # if ( $sub and $permission ) { if ( $sub =~ /(\S+)_record/ ) { $sub = uc ( $1 ) ; $sub = "All $sub Records" ; } $sub = "All HOST Records" if $sub =~ /host/; $sub = "All BULKHOST Records" if $sub =~ /bulk_host/; # save the perm $permission->resource_type( $sub ) ; } if ( $permission ) { # Submit for addition &do_func("add",$permission); } } =head2 Deleting or Changing permissions Permissions can be any of 'read' 'write' or 'deny', and ar applied to an admin group or an admin. If you want to change a permission, you have to delete it, then add a new permission (yes, there should be a better way) conf zone del permission group conf zone test.com del permission read group ops conf net 45.0.0.0/24 del permission read group ops =cut sub cli_del_permission { my ( $line ) = @_ ; # get the args from the line my ( $otype, $oname ) = $line =~/ (zone|network|member) (\S+)/ ; my ( $perm ) = $line =~/\s(read|write|deny)\s/ ; my ( $group ) = $line =~/ group (\S+)/ ; &debug_cli (1,"delperm t($otype) n($oname) p($perm) g($group)"); # make this generic for different object types my $pobj ; if ( $otype eq "zone" ) { # greate a zone handle $pobj = Infoblox::DNS::Zone->new( name => $oname, ); } if ( $otype eq "network" ) { # greate a zone handle $pobj = Infoblox::DHCP::Network->new( network => $oname, ); } if ( $otype eq "member" ) { # get a member handle (tis easier than creating one # because we need more params to call new() ( $pobj ) = &do_func("get", ( object => "Infoblox::Grid::Member", name => $oname, ) ); } if ( $pobj ) { # now create the permission my $permission = Infoblox::Grid::Admin::Permission->new( admin_group => $group, resource_object => $pobj, permission => $perm, ); # Submit for addition &do_func("remove",$permission); } else { print " Error, can't create resource_object ($otype) ($oname)\n"; } } =head2 Modifying permissions you need enough unique information to find the correct permission and them modify it, thus you need : - resource object - admin group The CLI will then try to get the current permission and modify it conf zone mod permission group To change a zone permision to 'read-only' : conf zone test.com mod permission read group ops =cut sub cli_mod_permission { my ( $line ) = @_ ; # get the args from the line my ( $otype, $oname ) = $line =~/ (zone|network|member) (\S+)/ ; my ( $perm ) = $line =~/\s(read|write|deny)\s/ ; my ( $group ) = $line =~/ group (\S+)/ ; &debug_cli (1,"modperm t($otype) n($oname) p($perm) g($group)"); # make this generic for different object types my $pobj ; if ( $otype eq "zone" ) { # greate a zone handle $pobj = Infoblox::DNS::Zone->new( name => $oname, ); } if ( $otype eq "network" ) { # greate a zone handle $pobj = Infoblox::DHCP::Network->new( network => $oname, ); } if ( $otype eq "member" ) { # get a member handle (tis easier than creating one # because we need more params to call new() ( $pobj ) = &do_func("get", ( object => "Infoblox::Grid::Member", name => $oname, ) ); } if ( $pobj ) { # get the current permission my %opthash = ( object => "Infoblox::Grid::Admin::Permission", admin_group => $group, resource_object => $pobj, ); my ( $permission ) = &do_func("get", %opthash ); # now modify the permission if ( $permission ) { $permission->permission($perm); # Submit for modition &do_func("modify",$permission); } } else { print " Error, can't create resource_object ($otype) ($oname)\n"; } } =head2 configure global Grid settings Add or remove access methods conf grid add remote_console_access conf grid delete remote_console_access conf grid add support_access conf grid add lcd_input Modify some basic grid values configure grid Infoblox modify grid_name My_Grid configure grid Infoblox modify vpn_port 1194 configure grid Infoblox modify shared_secret test configure grid Infoblox modify session_timeout 14400 configure grid Infoblox modify prefer_resolver 127.0.0.1 Add ntp servers ( and enable NTP at the same time ) configure grid Infoblox add ntp_server 1.1.1.1 ntp_server 2.2.2.2 Disable the NTP service configure grid Infoblox disable ntp or set a random API method configure grid Infoblox set = configure grid Infoblox set query_comm_string=public =cut sub cli_mod_grid { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/configure grid (\S+)/ ; ( my $timeout) = $line =~/session_timeout (\S+)/ ; ( my $resolver) = $line =~/resolver (\S+)/ ; ( my $vpn_port) = $line =~/vpn_port (\S+)/ ; ( my $secret) = $line =~/shared_secret (\S+)/ ; ( my $newname) = $line =~/grid_name (\S+)/ ; my (@ntp_servers) = $line =~/ ntp_server (\S+)/g ; # find some booleans my $rem_access ; my $supp_access ; my $lcd_input ; my $use_ntp ; $line =~/add remote_console_access/ and $rem_access = "TRUE" ; $line =~/add support_access/ and $supp_access = "TRUE" ; $line =~/add lcd_input/ and $lcd_input = "TRUE" ; $line =~/delete remote_console_access/ and $rem_access = "FALSE" ; $line =~/delete support_access/ and $supp_access = "FALSE" ; $line =~/delete lcd_input/ and $lcd_input = "FALSE" ; $line =~/enable ntp$/ and $use_ntp = "TRUE" ; $line =~/disable ntp$/ and $use_ntp = "FALSE" ; # first get the grid my %opthash = ( "object" => "Infoblox::Grid", "name" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); &debug_cli (2,"modgr st($timeout) rc($rem_access) sa($supp_access)"); &debug_cli (2,"modgr lc($lcd_input) vpn($vpn_port) ss($secret)"); # make some changes, if we have a grid if ( $grid ) { if ( $resolver ) { $grid->prefer_resolver ( $resolver ) } if ( $timeout ) { $grid->session_timeout ( $timeout ) } if ( $secret ) { $grid->secret ( $secret ) } if ( $vpn_port ) { $grid->vpn_port ( $vpn_port ) } if ( $newname ) { $grid->name ( $newname ) } if ( $rem_access ) { $grid->remote_console_access ( $rem_access ) } if ( $supp_access ) { $grid->support_access ( $supp_access ) } if ( $lcd_input ) { $grid->lcd_input ( $lcd_input ) } if ( $use_ntp ) { $grid->enable_ntp ( $use_ntp ) } # Add NTP servers if ( @ntp_servers ) { my @ntp_list ; foreach my $ntp ( @ntp_servers ) { # normaal members push @ntp_list , Infoblox::Grid::NTPServer->new( "address" => $ntp , authentication => "FALSE", ); &debug_cli (2,"modgr ntp($ntp)"); } $grid->ntp_server ( \@ntp_list ) ; $grid->enable_ntp( "TRUE" ); } # settings - these are additional fields that the API supports my @settings = $line =~/ set (\S+)/g ; if ( @settings ) { $grid = add_settings ( $grid , @settings ) } # push the changes, if we were ok &do_func("modify",$grid); } } =head2 configure Member settings Enable or Disable the NTP service configure grid Infoblox modify member 1.2.3.20 enable ntp configure grid Infoblox modify member 1.2.3.20 disable ntp Change the IP address conf grid Infoblox modify member infoblox.localdomain ipaddress 10.64.128.40/24 change the name conf grid Infoblox modify member infoblox.localdomain ipaddress 10.64.128.40/24 name dns1.myzone.com Make this an HA pair conf grid blox modify member foo.com hapair 10.64.128.41,10.64.128.42,10.64.128.43,10.64.128.44 routerid 40 =cut sub cli_mod_member { my ( $line ) = @_ ; # get the args from the line ( my $member) = $line =~/modify member (\S+)/ ; my ($address) = $line =~/ ipaddress (\S+)/ ; my ($name) = $line =~/ name (\S+)/ ; my ($gateway) = $line =~/ gateway (\S+)/ ; my ($haaddr) = $line =~/ hapair (\S+)/ ; my ($harid) = $line =~/ routerid (\S+)/ ; my ($comment) = $line =~/ comment (\S+)/ ; $comment = decode_comment( $comment ) ; # find some booleans my $use_ntp ; $line =~/enable ntp$/ and $use_ntp = "TRUE" ; $line =~/disable ntp$/ and $use_ntp = "FALSE" ; # break ip into ip and mask my ( $ip , $cidr ) = split ( /\// , $address ) ; my $mask = &cidr_to_mask( $cidr ) ; &debug_cli (2,"modme ip($ip) m($mask) gw($gateway) ha($haaddr)"); # first get the member my %opthash = ( "object" => "Infoblox::Grid::Member", "name" => $member , ); my ( $memobj ) = &do_func("get", %opthash ); # make some changes, if we have a grid if ( $memobj ) { if ( $comment ) { $memobj->comment ( $comment ) } if ( $name ) { $memobj->name ( $name ) } if ( $use_ntp ) { $memobj->enable_ntp ( $use_ntp ) } if ( $gateway ) { $memobj->gateway ( $gateway ) } if ( $address ) { $memobj->mask ( $mask ) ; $memobj->ipv4addr ( $ip) } # make this an hapair if ( $haaddr ) { my ( $l1,$h1,$l2,$h2 ) = split ( /,/ , $haaddr ) ; $memobj->type ("HApair" ) ; $memobj->node1_lan ($l1) ; $memobj->node1_ha ($h1) ; $memobj->node2_lan ($l2) ; $memobj->node2_ha ($h2) ; $memobj->router_ID ( $harid ) ; } elsif ( $address ) { # do we reset HA settings ? $memobj->type ("IDnode" ) ; } # push the changes, if we were ok &do_func("modify",$memobj); } } =head2 Configure Member DNS settings Set a value conf member ns1.lab.com dns set ... conf member ns1.lab.com dns set forwarders="[1.1.1.1,4.2.2.2]" =cut sub cli_mod_member_dns { my ( $line ) = @_ ; # get the args from the line ( my $member) = $line =~/configure member (\S+)/ ; my @settings = $line =~/ set (\S+)/g ; &debug_cli (2,"medns ip($member) s(@settings)"); # first get the member my %opthash = ( "object" => "Infoblox::Grid::Member::DNS", "name" => $member , ); my ( $memobj ) = &do_func("get", %opthash ); # make some changes, if we have a member if ( $memobj ) { # settings - these are additional fields that the API supports if ( @settings ) { $memobj = add_settings ( $memobj , @settings ) } # push the changes, if we were ok &do_func("modify",$memobj); } } =head2 Show Member DNS settings show member dns show member ns1.lab.com dns You can also get there from the zone command show zone member ns1.lab.com =cut sub cli_show_member_dns { my ( $line ) = @_ ; # get the args from the line ( my $member) = $line =~/ member (\S+)/ ; &debug_cli (2,"shmdns m($member) "); # first get the member my %opthash = ( "object" => "Infoblox::Grid::Member::DNS", "name" => $member , ); my ( $memobj ) = &do_func("get", %opthash ); if ( $memobj ) { &dump_object ( $memobj ) ; } } =head2 Configure Member DHCP settings Set an option conf member ns1.lab.com dhcp option 82="some data" Set a value conf member ns1.lab.com dhcp set ... conf member ns1.lab.com dhcp set forwarders="[1.1.1.1,4.2.2.2]" =cut sub cli_mod_member_dhcp { my ( $line ) = @_ ; # get the args from the line ( my $member) = $line =~/configure member (\S+)/ ; my @options = $line =~/ option (\S+)/g ; my @settings = $line =~/ set (\S+)/g ; &debug_cli (2,"medhcp ip($member) s(@settings)"); # first get the member my %opthash = ( "object" => "Infoblox::Grid::Member::DHCP", "name" => $member , ); my ( $memobj ) = &do_func("get", %opthash ); # make some changes, if we have a member if ( $memobj ) { # options if ( @options ) { my @opt_list ; foreach my $optval ( @options ) { # set some custom options; # split on the '=' my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli(2,"addnet o($opt) v($val)"); my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; # only add options we recognise if ( $opt_obj ) { push @opt_list , $opt_obj ; } } # and set it $memobj->options ( \@opt_list ) ; } # settings - these are additional fields that the API supports if ( @settings ) { $memobj = add_settings ( $memobj , @settings ) } # push the changes, if we were ok &do_func("modify",$memobj); } } =head2 Adding members configure grid Infoblox add member ns1.foo.com ipaddress 1.2.3.20/24 Add a member as an HA pair configure grid Infoblox add member ns1.foo.com \ ipaddress 192.168.1.100/24 gateway 192.168.1.1 \ hapair 192.168.1.101,192.168.1.102,192.168.1.103,192.168.1.104 \ routerid 100 Members can have a LOT of options... To add a mgmt port : mgmt_ip 5.195.156.52/25 mgmt_gate 5.195.156.1 To add mgmt ports to HA pairs ( 'hapair' must be defined ) : mgmt_ip 5.195.156.52/25 mgmt_gate 5.195.156.1 mgmt_ip_2 5.195.156.53/25 =cut sub cli_add_member { my ( $line ) = @_ ; # get the args from the line ( my $member) = $line =~/add member (\S+)/ ; my ($grid) = $line =~/ grid (\S+)/ ; my ($vip) = $line =~/ ipaddress (\S+)/ ; my ($gateway) = $line =~/ gateway (\S+)/ ; my ($haaddr) = $line =~/ hapair (\S+)/ ; my ($harid) = $line =~/ routerid (\S+)/ ; my ($mgmtip) = $line =~/ mgmt_ip (\S+)/ ; my ($mgmtip2) = $line =~/ mgmt_ip_2 (\S+)/ ; my ($mgate) = $line =~/ mgmt_gateway (\S+)/ ; # get and de-urlencode the comment string my ( $comment ) = $line =~/comment "?([^"]+)"?/ ; $comment = decode_comment( $comment ) ; # break ip into ip and mask my ( $ip , $cidr ) = split ( /\// , $vip ) ; my $mask = &cidr_to_mask( $cidr ) ; my ( $mip , $mcidr ) = split ( /\// , $mgmtip ) ; my $mmask = &cidr_to_mask( $mcidr ) ; my ( $mip2 , $mcidr2 ) = split ( /\// , $mgmtip2 ) ; my $mmask2 = &cidr_to_mask( $mcidr ) ; &debug_cli (2,"addme ip($ip) m($mask) gw($gateway) ha($haaddr)"); &debug_cli (2,"addme mip($mip) mm($mmask) mgw($mgate) m2($mgmtip2)"); # create the object my $memobj = Infoblox::Grid::Member->new( "grid" => $grid , "name" => $member , "gateway" => $gateway , "ipv4addr" => $ip , "mask" => $mask , # "comment" => $comment, ); if ( $comment ) { $memobj->comment ( $comment ) } # make some changes, if we have a grid if ( $memobj ) { # make this an hapair if ( $haaddr ) { my ( $l1,$h1,$l2,$h2 ) = split ( /,/ , $haaddr ) ; $memobj->type ("HApair" ) ; $memobj->node1_lan ($l1) ; $memobj->node1_ha ($h1) ; $memobj->node2_lan ($l2) ; $memobj->node2_ha ($h2) ; $memobj->router_ID ( $harid ) ; } if ( $mip and $mcidr and $mgate ) { if ( $haaddr ) { # set ALL the mgmt port info $memobj->mgmt_lan ($mip) ; $memobj->mgmt_gateway ($mgate) ; $memobj->mgmt_mask ($mmask) ; $memobj->mgmt_port ('true') ; $memobj->node1_mgmt_lan ($mip) ; $memobj->node1_mgmt_gateway ($mgate) ; $memobj->node1_mgmt_mask ($mmask) ; $memobj->node1_mgmt_port ('true') ; $memobj->node2_mgmt_lan ($mip2) ; $memobj->node2_mgmt_gateway ($mgate) ; $memobj->node2_mgmt_mask ($mmask) ; $memobj->node2_mgmt_port ('true') ; } else { $memobj->mgmt_lan ($mip) ; $memobj->mgmt_gateway ($mgate) ; $memobj->mgmt_mask ($mmask) ; $memobj->mgmt_port ('true') ; } } # push the changes, if we were ok &do_func("add",$memobj); } else { my $result = Infoblox->status_code(); my $response = Infoblox->status_detail(); print "ERR $result , $response\n"; } } =head2 Removing members configure grid Infoblox delete member ns1.foo.com ipaddress 1.2.3.20/24 =cut sub cli_del_member { my ( $line ) = @_ ; # get the args from the line ( my $member) = $line =~/delete member (\S+)/ ; my ($grid) = $line =~/ grid (\S+)/ ; my ($vip) = $line =~/ ipaddress (\S+)/ ; my ($gateway) = $line =~/ gateway (\S+)/ ; # break ip into ip and mask my ( $ip , $cidr ) = split ( /\// , $vip ) ; my $mask = &cidr_to_mask( $cidr ) ; &debug_cli (2,"delme n($member) ip($ip) m($mask) gw($gateway)"); # create the object my $memobj = Infoblox::Grid::Member->new( "Grid" => $grid , "name" => $member , "gateway" => $gateway , "ipv4addr" => $ip , "mask" => $mask , ); # make some changes, if we have a grid if ( $memobj ) { # push the changes, if we were ok &do_func("remove",$memobj); } else { my $result = Infoblox->status_code(); my $response = Infoblox->status_detail(); print "ERR $result , $response\n"; } } # dns settings =head2 add global DNS settings conf grid dns add default_ttl conf grid dns add recursion conf grid dns add ns_group DNS ACLs These will ADD the addresses to the acl, not replace the list conf grid dns add acl allow transfer x.x.x.x/nn conf grid dns add acl deny update x.x.x.x/nn =cut sub cli_mod_dns { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/configure grid (\S+)/ ; # field based values ( my $ttl) = $line =~/ default_ttl (\S+)/ ; # find some booleans my $recursion ; $line =~/add recursion/ and $recursion = "TRUE" ; &debug_cli (1,"modgd g($gridname) rc($recursion) tt($ttl)"); # get the grid my %opthash = ( "object" => "Infoblox::Grid::DNS", "name" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); # make some changes, if we have a grid if ( $grid ) { if ( $recursion ) { $grid->allow_recursive_query ( $recursion ) } if ( $ttl ) { $grid->default_ttl ( $ttl ) } # push the changes, if we were ok &do_func("modify",$grid); } } sub cli_add_grid_dns_acl { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/configure grid (\S+)/ ; my ( $perm , $type , $ip) = $line =~/ (allow|deny) (\S+) (\S+)/g ; # build the ACL lust my $acl; if ($type eq "recursion") { $acl = "recursive_query_list"; } else { $acl = "allow_$type" ; } # convert the ACL list into an array my @iplist ; foreach my $aip ( split (/\s*,\s*/ , $ip ) ) { # allow for deny rules if ( $perm eq "deny" ) { push @iplist , "!$ip"; } else { push @iplist , $aip ; } } # # allow for deny rules # if ( $perm eq "deny" ) { # $ip = "!$ip"; # } &debug_cli(1,"addgx p($perm) a($acl) i(@iplist)"); # get the grid my %opthash = ( "object" => "Infoblox::Grid::DNS", "name" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); if ( $grid ) { # get the grid ACL my @list = @{ $grid->$acl() }; &debug_cli(2,"addgx l(@list)"); # add this address to the ACL list $grid->$acl([ @list , @iplist ]); # push it back &do_func("modify",$grid); } } =head2 add global DHCP option definitions Create a Vendor space conf network add space conf network add space SUNW Create a DHCP network option conf network add optiondef code type conf network add optiondef voip 150 type string If you want to add them to vendor spaces.. conf network add optiondef server 150 type string space SUNW conf network add optiondef boot 270 type string space Cisco-ap =cut sub cli_add_option_space { my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/add space (\S+)/ ; my $def = Infoblox::DHCP::OptionSpace->new( name => $name, ); # Submit for adding &do_func("add",$def); } sub cli_add_option_def { # Infoblox::DHCP::OptionDefinition # my $option_definition1 = Infoblox::DHCP::OptionDefinition->new( # space => "SUNW", # name => "JumpStart-server", # code => "6", # type => "text" # ); # # Submit for adding # my $response = $session->add( $option_definition1 ); my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/add optiondef (\S+)/ ; my ( $num ) = $line =~/ code (\S+)/ ; my ( $type ) = $line =~/ type (\S+)/ ; my ( $space ) = $line =~/ space (\S+)/ ; # [ ] can't add 'array of IP address' $type = decode_comment( $type ) ; &debug_cli (1,"adddef n($name) #($num) t($type)"); my $def = Infoblox::DHCP::OptionDefinition->new( # space => "SUNW", name => $name, code => $num, type => $type, ); if ( $space ) { $def->space( $space ) }; # Submit for adding &do_func("add",$def); } =head2 add global DHCP options Add a network option to the grid level conf grid dhcp add option = conf grid Infoblox dhcp add option 82="some data" Remove a custom option conf grid delete network option 82 =cut # you only need the GRID name to ADD an option, not to DEFINE an option # so, ADD should use the same syntax as networks etc sub cli_add_grid_option { my ( $line ) = @_ ; # get the args from the line my ( $gridname) = $line =~/configure grid (\S+)/ ; my ( $optval ) = $line =~/add option (\S+)/ ; my ( $opt , $val ) = $optval =~ /^(.*?)=(.*)/ ; &debug_cli (1,"addgo g($gridname) o($optval) v($val)"); # turn our selection into a DHCP option object my ($opt_obj,$key)=make_dhcp_option($DEBUG,$opt,$val) ; print "NOT implemented, just use the GUI\n"; return ; if ( $opt_obj ) { # get the grid my %opthash = ( "object" => "Infoblox::Grid::DHCP", "name" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); # get the current grid options # search for the option we want to set # add or replace it } # if ( $opt_obj ) { # push @opt_list , $opt_obj ; # } } =head2 Remove a custom option conf network global delete custom_option 82 conf network global delete lease_time =cut sub cli_del_global_dhcp { } =head2 Show global DNS options show grid dns =cut sub cli_show_grid_dns { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/show grid (\S+)/ ; # get the grid my %opthash = ( "object" => "Infoblox::Grid::DNS", "name" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); if ( $grid ) { &dump_object ( $grid ) ; } } =head2 Show global DHCP options show grid dhcp =cut sub cli_show_grid_dhcp { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/show grid (\S+)/ ; # get the grid my %opthash = ( "object" => "Infoblox::Grid::DHCP", "grid" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); if ( $grid ) { &dump_object ( $grid ) ; } } # End -- API --- # # show stuff # =head1 SHOW commands =head2 Showing Zones Show all zones show zone Show all zones with details show zone detailed Show all forward zones show zone forward Show all reverse zones show zone reverse Show all secondary (external_primary) zones show zone secondary Show just 1 zone show zone foo.com Show NameServer Groups show zone ns_group show zone ns_group Show Shared Record Groups show zone shared_record_group show zone shared_record_group =cut sub cli_show_zone { # # show needs some cleanup my ( $line ) = @_ ; # get the args from the line my ( $type ) = $line =~/ zone (\S+)/ ; my ( $name ) = $line =~/ zone \S+ (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $detailed ) = $line =~/\bdetailed\b/ ; my ( $shared ) = $line =~/ shared_record_group (\S+)/ ; $view = decode_comment( $view ) ; my $ucname = $name ; # some crap is case inseneitive my $capname = $name ; $name = lc( $name ) ; $type = lc( $type ) ; &debug_cli(1,"showz ty($type) n($name)"); # we can get either all forward, rev or just a zone if ( !$type or $type eq "detailed" or $type eq "forward" or $type eq "reverse" or $type eq "secondary" or $type eq "view" ) { # get all the zones my %opthash = ( "object" => "Infoblox::DNS::Zone", "name" => ".*" ); # do we have a specific view ? if ( $view ) { $opthash{"view"} = $view ; }; my @zones = &do_func("search", %opthash ); # we can't sort on oid, we have to sort on name # and we don't want the view name to get in the way, so we will # store this in a hash... my %forzones ; my %revzones ; my @fsummary ; my @rsummary ; my @ssummary ; my @fobjects ; my @robjects ; # we need a 'sorted' list of objects # so first we get forward or reverse zones # and drop them into hashes for sorting # # we also need to key these by the view to avoid colbbering dupes foreach my $zone (@zones) { my $name = $zone->name(); my $view = $zone->{views}[0]->{name}; my $is_secondary = $zone->{'use_external_primary'}; # print "n[$name] v[$view] s($is_secondary)\n"; my $nkey = "$view.$name"; # only get the zones we need if ( $name =~ /\/\d+$/ ) { $revzones{$nkey}{'name'} = $name ; $revzones{$nkey}{'view'} = $view ; $revzones{$nkey}{'obj'} = $zone ; $revzones{$nkey}{'sec'} = $is_secondary ; } else { $forzones{$nkey}{'name'} = $name ; $forzones{$nkey}{'view'} = $view ; $forzones{$nkey}{'obj'} = $zone ; $forzones{$nkey}{'sec'} = $is_secondary ; } } # now sort both forward and reverse foreach my $fz ( sort by_dom keys %forzones ) { my $name = $forzones{$fz}{'name'} ; my $v = "view $forzones{$fz}{'view'}" ; my $info = " $name $v"; my $ng = $forzones{$fz}{'obj'}->ns_group() ; if ( $ng ) { $info .= " ns_group $ng"; } # push @fsummary , " $name view $forzones{$fz}{'view'}\n"; push @fsummary , "$info\n"; push @fobjects , $forzones{$fz}{'obj'} ; # print "z[$fz] ($forzones{$fz}{'sec'})\n"; if ( $forzones{$fz}{'sec'} == 1 ) { push @ssummary , "$info\n"; } } foreach my $rz ( sort by_ip keys %revzones ) { my $name = $revzones{$rz}{'name'} ; my $v = "view $revzones{$rz}{'view'}" ; my $info = " $name $v"; my $ng = $revzones{$rz}{'obj'}->ns_group() ; if ( $ng ) { $info .= " ns_group $ng"; } push @rsummary , "$info\n"; push @robjects , $revzones{$rz}{'obj'} ; if ( $revzones{$rz}{'sec'} == 1 ) { push @ssummary , "$info\n"; } } # now print them (sorted) if ( $type eq "forward" ) { if ( $detailed ) { &dump_object ( @fobjects ) ; } else { print @fsummary ; } } elsif ( $type eq "reverse" ) { if ( $detailed ) { &dump_object ( @robjects ) ; } else { print @rsummary ; } } elsif ( $type eq "secondary" ) { print @ssummary ; # if ( $detailed ) { # &dump_object ( @robjects ) ; # } # else { # print @rsummary ; # } } else { # all zones if ( $detailed ) { &dump_object ( @robjects , @fobjects ) ; } else { print @rsummary ; print @fsummary ; } } } elsif ( $type eq "ns_group" ) { # get nsgroup info my $search = ".*" ; if ( $name ) { $search = $capname } my %opthash = ( "object" => "Infoblox::Grid::DNS::Nsgroup", "name" => $search, ); my @groups = &do_func("search", %opthash ); &dump_object ( @groups ) ; } elsif ( $type eq "shared_record_group" ) { # get nsgroup info my $search = ".*" ; if ( $name ) { $search = $ucname } # ( keep the case ) my %opthash = ( "object" => "Infoblox::DNS::SRG", "name" => $search, ); my @groups = &do_func("search", %opthash ); # my @names = map ( " " . $_->name() . "\n" , @groups ) ; if ( $name ) { &dump_object ( @groups ) ; } else { print map ( " " . $_->name() . "\n" , @groups ) ; } } else { # specific zone my %opthash = ( "object" => "Infoblox::DNS::Zone", "name" => $type ); # do we have a specific view ? if ( $view ) { $opthash{"view"} = $view ; }; # multiple views... my @zlist = &do_func("get", %opthash ); if ( @zlist ) { &dump_object ( @zlist ) ; } else { print "zone $type (view $view) not found\n"; } } } =head2 Showing Views Show all views show views or show zone view Show just One view show views default or show zone view default =cut sub cli_show_view { # my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ views (\S+)/ ; $name = decode_comment( $name ) ; &debug_cli(2,"showv t($name)"); # we can get either all forward, rev or just a view if ( ! $name ) { # get all the views my %opthash = ( "object" => "Infoblox::DNS::View", "name" => ".*" ); my @views = &do_func("search", %opthash ); my @vlist ; foreach my $view (@views) { my $name = $view->name(); push @vlist , " $name\n"; } print sort @vlist ; } else { # specific zone my %opthash = ( "object" => "Infoblox::DNS::View", "name" => $name ); my ( $view ) = &do_func("get", %opthash ); &dump_object ( $view ) ; } } =head2 Showing hosts show host www.foo.com =cut sub cli_show_host { my ( $line ) = @_ ; # get the args from the line my ( $fqdn ) = $line =~/ host (\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; my ( $ip ) = $line =~/ ipv4addr (\S+)/ ; my ( $mac ) = $line =~/ mac (\S+)/ ; $view = decode_comment( $view ) ; &debug_cli(2,"showh n($fqdn) v($view) i($ip)"); # set up the searcp params my %opthash = ( "object" => "Infoblox::DNS::Host", ); # search by IP/name if ( $ip ) { $opthash{'ipv4addr'} = $ip ; } elsif ( $mac ) { $opthash{'mac'} = $mac ; } elsif ( $fqdn ) { # this must be the LAST test $opthash{'name'} = $fqdn ; } # include view if ( $view ) { $opthash{'view'} = $view ; } my @hosts = &do_func("search", %opthash ); &dump_object ( @hosts ) ; } =head2 Showing records show record cname www.foo.com show record a_record test.foo.com show record a_record test.foo.com view internal Searching by EA is tricky because of the parser, you have to give a name, but it will be ignored show record a_record anyname info Site=west =cut sub cli_show_record { my ( $line ) = @_ ; # get the args from the line my ( $type , $name ) = $line =~/ record (\S+)\s+(\S+)/ ; my ( $view ) = $line =~/ view (\S+)/ ; # set up the object my $olist = { cname => "Infoblox::DNS::Record::CNAME", bulkhost => "Infoblox::DNS::BulkHost", a_record => "Infoblox::DNS::Record::A", aaaa => "Infoblox::DNS::Record::AAAA", mx => "Infoblox::DNS::Record::MX", txt => "Infoblox::DNS::Record::TXT", ptr => "Infoblox::DNS::Record::PTR", srv => "Infoblox::DNS::Record::SRV", } ; my $otype = $olist->{"$type"}; # wildcard the names &debug_cli(2,"showr n($name) v($view) t($type) ot($otype)"); if ( $otype ) { # get the host my %opthash = ( "object" => $otype, "name" => $name, ); if ( $view ) { $opthash{'view'} = $view ; } # now add IPAM info to filter the search my @ipam = $line =~/ info (\S+)/g ; if ( @ipam ) { delete $opthash{'name'} ; my $exts = __info_to_ipamhash( @ipam ); $opthash{'extensible_attributes'} = $exts ; } # hack for PTR records if ( $type =~ /ptr/i ) { delete $opthash{'name'} ; if ( $name =~ /$REip/ ) { $opthash{'ipv4addr'} = $name ; } elsif ( $name =~ /::/ ) { $opthash{'ipv6addr'} = $name ; } else { $opthash{'ptrdname'} = $name ; } } print "Search records :\n"; my @records = &do_func("search", %opthash ); &dump_object ( @records ) ; unless ( $otype =~ /ptr|cname|bulk/i ) { # and do the search again for shared records $otype=~s/::Record/::SharedRecord/; $opthash{'object'} = $otype; print "Search shared records :\n"; my @srecords = &do_func("search", %opthash ); &dump_object ( @srecords ) ; } # print "Results :\n"; # # report everything # &dump_object ( @records , @srecords ) ; } } =head2 Showing leases show network lease 1.1.1.1 shoe network lease aa:bb:cc:11:22:33 Show ALL leases show network lease =cut sub cli_show_lease { # my ( $line ) = @_ ; # get the args from the line my ( $item ) = $line =~/ lease (\S+)/ ; # is it IP or mac... my $stype ; if ( $item =~ /:/ ) { $stype = "mac" } else { $stype = "ipv4addr" } &debug_cli(2,"showl l($item) t($stype)"); # get all the leases my %opthash = ( "object" => "Infoblox::DHCP::Lease", # $stype => $item, ); # do we limit our search ? if ( $item ) { $opthash{$stype} = $item ; } my @leases = &do_func("search", %opthash ); &dump_object ( @leases ) ; } =head2 Showing networks show network show network 45.200.100.128/25 Show all the networks that have a common network_container show network 45.0.0.0/16 subnets Show networks with their details show network details Show networks matching an Extensible Attribute, you can use multiple 'info' arguments show network info Site=west show network info Site=west info Closet=24 And show the detailed version of the above searches show network info Site=west details =cut sub cli_show_network { # # try and show a network my ( $line ) = @_ ; # get the args from the line my ( $net ) = $line =~/ network (\S+)/ ; my ( $parent ) = $line =~/\bsubnets\b/ ; my ( $details ) = $line =~/\bdetails\b/ ; # set a default search my $otype = "Infoblox::DHCP::Network"; # do we want a detailed listing ? if ( $net eq "details" ) { $details = $net ; undef $net ; } my %opthash ; # now add IPAM info to filter the search my @ipam = $line =~/ info (\S+)/g ; if ( $net eq "info" ) { undef $net ; if ( @ipam ) { my $exts = __info_to_ipamhash( @ipam ); $opthash{'extensible_attributes'} = $exts ; } } # do we get all nets my $search = ".*"; # or a specific list if ( $net ) { $search = $net ; } &debug_cli(2,"shown n($net) c($parent) i(@ipam)"); # NOW get all the networks print "Search : $otype:\n"; $opthash{"object"} = $otype ; if ( $parent ) { $opthash{'network_container'} = $search; # $opthash{'network'} = ".*"; # $opthash{'view'} = 'default'; } else { $opthash{'network'} = $search; } # print Dumper ( \%opthash ) ; # set network views... my ( $view ) = $line =~/ view (\S+)/ ; $view = decode_comment( $view ) ; if ( $view ) { $opthash{'network_view'} = $view ; } # $obj = add_ipam( $obj , @ipam ) ; # search networks my @nets ; my @subnets = &do_func("search", %opthash ); push @nets , @subnets ; # then switch to search for containers.. $otype = "Infoblox::DHCP::NetworkContainer"; print "Search : $otype:\n"; if ( require_api ( "4.3r4-0" ) ) { # and all the containers $opthash{'object'} = $otype; my @parents = &do_func("search", %opthash ); push @nets , @parents ; } # dump the network(s) or just the list if ( $net or $details ) { # &dump_object ( shift @nets ) ; &dump_object ( @nets ) ; } else { # sput a sumary list my @netlist ; my $num = @nets ; foreach my $nobj (@nets) { my $name = $nobj->network(); my $comm = $nobj->comment(); my $view ; if ( require_api ( "4.3r5-0" ) ) { $view = $nobj->network_view->name();; } $name = " $name ($comm) ($view)"; if ( ref($nobj) =~ /NetworkContainer/ ) { $name =~ s/^ /+/; } # get the membership as well foreach my $sobj ( @{ $nobj->{'members'} } ) { my $sname = $sobj->{'name'}; my $ip = $sobj->{'ipv4addr'}; # and append this as a string my $mname = sprintf ( "%s(%s)", $sname,$ip ) ; $name .= ", $mname"; } # save for a sorted list push @netlist , " $name\n"; } # now print them (sorted) if ( @netlist and ! $details ) { print sort by_ip @netlist ; print "$num networks found\n "; } } } =head2 Showing shared networks Show All shared networks show network shared Show a specifc shared network show network shared myShared show template network =cut sub cli_show_network_shared { # # try and shopw a network my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ shared (\S+)/ ; &debug_cli(2,"showns t($name)"); # do we get all nets my $search = ".*"; # or a specific list if ( $name ) { $search = $name ; } # get all the stiff my %opthash = ( "object" => "Infoblox::DHCP::SharedNetwork", "name" => $search, ); my @nets = &do_func("search", %opthash ); # dump the network(s) or just the list if ( $name ) { &dump_object ( @nets ) ; # dump_object strips 'networks' for some reason, so we add it back in # here my @nlist = map { $_->network() } @{ $nets[0]->networks() } ; my $netnames = "@nlist"; printf ( "%30s : %s\n", "networks" , $netnames ) ; } else { # sput a summary list # make a hash so we can sort it later, my %nhash ; foreach my $nobj ( @nets ) { my $name = $nobj ; my @nlist = map { $_->network() } @{ $nobj->networks() } ; $nhash{ $nobj->name() } = "@nlist" ; } # print Dumper ( \%nhash ); # now sort them foreach my $nkey ( sort keys %nhash ) { print " $nkey: $nhash{$nkey}\n"; } } } =head2 Showing networks templates show template network show template network =cut sub cli_show_network_template { # # try and shopw a network my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ network (\S+)/ ; &debug_cli(2,"shownt t($name)"); # do we get all nets my $search = ".*"; # or a specific list if ( $name ) { $search = $name ; } # get all the templates my %opthash = ( "object" => "Infoblox::DHCP::NetworkTemplate", "name" => $search, ); my @nets = &do_func("search", %opthash ); # dump the network(s) or just the list if ( $name ) { &dump_object ( @nets ) ; } else { # sput a sumary list my @names = map ( " " . $_->name() , @nets ) ; my $sorted = join ( "\n" , sort @names ) ; print "$sorted\n"; } } =head2 Showing Network Views Show all views show network view Show just One view show network view default =cut sub cli_show_network_views { # my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ views (\S+)/ ; $name = decode_comment( $name ) ; &debug_cli(2,"showv t($name)"); # all net views if ( ! $name ) { # get all the views my %opthash = ( "object" => "Infoblox::DHCP::View", "name" => ".*" ); my @views = &do_func("search", %opthash ); my @vlist ; foreach my $view (@views) { my $name = $view->name(); push @vlist , " $name\n"; } print sort @vlist ; } else { # specific net view my %opthash = ( "object" => "Infoblox::DHCP::View", "name" => $name ); my ( $view ) = &do_func("get", %opthash ); &dump_object ( $view ) ; } } =head2 Showing network failover show network failover =cut sub cli_show_failover { # # try and show a failover my ( $line ) = @_ ; # get the args from the line my ( $net ) = $line =~/ network (\S+) failover/ ; &debug_cli(2,"netfail n($net)"); # do we get all nets my $search = ".*"; # or just one net if ( $net ) { $search = $net ; } # get all the networks my %opthash = ( "object" => "Infoblox::DHCP::FailOver", "name" => $search, ); my @results = &do_func("search", %opthash ); # dump the network or all the list &dump_object ( @results ) ; } =head2 Showing network option definitions (grid level) show network options =cut sub cli_show_dhcp_options { # # try and show a failover my ( $line ) = @_ ; # # get the args from the line # my ( $net ) = $line =~/ network (\S+) failover/ ; # # &debug_cli(2,"netfail n($net)"); # # do we get all nets # my $search = ".*"; # # or just one net # if ( $net ) { $search = $net ; } # get all the networks my %opthash = ( "object" => "Infoblox::DHCP::OptionDefinition", name => ".*", # "name" => $search, ); my @results = &do_func("get", %opthash ); # dump the network or all the list &dump_object ( @results ) ; } =head2 Showing network statistics show network statistics show network 45.200.100.128/25 statistics =cut sub cli_show_netstats { # # cli_show_network_stats # try and shopw a network my ( $line ) = @_ ; # get the args from the line my ( $net ) = $line =~/ network (\S+)/ ; &debug_cli(2,"netstat n($net)"); # do we get all nets my $search = ".*"; # or just one net unless ( $net =~ /statistics/ ) { $search = $net ; } # get all the networks my %opthash = ( # "object" => "Infoblox::DHCP::Statistics", "object" => "Infoblox::IPAM::Statistics", "network" => $search, ); my @nets = &do_func("get", %opthash ); # dump the first stat in the list (they are all the same) &dump_object ( shift @nets ) ; } =head2 Showing network IPAM List ALL the used and unused addresses in a subnet show network 45.200.100.128/25 ipam Show just the used addresses show network 45.200.100.128/25 ipam used Show just the used or free addresses show network 45.200.100.128/25 ipam unused Show just the next available IP address show network 45.200.100.128/25 ipam next_available OR the next N addresses show network 45.200.100.128/25 ipam next_available 5 Show just the next available network show network 45.200.100.128/25 ipam next_network =cut sub cli_show_ipam { my ( $line ) = @_ ; # get the args from the line my ( $subnet ) = $line =~/ network (\S+)/ ; my ( $type ) = $line =~/ ipam (\S+)/ ; &debug_cli(2,"netipam n($subnet) t($type)"); unless ( require_api ( "5.3" ) ) { # call the original version of this command __old_show_ipam( $line ); return; } # so re-write all this code to just use infoblox::ipam::address # objects (and a few other things) if ( $type =~ /next_network/ ) { unless ( require_api ( "6.3" ) ) { print " Next Network not available in this version of NIOS\n"; } else { my ( $cidr ) = $line =~/ next_network (\S+)/ ; $cidr =~ s/\///; &debug_cli(2,"netipam n($subnet) c($cidr)"); my ( $nobj ) = &do_func("get", ( object => "Infoblox::DHCP::Network", network => $subnet, ) ); unless ( $nobj ) { # try and get a container... ( $nobj ) = &do_func("get", ( object => "Infoblox::DHCP::NetworkContainer", network => $subnet, ) ); } if ( $nobj ) { # get the first one, we return a STRING if we don't # specify the # of networks my $nnet = $nobj->next_available_network( # requested_num => 1, cidr => $cidr ); unless ( $nnet ) { print "Error: " . $SESSION->status_detail() . "\n"; } else { print " $subnet : next avaiable : $nnet\n"; } } } return ; } if ( $type =~ /next_available/ ) { # the min rev for this part of the code is 5.3, so # next_available is already there my ( $num ) = $line =~/ next_available (\S+)/ ; $num = 1 unless $num ; &debug_cli(2,"nextip n($subnet) n($num)"); my ( $nobj ) = &do_func("get", ( object => "Infoblox::DHCP::Network", network => $subnet, ) ); if ( $nobj ) { my $nip ; if ( $num > 1 ) { $nip = $nobj->next_available_ip( requested_num => $num ); } else { $nip = $nobj->next_available_ip( ); } # string or AREF ? it depends on the # of addresses my $res ; if ( $num > 1 ) { $res = join( "," , @{ $nip }); } else { $res = $nip ; } print " $subnet : $res\n"; } return ; } # now just get uses or unused my %opthash = ( "object" => "Infoblox::IPAM::Address", "network" => $subnet, ); # see if we need to filter the addresses if ( $type ) { $opthash{status} = $type ; } # get all the matching addrs my @addrs = &do_func("get", %opthash ); # then list the results foreach my $iobj ( @addrs ) { my $ip = $iobj->ip_address(); my $status = $iobj->status(); my $types = $iobj->types(); $status .= "/$types" if $types ; print "$ip : $status\n"; } } sub __old_show_ipam { my ( $line ) = @_ ; # get the args from the line my ( $subnet ) = $line =~/ network (\S+)/ ; my ( $type ) = $line =~/ ipam (\S+)/ ; &debug_cli(2,"netipam n($subnet) t($type)"); if ( $type =~ /next_network/ ) { my ( $cidr ) = $line =~/ next_network (\S+)/ ; $cidr =~ s/\///; &debug_cli(2,"netipam n($subnet) c($cidr)"); if ( require_api ( "6.3" ) ) { my ( $nobj ) = &do_func("get", ( object => "Infoblox::DHCP::Network", network => $subnet, ) ); unless ( $nobj ) { # try and get a container... ( $nobj ) = &do_func("get", ( object => "Infoblox::DHCP::NetworkContainer", network => $subnet, ) ); } if ( $nobj ) { # get the first one, we return a STRING if we don't # specify the # of networks my $nnet = $nobj->next_available_network( # requested_num => 1, cidr => $cidr ); unless ( $nnet ) { print "Error: " . $SESSION->status_detail() . "\n"; } else { print " $subnet : $nnet\n"; } } } else { print " Next Network not available in this version of NIOS\n"; } return ; } if ( $type =~ /next_available/ ) { # just get the next IP if ( require_api ( "4.3r2-0" ) ) { my ( $nobj ) = &do_func("get", ( object => "Infoblox::DHCP::Network", network => $subnet, ) ); if ( $nobj ) { my $nip = $nobj->next_available_ip(); print " $subnet : $nip\n"; } } else { print " Next IP not available in this version of NIOS\n"; } return ; } # create an hash of used ip addrs my %used_ip ; # now get all the ranges in the subnet my @ranges = &do_func("search", ( "object" => "Infoblox::DHCP::Range", "network" => $subnet, "start_addr" => ".*", ) ); # now walk these, marking the addresses foreach my $robj (@ranges) { # we have to coerce these into relative numbers # or perl complains (arrg the overhead...) my $afip = abs_ip ( $robj->start_addr() ) ; my $alip = abs_ip ( $robj->end_addr() ) ; # walk all these addresses foreach my $arip ( 0 .. $alip - $afip ) { # convert this back to absolute my $ip = ip_abs ( $arip + $afip ) ; # mark it used $used_ip{$ip} = "used/range" ; } } # now find all the fixed addresses # we have to fix a bug in the API my $snet = $subnet ; # $snet =~ s/\//\.\*/; # my @fixed = &do_func("search", ( "object" => "Infoblox::DHCP::FixedAddr", "network" => $snet, # ** you can't use wildcard here or searches will break ** # "ipv4addr" => ".*", ) ); # walk this list, mark as used... foreach my $fobj ( @fixed ) { my $ip = $fobj->ipv4addr(); $used_ip{$ip} = "used/fixed" ; } # now walk the rest of the addresses looking for used/unused # get the first/last IP for the subnet my ( $network , $bcast ) = netbounds ( $subnet ) ; my $fabs = abs_ip( $network ) + 1 ; my $labs = abs_ip( $bcast ) - 1 ; foreach my $asip ( 0 .. $labs - $fabs ) { my $sip = ip_abs ( $asip + $fabs ) ; # see if it is in use. if ( exists $used_ip{$sip} and $used_ip{$sip} =~ /used/ ) { next ; } else { # free $used_ip{$sip} = "unused" ; } # now do some DNS lookups # if $USE_EL is set, we are a unix system my $dns ; if ( $USE_EL ) { chomp ( $dns = `host $sip $MASTER_IP` ) ; } else { # winders chomp ( $dns = `nslookup $sip $MASTER_IP` ) ; } # mark at the results if ( $dns =~ /timed out/i ) { $dns=~s/;//g; $used_ip{$sip} = "Error : $dns"; # and DONT continue last ; } # unix if ( $dns =~ /domain name/ ) { # this IP has a DNS entry $used_ip{$sip} = "used/dns" ; } # winders nslookup elsif ( ! $USE_EL and $dns =~ /name:/i ) { # this IP has a DNS entry $used_ip{$sip} = "used/dns" ; } else { # free $used_ip{$sip} = "unused" ; } } # now dump the entire list foreach my $key ( sort by_ip keys %used_ip ) { my $value = $used_ip{$key}; # filter out the lists if ( $type and $value !~ /^$type/i ) { next ; } print " $key : $used_ip{$key}\n"; } } =head2 Showing ranges Show all ranges in a network show network 45.200.100.128/25 range Show just a specific range show network 45.200.100.128/25 range 161.245.254.130 =cut sub cli_show_range { # # try and shopw a network my ( $line ) = @_ ; # get the args from the line my ( $net ) = $line =~/ network (\d+\S+)/ ; my ( $range ) = $line =~/ range (\S+)/ ; # handle a wildcard case if ( ! $range ) { $range = ".*" } &debug_cli(2,"showr n($net) r($range)"); # # To get get ALL ranges # my %opthash = ( # "object" => "Infoblox::DHCP::Range", # "start_addr" => ".*", # ); # just get a range my %opthash = ( "object" => "Infoblox::DHCP::Range", "start_addr" => $range, ); # otherwise... if ( $net ) { # get all ranges in a network ( 3.2r9 ? ) %opthash = ( "object" => "Infoblox::DHCP::Range", "network" => $net, "start_addr" => $range, ); } # network views... my ( $view ) = $line =~/ view (\S+)/ ; $view = decode_comment( $view ) ; if ( $view ) { $opthash{'network_view'} = $view ; } # get what we need my @ranges = &do_func("search", %opthash ); # dump the range or all the list if ( $range and $range ne ".*" ) { &dump_object ( shift @ranges ) ; } else { # display all my @rangelist ; foreach my $nobj (@ranges) { my $net = $nobj->network(); my $mem ; if ( $nobj->member() ) { $mem = $nobj->member()->name(); } my $fail = $nobj->failover_assoc(); my $fip = $nobj->start_addr(); my $lip = $nobj->end_addr(); push @rangelist , " $net : $fip - $lip : $mem : $fail\n"; } # now print them (sorted) print sort @rangelist ; } } =head2 Showing fixed addresses Show all fixed addresses show network 45.200.100.128/25 fixed Show all fixed addresses in a network. show network 45.200.100.128/25 fixed The Network is optional unless you want to limit your search to just that network. (The network was required in some older versions of NIOS) Show just a specific fixed address show network fixed 161.245.254.130 You can also show fidex addresses by mac address show network fixed aa:bb:cc:11:22:33 Show a fixed address(s) in a network view. (the IP or mac must come before the view statement) show network fixed view internal show network fixed 1.2.3.4 view internal show network 1.2.3.0/24 fixed view internal =cut sub cli_show_fixed { # my ( $line ) = @_ ; # get the args from the line my ( $net ) = $line =~/ network (\S+)/ ; my ( $fixed ) = $line =~/ fixed (\S+)/ ; &debug_cli(2,"showr n($net) r($fixed)"); # # To get get ALL fixed # my %opthash = ( # "object" => "Infoblox::DHCP::fixed", # "start_addr" => ".*", # ); # workaround some oddnesses in the syntax if ( $net eq "fixed" ) { undef $net } my %opthash = ( "object" => "Infoblox::DHCP::FixedAddr", # "ipv4addr" => ".*", ); # network views... my ( $view ) = $line =~/ view (\S+)/ ; $view = decode_comment( $view ) ; if ( $view ) { $opthash{'network_view'} = $view ; } if ( $fixed =~ /view/ ) { undef $fixed ; } if ( $net ) { # get all fixed in a network ( 3.2r9 ? ) # to work around a bug, we have to protect the cidr # and wildcard it (only for older code) # moved from 5.0r0-0 to 4.3r5-1 unless ( require_api ( "4.3r5-1" ) ) { $net =~ s/\//\.\*/; } $opthash{"network"} = $net ; } # if we want to be specific, we get an actual fixed if ( $fixed ) { # we check for IP or MAC if ( $fixed =~ /:/ ) { $opthash{"mac"} = $fixed ; } else { $opthash{"ipv4addr"} = $fixed ; } } # get what we need my @fixobjs = &do_func("search", %opthash ); # my @fixobjs = &do_func("get", %opthash ); # dump the fixed or all the list if ( $fixed ) { &dump_object ( @fixobjs ) ; } else { # display all my @fixedlist ; foreach my $nobj (@fixobjs) { my $net = $nobj->network(); my $ip = $nobj->ipv4addr(); my $mac = $nobj->mac(); push @fixedlist , " $net : $ip $mac\n"; } # now print them (sorted) print sort @fixedlist ; } } =head2 Showing fixed addresses templates Show all fixed addresses in a network show template fixed Show just a specific fixed address show template fixed =cut sub cli_show_fixed_template { # my ( $line ) = @_ ; # get the args from the line my ( $fixed ) = $line =~/ fixed (\S+)/ ; &debug_cli(2,"showtf r($fixed)"); my $name = ".*"; if ( $fixed ) { $name = $fixed } my %opthash = ( "object" => "Infoblox::DHCP::FixedAddrTemplate", name => $name, ); # get what we need my @fixobjs = &do_func("search", %opthash ); # dump the fixed or all the list if ( $fixed ) { &dump_object ( @fixobjs ) ; } else { # display all my @fixedlist ; foreach my $nobj (@fixobjs) { my $name = $nobj->name(); my $comment = $nobj->comment(); push @fixedlist , " $name : $comment\n"; } # now print them (sorted) print sort @fixedlist ; } } =head2 Showing MAC filters Show all macfilter lists show network filter Show the entries in a filter show network filter my_filter entries Show the details of a filter entry in a filter show network filter my_filter mac aa:aa:aa:11:22:33 =cut sub cli_show_filter { # my ( $line ) = @_ ; # get the args from the line my ( $name ) = $line =~/ filter (\S+)/ ; my ( $mac ) = $line =~/ mac (\S+)/ ; my ( $comm ) = $line =~/ comment (\S+)/ ; my ( $entries ) = $line =~/ filter \S+ (entries)/ ; &debug_cli(2,"showf n($name) e($entries) m($mac)"); # # To get get ALL filters # my %opthash = ( # "object" => "Infoblox::DHCP::Filter::MAC", # "name" => ".*", # ); my %opthash = ( "object" => "Infoblox::DHCP::Filter::MAC", "name" => ".*", ); # if we want to be specific, we get an actual filter if ( $name ) { %opthash = ( "object" => "Infoblox::DHCP::Filter::MAC", "name" => $name, ); } # are we instead searching mac addrs ? if ( $entries ) { %opthash = ( "object" => "Infoblox::DHCP::MAC", "filter" => $name, "mac" => ".*", ); } if ( $mac ) { %opthash = ( "object" => "Infoblox::DHCP::MAC", "filter" => $name, "mac" => $mac, ); } if ( $comm ) { %opthash = ( "object" => "Infoblox::DHCP::MAC", "filter" => $name, "comment" => $comm, ); } # get what we need my @filters = &do_func("search", %opthash ); # dump the filter or all the list, odd conditions here if ( $mac ) { &dump_object ( @filters ) ; } elsif ( $name and ! $entries ) { &dump_object ( @filters ) ; } else { # display all my @flist ; foreach my $nobj (@filters) { my $name ; if ( $entries ) { $name = $nobj->mac(). " : " . $nobj->comment() ; } else { $name = $nobj->name(); } push @flist , " $name\n"; } # now print them (sorted) print sort @flist ; } } # global grid settings =head2 Show global Grid settings show grid Infoblox =cut sub cli_show_grid { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/show grid (\S+)/ ; # get the grid my %opthash = ( "object" => "Infoblox::Grid", "name" => $gridname , ); my ( $grid ) = &do_func("get", %opthash ); if ( $grid ) { &dump_object ( $grid ) ; } } # global member settings =head2 Show global Member settings Get a list of all grid members show grid infoblox member Get a list of all grid members matching a REGEX show grid infoblox member Get detailed info about a member show grid Infoblox member ns1.test.com detailed Get service status or licenses for a member show grid Infoblox member ns1.test.com status show grid Infoblox member ns1.test.com licenses You can also filter this for a particular value (the value has to match something reported by the API) show grid Infoblox member ns1.test.com status memory show grid Infoblox member ns1.test.com license KeystoneDVS You can also use this to get a single status from ALL the grid members show grid Infoblox license KeystoneDVS show grid Infoblox status KeystoneDVS =cut sub cli_show_member { my ( $line ) = @_ ; # get the args from the line ( my $gridname) = $line =~/show grid (\S+)/ ; my ( $show_mems ) = $line =~/\b(member)\b/ ; my ( $member ) = $line =~/member (\S+)/ ; my ( $info ) = $line =~/\b(detailed|status|licenses)\b/ ; my ( $filter ) = $line =~/$info\s*(\S+)\b/ ; # my $detailed = $info ; # my ( $status ) = $line =~/\bstatus\b/ ; # my ( $license ) = $line =~/\blicenses\b/ ; &debug_cli (2,"sgm g($gridname) sm($show_mems) m($member) i($info) fi($filter)"); if ( $info =~ /status|licenses/ ) { unless ( require_api ( "5.0r0-0" ) ) { print " '$info' not supported in NIOS 4x\n"; } } # get all grid members my %opthash = ( "object" => "Infoblox::Grid::Member", "name" => '.*' # "Grid" => $gridname , ); # or get a member (some odd rules here): if ( $member && $member ne $info ) { # then be specific $opthash{'name'} = $member ; } # get the data my @mems = &do_func("search", %opthash ); # now walk the list of ALL the members we found and retusn the results # then do some conditional stuff foreach my $mobj ( @mems ) { my $name = $mobj->name(); if ( $info =~ /status/i ) { # get the list of statuses print_status( "$name member" , $mobj->service_status() , $filter ) ; print_status( "$name node1" , $mobj->node1_service_status() , $filter ) ; print_status( "$name node2" , $mobj->node2_service_status() , $filter ) ; } elsif ( $info =~ /license/i ) { # get the list of statuses foreach my $lobj ( @{ $mobj->node1_licenses() } , @{ $mobj->node2_licenses() } ) { if ( $filter ) { next unless $lobj->type() =~ /$filter/i } printf( "%25s %15s : %s %s : %s\n" , $name, $lobj->type(), $lobj->expiry_date(), $lobj->expiry_time(), $lobj->key(), ); # print Dumper ( $lobj ) ; } } elsif ( $info =~ /detailed/i ) { # detailed view &dump_object ( $mobj ) ; } else { my $mname = $mobj->name(); my $mip = $mobj->ipv4addr(); my $mco = $mobj->comment(); printf ( "%35s %s \%s\n", $mname , $mip , $mco ); } } } sub print_status(){ my ( $tag, $statref , $filter ) = @_ ; if ( $statref ) { foreach my $sobj ( @{ $statref } ) { # filter the results if ( $filter ) { next unless $sobj->service() =~ /$filter/i } # walk each of the statusses printf( "%35s %17s : %10s : %s\n" , $tag, $sobj->service(), $sobj->status(), $sobj->description(), ); } } } # showing users =head2 Showing users and groups show admin user show admin admin_group show admin role =cut sub cli_show_admin { my ( $line ) = @_ ; my ( $type ) = $line =~/^show admin (\S+)/ ; my ( $name ) = $line =~/^show admin \S+ (\S+)/ ; $name = decode_comment( $name ) ; &debug_cli (2,"show adm t($type) u($name)"); my %opthash ; # do we get all values my $search = ".*"; # or just one value if ( $name ) { $search = $name ; } if ( $type eq "user" ) { %opthash = ( "object" => "Infoblox::Grid::Admin::User", "name" => $search , ); } elsif ( $type eq "role" ) { %opthash = ( "object" => "Infoblox::Grid::Admin::Role", "name" => $search , ); } else { %opthash = ( "object" => "Infoblox::Grid::Admin::Group", "name" => $search , ); } my @results ; if ( $search =~ /\*/ ) { ( @results ) = &do_func("search", %opthash ); } else { ( @results ) = &do_func("get", %opthash ); } # walk the results &dump_object ( @results ) ; } # # SERVER CONNECTION STUFF..... # =head1 Server Management =head2 Connect to a server conf server user password Select a server to talk to server ? > conf server 10.0.1.230 10.0.1.20 > Supply the username and or password server ? > conf server 10.0.1.230 user admin pass infoblox admin@10.0.1.20 > =cut sub cli_add_server { my ( $line ) = @_ ; # get the args from the line ( my $server) = $line =~/server (\S+)/ ; ( my $user) = $line =~/user (\S+)/ ; ( my $passwd) = $line =~/password (\S+)/ ; ( my $master) = $line =~/master (\S+)/ ; &debug_cli (2,"add svr s($server) u($user) p($passwd)"); ######################## # check that the end device is EVEN present, # the infoblox API has a long timeout, so we will # short curcuit this with an LWP call my $timeout = 10 ; my $ua = LWP::UserAgent->new; my $request = new HTTP::Request('GET', "https://$server/"); my $lerr ; # From the LWP FAQ, but needed to be changed eval { local $SIG{ALRM} = sub { die "timeout exceeded\n" }; alarm $timeout; my $response = $ua->request($request); alarm 0; if ( $response->is_error ) { $lerr = $response->status_line; die $lerr; } }; if ( $@ ) { print " Error : server $server is not responding : $lerr\n"; exit ; } # otherwise, go on # set the grid master IP if ( $master ) { &cli_add_master ( "configure master $master" ) ; } else { &cli_add_master ( "configure master $server" ) ; } # create the session handler $SESSION = Infoblox::Session->new( "master" => $server, "username" => $user, "password" => $passwd , "timeout" => 36000 , ); print Dumper ( \$SESSION ) if $DEBUG >= 2 ; if ( $SESSION->status_code() ) { my $result = $SESSION->status_code(); my $response = $SESSION->status_detail(); print " Error : $response ($result)\n"; } else { $ONLINE = 1 ; $PROMPT = "$user\@$server > " ; # check for editline if ( $USE_EL ) { $el->set_prompt ( $PROMPT ); } else { print "$PROMPT"; } } } =head2 connecting to the MGMT (management) port If you are connecting to the MGMT port, you need to define the GRID MASTER IP address. This is because it is different from the MGMT port and it is autoassigned to any zones, networks or ranges that do not specifically have a member assignment configure master Alternatively, you can add it as a keyword to the 'conf server' command : conf server 10.0.1.230 user admin pass infoblox master 45.0.12.20 You can also do this from the command line : ibcli -m =cut sub cli_add_master { my ( $line ) = @_ ; # get the args from the line ( my $master) = $line =~/configure master (\S+)/ ; # print "Using Grid Master [$master]\n"; # set the global $MASTER_IP = $master ; } =head2 Show server details show server version show server error show server message =cut sub cli_show_server { my ( $line ) = @_ ; # get the status if ( $SESSION ) { my $result = $SESSION->status_code(); my $response = $SESSION->status_detail(); my $rev = $SESSION->server_version(); # get the args from the line if ( $line =~ /version/ ) { print " Server Version : $rev\n"; } } } =head2 Restarting services the cli will not restart services, you have to do that manually restart dns restart dhcp IF you want to delay the restart you can do that also restart dns delay 10 =cut sub cli_restart { my ( $line ) = @_ ; # get the args from the line ( my $service) = $line =~/restart (\S+)/ ; ( my $delay) = $line =~/delay (\S+)/ ; ( my $member) = $line =~/member (\S+)/ ; ( my $test) = $line =~/(test_dhcp)/ ; &debug_cli (2,"restart s($service) d($delay)"); # my %opthash ; # $opthash{'service'}=$service; # $opthash{'when'}=0; my %opthash = ( "service" => $service , ); unless ( require_api ( "5.0r0-0" ) ) { $opthash{'when'}="now"; } if ( $member ) { $opthash{'member'}=$member; } if ( $test ) { $opthash{'test_restart'}="yes"; } if ( $delay ) { $opthash{'delay'}=$delay; $opthash{'delay_between_members'}=$delay; } my @function=("restart",%opthash); &do_func(@function); } =head2 Starting Discovery jobs This will just start a discovery job if one is not already running restart discovery You can't control the member from a restart, you have to do that from the discovery job =cut sub cli_restart_discovery { my ( $line ) = @_ ; # ( my $service) = $line =~/restart (\S+)/ ; # ( my $member) = $line =~/member (\S+)/ ; # check to see if it is already running... my ( $dobj ) = do_func('get', "object" => "Infoblox::IPAM::DiscoveryTask" ); if ( $dobj->state() =~ /running/i ) { print "Discovery task is already RUNNING\n"; return ; } # # otherwise try and start/stop a discovery my ( $dres ) = do_func('network_discovery_control', action => "START", ); } =head2 Download csv data for an object type download csv object download csv zonelist.csv object Infoblox::DNS::Zone =cut =head2 Export log files download merge_log download log_files member =cut sub cli_download_log { # we can probably make this generic # ... my ( $line ) = @_ ; # get the args from the line ( my $type ) = $line =~/download (\S+)/ ; ( my $dest ) = $line =~/$type (\S+)/ ; ( my $member) = $line =~/member (\S+)/ ; ( my $object) = $line =~/object (\S+)/ ; ( my $subtype) = $line =~/$type \S+ (\S+)/ ; ( my $syslog) = $line =~/\bsyslog\b/ ; my %opthash = ( "path" => $dest , "type" => $type , ); if ( $type =~ /log/ ) { $opthash{'format'} = 'tar'; $opthash{'log_type'} = $subtype ; } if ( $object ) { $opthash{'object_type'} = $object ; $opthash{'separator'} = 'comma' ; } if ( $syslog ) { } # grab a log from a member if ( $member ) { $opthash{'member'} = $member ; } print "DOWNLOADING $type as $dest ...\n"; my @function=("export_data",%opthash); &do_func(@function); print "DONE\n"; } =head2 Download the database download database =cut sub cli_download_database { my ( $line ) = @_ ; # get the args from the line ( my $dest) = $line =~/database (\S+)/ ; my %opthash = ( "path" => $dest , "type" => "backup", "format" => "tar", ); print "DOWNLOADING ALL DATABASE data as $dest ...\n"; my @function=("export_data",%opthash); &do_func(@function); print "DONE\n"; } =head2 Restore the database This will always force a restore and KEEP the current IP settings (the API and GUI has other options, but I can't see why you'd need them) upload database =cut sub cli_upload_database { my ( $line ) = @_ ; # get the args from the line ( my $dest) = $line =~/database (\S+)/ ; my %opthash = ( "type" => "backup", "force" => "true", "path" => $dest, "keep_grid_ip" => "true", ); print "RESTORING ALL DATABASE data from $dest ...\n"; my @function=("import_data",%opthash); &do_func(@function); print "DONE\n"; } =head2 Upload lease data upload leases =cut sub cli_upload_file { my ( $line ) = @_ ; # get the args from the line my ( $type , $dest) = $line =~/upload (\S+) (\S+)/ ; my %opthash = ( "type" => $type, "path" => $dest, ); if ( $type =~ /csv/ ) { $opthash{'continue_on_error'}='true'; $opthash{'operation'}='insert'; } print "Uploading $type from $dest ...\n"; my @function=("import_data",%opthash); &do_func(@function); print "DONE\n"; } =head2 Download the DHCP configuration download dhcp_conf =cut sub cli_download_dhcp { my ( $line ) = @_ ; # get the args from the line ( my $dest) = $line =~/dhcp_conf (\S+)/ ; ( my $member) = $line =~/member (\S+)/ ; ( my $type) = $line =~/(expert_dhcp_conf)/ ; # set the defaults ; my %opthash = ( "path" => $dest , ); # pick the member if ( $member ) { # $opthash{'member'} = $member ; $opthash{'member'} = $member ; } else { # $opthash{'member'} = $MASTER_IP ; $opthash{'member'} = $MASTER_IP ; } # pick the file type if ( $type eq "expert_dhcp_conf" ) { $opthash{"type"} = "dhcp_expert_mode_config", } else { $opthash{"type"} = "dhcpd_conf", } print "DOWNLOADING ALL dhcpd.conf data as $dest ...\n"; my @function=("export_data",%opthash); &do_func(@function); print "DONE\n"; } =head2 Upload the DHCP expert mode configuration upload expert_dhcp_conf Or you can push the conf file to a specific member : upload expert_dhcp_conf member =cut sub cli_upload_dhcp { my ( $line ) = @_ ; # get the args from the line ( my $dest) = $line =~/dhcp_conf (\S+)/ ; ( my $member) = $line =~/member (\S+)/ ; my %opthash = ( "path" => $dest , "type" => "dhcp_expert_mode_config", ); # pick the member if ( $member ) { # $opthash{'member'} = $member ; $opthash{'virtual_ip'} = $member ; } else { # $opthash{'virtual_ip'} = $MASTER_IP ; $opthash{'member'} = $MASTER_IP ; } print "UPLOADING dhcpd.conf data as $dest ...\n"; my @function=("import_data",%opthash); &do_func(@function); print "DONE\n"; } =head2 Browse and load a datbase backup configure file load =cut sub cli_load_db { my ( $line ) = @_ ; # get the args from the line ( my $file) = $line =~/load (\S+)/ ; # untar the file my $tmp_xml = ".ibcli-ondeb.xml"; print "Unpacking the database backup...\n"; require Archive::Tar; my $tar = Archive::Tar->new ; $tar->read($file) or die "Cannot load database : $file"; $tar->extract_file ( 'onedb.xml' , $tmp_xml ); open ( DB , $tmp_xml ) or print "Cannot load file '$file'\n"; # reset the count $SDBCOUNT = 0 ; # now read the onedb.xml and store it in a hash while () { next if /^ { '.com.infoblox.one.virtual_node' => { }, }, dns => { zones => { '.com.infoblox.dns.zone' => { hosts => "", records =>"", }, }, '.com.infoblox.dns.cluster_dns_properties' => { '.com.infoblox.dns.member_dns_properties' => { } }, }, network => { subnets => { '.com.infoblox.dns.network' => { ranges => "", fixed_addr =>"", }, }, '.com.infoblox.dns.cluster_dhcp_properties' => { '.com.infoblox.dns.member_dhcp_properties' => { } }, shared_networks => {}, filters => {}, }, dhcp => { }, aaa => { }, admins => { }, ); # and set the pointer ; $CWD = \%SCHEMA ; return ; } =head2 show the contents of a file To get the database summary show file summary to lost all the nodes in a path show file path To show the details of a path show file properties show file properties =cut # debug state sub debug_file { # dump all types in the sdb my $rec_count ; foreach my $type ( sort keys %SDB ) { my $count = @{ $SDB{$type} }; $rec_count += $count ; print "$count\t$type\n"; } print "$rec_count\t Total records in database\n"; } sub debug_pwd { print "otype($COT) path [@PATH]\n"; print Dumper ( $CWD ) ; } sub cli_show_date { chomp( my $date = `date`); print "$date\n"; } sub cli_show_file { my ( $line ) = @_ ; # get the args from the line ( my $type ) = $line =~/^show file (\S+)/ ; ( my $value ) = $line =~/^show file \S+ (\S+)/ ; # no do some simple things if ( $type eq "summary" ) { # dump a summary print " Total records in database: $SDBCOUNT\n"; # now walk all the vnodes foreach my $obj ( @{ $SDB{'.com.infoblox.one.virtual_node'} } ) { my $name = $obj->{'host_name'} ; my $ip = $obj->{'virtual_ip'} ; print " Member : $name \t$ip\n"; } } # show file properties ( == cat ) if ( $type eq "properties" ) { # show something # show the CWD, # later accept an additional path # ( this requires object recognition, errk) # if ( $value ) { # } # we use the current object type to get what we want my $name = $PATH[-1]; # and call dumpsdb &dump_sdb ( $name , $COT ) ; } # show file nodes ( == ls ) if ( $type eq "nodes" ) { # do an ls of the current schema position # from the CDW pointer foreach my $node ( sort keys %{ $CWD } ) { if ( $node =~ /com.infoblox/ ) { # we either dump a set of DB objects foreach my $obj ( sort @{ $SDB{$node} } ) { &list_sdb ( $node , $obj ) ; } # need to sort these } else { # or we dump a list of nodes. print " $node\n"; } } # and somehow we push the stack if it is a node } # show file path ( == pwd ) if ( $type eq "path" ) { my $pwd = join ( "/" , @PATH ) ; print " /$pwd\n"; } } sub list_sdb { # print a name of an object # (ls file) (ls list) my ( $type , $obj ) = @_ ; my $name ; # and do some fudging if ( $type eq ".com.infoblox.dns.zone" ) { # we have to join parent and child my $zone = join ( "." , reverse ( split ( /\./, $obj->{'zone'}))); $name = join ( "." , reverse ( split ( /\./, $obj->{'name'}))); $name = "$name.$zone"; # $name = $obj->{'name'}; } elsif ( $type eq ".com.infoblox.dns.cluster_dns_properties" ) { # get the gridname ; $name = $SDB{'.com.infoblox.one.cluster'}[$obj->{cluster}]->{name}; } elsif ( $type eq ".com.infoblox.dns.cluster_dhcp_properties" ) { # get the gridname ; $name = $SDB{'.com.infoblox.one.cluster'}[$obj->{cluster}]->{name}; } elsif ( $type eq ".com.infoblox.dns.member_dns_properties" ) { # find this vnode my ( $mem ) = &find_sdb ( $obj->{'virtual_node'} , 'virtual_oid' , @{ $SDB{'.com.infoblox.one.virtual_node'} } ); $name = $mem->{'host_name'}; # and hack this object to have a name } elsif ( $type eq ".com.infoblox.one.virtual_node" ) { $name = $obj->{'host_name'}; } else { $name = $obj->{'name'}; } print " $name\n"; } sub dump_sdb { # dump a particular node in the database my ( $search , $type ) = @_ ; print "Properties for $search : ($COT)\n"; my @olist ; # this gets really messy for different object types if ( $type eq ".com.infoblox.dns.zone" ) { # unravel the name back to the sdb version my $name = join ( "." , reverse ( split ( /\./, $search ))); # go find it, push @olist , &find_sdb ( $name , 'name' , @{ $SDB{$type} } ); # my ( $zone ) = &find_sdb ( $name , 'name' , @{ $SDB{$type} } ); # now find any nameservers push @olist , &find_sdb ( $name , 'zone' , @{ $SDB{'.com.infoblox.dns.bind_ns'} } ); push @olist , &find_sdb ( $name , 'zone' , @{ $SDB{'.com.infoblox.dns.bind_soa'} } ); push @olist , &find_sdb ( $name , 'zone' , @{ $SDB{'.com.infoblox.dns.zone_ext_primary'} } ); push @olist , &find_sdb ( $name , 'zone' , @{ $SDB{'.com.infoblox.dns.zone_cluster_secondary_server'} } ); push @olist , &find_sdb ( $name , 'zone' , @{ $SDB{'.com.infoblox.dns.zone_ext_secondary_server'} } ); } elsif ( $type eq ".com.infoblox.dns.cluster_dns_properties" ) { # we have to back-track the search string # by getting the oid of the gridname # which we ALWAYS assume is '0' push @olist , &find_sdb ( '0' , 'cluster' , @{ $SDB{'.com.infoblox.dns.cluster_dns_properties'} } ); # and get the ACLS push @olist , &find_sdb ( '0' , 'cluster_dns_properties' , @{ $SDB{'.com.infoblox.dns.cluster_rqacl_item'} } ); } elsif ( $type eq ".com.infoblox.dns.member_dns_properties" ) { push @olist , &find_sdb ( $search , 'virtual_node' , @{ $SDB{$type} } ); } elsif ( $type eq ".com.infoblox.one.virtual_node" ) { push @olist , &find_sdb ( $search , 'host_name' , @{ $SDB{$type} } ); } else { # generic object push @olist , &find_sdb ( $search , 'name' , @{ $SDB{$type} } ); } # print all the stuff to a single pager open ( LESS , "| less" ) ; foreach my $pobj ( @olist ) { # print it out print LESS Dumper ( $pobj ) ; } close LESS ; } sub find_sdb { # this will search an array of objects and return any matches my ( $search , $field , @olist ) = @_ ; debug_cli(2,"find sdb ( $search , $field , @olist )"); my @results ; foreach my $obj ( @olist ) { # is the regex matches the named field # if ( $obj->{$field} =~ /^$search$/ ) { if ( $obj->{$field} =~ /$search$/ ) { push @results , $obj ; } } return @results ; } =head2 Change the path of the current node conf file path /path/to/node =cut sub cli_set_path { # conf file path xxx ( == cd ) my ( $line ) = @_ ; # get the args from the line ( my $newpath ) = $line =~/file path (\S+)/ ; # then support '/' and absolute if ( $newpath =~ /^\// ) { # we just reset the CWD to the top $CWD = \%SCHEMA ; undef @PATH ; # clean the input $newpath =~ s/^\///; } # now break the path into args foreach my $dir ( split /\// , $newpath ) { debug_cli(2,"chdir ($dir) [$newpath]"); # support '..' # we go up if ( $dir eq ".." ) { # go up pop @PATH ; # and since we didn't maintain state, # just walk back down to get the right node $CWD = \%SCHEMA; foreach my $n ( @PATH ) { $CWD = $CWD->{$n}; } } # relative 'foo' # we go down , elsif ( $CWD->{$dir} ) { $CWD = $CWD->{$dir}; # and push the stack push @PATH , $dir; } elsif ( grep ( /com.infoblox/ , keys %{ $CWD } ) ) { # this node contains DB objects, do the push # by popping to the first dbobject in the stack # [ ] we wil have the fix this later my @dbtypes = keys %{ $CWD } ; # find out where we should push down to # find out where we should push down to my $ndir = shift @dbtypes ; $CWD = $CWD->{$ndir}; # but we make the path look correct push @PATH , $dir; # and save the current object type $COT = $ndir ; } else { print "no such node : $dir\n"; } }; } ########################################### # # END API calling functions. # ########################################### sub require_api { # this is a quick and dirty version checker # that should hopefully break out of a subroutine # # return unless require_api ( "4.1r2-0" ); # require_api ( "4.1r2-0" ) or return ; # GET/store the API version # my $host = Infoblox::DNS::Host->new ("name" => "name",); # my $CLIENT_REV = $host->{__version} ; my ( $needed ) = @_ ; my $oreq = $needed ; my $rev = $CLIENT_REV ; if ( $SESSION ) { debug_cli(2,"Checking Server Version"); $rev = $SESSION->server_version(); } # curiously, we can convert this to an IP address and $rev =~ s/(r\d+-\d+).*/$1/; $rev =~ s/\D/\./g; $needed =~ s/(r\d+-\d+).*/$1/; $needed =~ s/\D/\./g; # then just compare it # if ( abs_ip ( $needed ) >= abs_ip ( $rev ) ) { if ( abs_ip ( $rev ) < abs_ip ( $needed ) ) { debug_cli(1,"version too low : need($needed) server($rev)"); return ( 0 ) ; } else { debug_cli(1,"version OK : need($needed) server($rev)"); return ( 1 ) ; } } sub decode_comment { # urldecode a comment string my ( $string ) = @_ ; if ( $string ) { $string =~ s/%(..)/chr ( hex($1) )/ge; # remove leading and training " $string =~ s/^"//; $string =~ s/"$//; # also undo quotes in inside the comment $string =~ s/''/"/g; # lastly utf encode the string $string = encode ( "utf8", $string ); debug_cli(2,"dec comment ($string) "); return ( $string ) ; } else { return ( "" ) ; } } sub escape_dots { # return a string with 'literal' dots, for searches my ( $string ) = @_ ; $string =~ s/\./\\./g ; return ( $string) ; } sub dump_summ { # return a summary by the named field my ( $field , @olist ) = @_ ; my @results ; foreach my $obj ( @olist ) { push @results , $obj->$field(); } # print " @results\n"; return ( "@results" ); } sub dump_object { # wrapper to hold arrays foreach my $obj ( @_ ) { if ( $#_ > 0 ) { print "--- ---\n"; } &dump_data ( $obj ) ; } } sub dump_data { # pretty print an object my ( $obj ) = @_ ; print Dumper ( \$obj ) if $DEBUG > 3 ; # identify networks or containers if ( ref($obj) ) { if ( ref($obj) =~ /Infoblox.DHCP.NetworkContainer/ ) { printf ( "%30s : %s\n", "type" , "NetworkContainer" ) ; } if ( ref($obj) =~ /Infoblox.DHCP.Network/ ) { printf ( "%30s : %s\n", "type" , "Network" ) ; } } # nasty hack to insert the hostname into a fixed address if ( ref($obj) =~ /Infoblox.DHCP.FixedAddr/ ) { # get the name (fixed hosts do not have '/' in them) # unless ( $obj->__key() =~ /\// ) { # my $name = $obj->__key(); unless ( $obj->{'__key'} =~ /\// ) { my $name = $obj->{'__key'} ; # and clean it up, reverse the parts etc $name =~ s/.$REip$//; $name =~ s/^._(\w+).//; $name = join ( "." , reverse split ( /\./ , $name ) ) ; printf ( "%30s : %s\n", "name" , $name ) ; } } foreach my $key ( sort keys %{ $obj } ) { # ignore privates next if $key =~ /^__/; next if $key =~ /FUNCTION/; next if $key =~ /CHANGED/; next if $key =~ /RETURN_FIELDS/; # and some stuff we view through other processes next if $key =~ /service_status/; next if $key =~ /license/; next if $key =~ /networks/; next if $key =~ /tcp_ports/; # and undef but not 0 values unless ( exists $obj->{$key} ) { next ; } # if ( exists $obj->{$key} ) { # unless ( $obj->{$key} eq "0" ) { # next if ! $obj->{$key} ; # } # } my $data ; # get the value for grins my $ktype = $obj->{$key}; # look for sub objects by key 'name' if ( $key eq "primary" or $key eq "member" or $key eq "network_view" or $key eq "secondary" ) { # get the name my $sobj = $obj->{$key} ; # find out if we have an array or not # (this is the case for external primaries) if ( ref($sobj) eq 'ARRAY' ) { $sobj = @{ $sobj } [0] } # print "k[$key]\n"; # print Dumper ( $sobj ) ; $data = $sobj->{'name'}; } # special handling for joining 2 objects elsif ( $key eq "end_addr" ) { # get the 2 values $data = $obj->{'start_addr'} . " - " . $obj->{'end_addr'} ; $key = "range"; delete $obj->{'start_addr'}; } # arrays by just a value elsif ( $key eq "ntp_access_list" or $key eq "additional_ip_list" or $key eq "allow_query" or $key eq "allow_update" or $key eq "ospf_list" or $key eq "match_clients" or $key eq "match_client_keys" or $key eq "ntp_authentication_key" # or $key eq "permission_list" or $key eq "shared_record_groups" or $key eq "trap_receiver" or $key eq "nat_group_list" # or $key eq "ipv4addrs" or $key eq "aliases" ) { my @values = @{ $obj->{$key} } ; # remember it $data = "@values"; # $data = join " " , @values ; } # extensible attributes elsif ( $key eq "extensible_attributes" ) { my @values; foreach my $field ( keys %{ $obj->{$key} } ) { my $val = $obj->{$key}{$field}; # re-assemble arrays if ( $val =~ /ARRAY/ ) { $val = join( "," , @{$val}) } # push @values , "$field=$val"; # print the line directly printf ( "%35s : %s=%s\n", $key , $field,$val ) ; } # # remember it # $data = "@values"; } # host ipaddress can have nested fixed addresses, arrg elsif ( $key eq "ipv4addrs" ) { my @values; foreach my $ip ( @{ $obj->{$key} } ) { if ( $ip =~ /HASH/ ) { my $fip = $ip->{'ipv4addr'}; my $mac = $ip->{'mac'}; push @values , "$fip,$mac"; } else { push @values , $ip ; } } # remember it $data = "@values"; } # permission lists are just plain weird elsif ( $key eq "permission_list" ) { my @values; foreach my $perm ( @{ $obj->{$key} } ) { my $type = ref($perm); $type =~ s/.com.infoblox.perl_api.//; $type =~ s/\./::/g; # the name changes depending on the object type my $obj ; if ( $type =~ /ViewAndZonePermission/ ) { $obj = $perm->zone(); } if ( $type =~ /NetworkPermission/ ) { $obj = $perm->network(); } # there are 2 fields here...odd my $access = $perm->operation(); if ( $perm->access() eq "Deny" ) { $access = "Deny" } push @values , "$obj=$access" ; } # remember it $data = "@values"; } # hash arrays by key 'ipv4addr' elsif ( $key eq "syslog_server" or $key eq "forward_to" # or $key eq "delegate_to" or $key eq "match_members" ) { my @values ; # find all the names only # print Dumper ( $obj->{$key} ) ; foreach my $sobj ( @{ $obj->{$key} } ) { my $name = $sobj->{'name'}; my $ip = $sobj->{'ipv4addr'}; push @values , "$name,$ip" ; } # remember it # $data = "@values"; $data = join " " , @values ; } # hash arrays by key 'value' elsif ( $key eq "list_values" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { my $name = $sobj->{'value'}; push @values , "$name" ; } # remember it $data = join " " , @values ; } # hash arrays by key 'address' elsif ( $key eq "ntp_server" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { my $name = $sobj->{'address'}; push @values , "$name" ; } # remember it $data = join " " , @values ; } # hash arrays by key 'filters' elsif ( $key eq "filters" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { my $name = $sobj->{'filter_name'}; my $perm = $sobj->{'permission'}; push @values , "$name($perm)" ; } # remember it $data = join " " , @values ; } # hash arrays (exclude_ranges) by key 'start_address' elsif ( $key eq "exclude" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { my $efip = $sobj->{'start_address'}; my $elip = $sobj->{'end_address'}; push @values , "$efip,$elip" ; } # remember it $data = "@values"; $data = join " " , @values ; } # hash arrays by key 'object_name' elsif ( $key eq "changed_objects" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { # push @values , $sobj->{'name'}; my $name = $sobj->{object_name}; my $type = $sobj->{object_type}; my $action = $sobj->{action}; push @values , "$action : $type : $name" ; } # remember it $data = "@values"; } # hash arrays by key 'name' elsif ( $key eq "zone_association" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { # push @values , $sobj->{'name'}; my $name = $sobj->{'name'}; my $view = $sobj->{views}[0]->{name}; push @values , "$name($view)" ; } # remember it $data = "@values"; } # views , by key name elsif ( $key eq "views" or $key eq "nsgroups" ) { my @values ; # find all the names only foreach my $sobj ( @{ $obj->{$key} } ) { # this is either a scalar or an object my $name = $sobj ; if ( ref ($sobj) ) { # get the sub value $name = $sobj->{'name'}; } push @values , "$name" ; } # remember it $data = "@values"; # $data = join " " , @values ; } # nested sub objects elsif ( $key eq "secondaries" or $key eq "members" or $key eq "delegate_to" or $key eq "stub_from" or $key eq "stub_members" ) { my @values ; # find all the sub objects # [ ] and group,sort them by type/name my %sdata ; foreach my $sobj ( @{ $obj->{$key} } ) { my $type = $key ; # what kind of secondary if ( ref($sobj) =~ /Infoblox.DNS.Member/ ) { $type = "member secondary" } if ( ref($sobj) =~ /Infoblox.DNS.Nameserver/ ) { $type = "external secondary" } if ( $key eq "delegate_to" ) { $type = "delegate_to" } if ( $key eq "stub_from" ) { $type = "stub_from" } if ( $key eq "stub_members" ) { $type = "stub_members" } my $name = $sobj->{'name'}; my $ip = $sobj->{'ipv4addr'}; # check for lead secondaries if ( $sobj->{'lead'} =~ /true/i ) { $type = "*LEAD $type"; } # printf ( "%30s : %s,%s\n", $type , $name,$ip ) ; my $l = sprintf ( "%s,%s", $name,$ip ) ; push @{ $sdata{$type} }, $l; } foreach my $t ( sort keys %sdata ) { foreach my $vl ( sort @{ $sdata{$t} } ) { printf ( "%30s : %s\n", $t , $vl ) ; } } } # nested DHCP options elsif ( $key eq "options" ) { # find all sub objects foreach my $sobj ( @{ $obj->{$key} } ) { my $name = $sobj->{'type'}; my $val = $sobj->{'value'}; # perhaps it is something else ? if (! $name) {$name = $sobj->{'name'} }; if (! $val) {$val = $sobj->{'seconds'} }; if (! $val) {$val = $sobj->{'name'} }; if (! $val) {$val = $sobj->{'ipv4addr'} }; if (! $val) {$val = join ( "," , @{ $sobj->{'ipv4addrs'} } ) }; if ( $val ) { printf ( "%35s : %s = %s\n", $key , $name,$val ) ; } } } # generic catchall for arrays elsif ( $ktype =~ /ARRAY/ ) { my @values = @{ $obj->{$key} } ; # remember it $data = "@values"; } # just the value m'aam else { $data = $obj->{$key} ; # clean up any odd chars in the string $data =~ s/ / /g; $data =~ s/\n/ /g; } # print it if ( $data ) { # strip 'false' settings next if $data eq 'false' ; # cleansed $data =~ s/"/"/g; printf ( "%30s : %s\n", $key , $data ) ; } } } # Setting functions # Add a seting to an object sub add_settings { my ( $obj , @settings ) = @_ ; if ( @settings ) { foreach my $setval ( @settings ) { # set some custom methods; # split on the ',' my ( $set , $val ) = $setval =~ /^(.*?)=(.*)/ ; $val = decode_comment( $val ) ; &debug_cli(2,"add setting s($set) v($val)"); # If the value is an array, we need to cast it accordingly my @avals ; if ( $val =~ /^\[/ ) { $val =~ s/\[\s*//; $val =~ s/\s*\]//; @avals = split ( /\s*,\s*/ , $val ); &debug_cli(2,"add arr v s($set) va(@avals)"); # set this object $obj->$set ( \@avals ) ; } else { # and set this on the object $obj->$set ( $val ) ; } } } return ( $obj ) ; } # IPAM functions sub __info_to_ipamhash { # convert 'info foo=bar' into a searchable hash my @ipam = @_ ; my $exts ; foreach my $info ( @ipam ) { # break this onto name = value pairs my ( $name , $value ) = $info =~ /(\S+?)=(.*)/; # decode a quoted string my $cvalue = decode_comment( $value ) ; # and decode the attribute name $name = decode_comment( $name ) ; # and strip the quotes $cvalue =~ s/^"//; $cvalue =~ s/"$//; &debug_cli(3,"convi info($name) =($cvalue) [$value]"); # and store them in the hash $exts->{$name}=$cvalue; } return ( $exts ); } sub add_ipam { my ( $obj , @ipam ) = @_ ; # now add IPAM info # and add code for 4.3rx or higher and EA attributes # we use a hash because the EAs may not always exist my $exts = {}; # get the current EA's if there are any if ( require_api ( "4.3r2-0" ) ) { if ( $obj->extensible_attributes() ) { # %exts = %{ $obj->extensible_attributes() }; $exts = $obj->extensible_attributes(); } } # add/replace any new attributes foreach my $info ( @ipam ) { # break this onto name = value pairs my ( $name , $value ) = $info =~ /(\S+?)=(.*)/; # decode a quoted string my $cvalue = decode_comment( $value ) ; # and decode the attribute name $name = decode_comment( $name ) ; # and strip the quotes $cvalue =~ s/^"//; $cvalue =~ s/"$//; &debug_cli(3,"addh info($name) =($cvalue) [$value]"); if ( require_api ( "4.3r2-0" ) ) { # if we are running 4.3, we need to use EA attributes # add them to the hash # if they are a list context, we have to convert them if ( $cvalue =~ /\[(.*)\]/ ) { my $vals = $1 ; my @a = split ( /\s*\,\s*/ , $vals ) ; # $cvalue = \@a ; $exts->{$name} = \@a ; } else { $exts->{$name} = $cvalue ; } # print Dumper ( $exts ) ; # $exts{$name} = $cvalue ; # $obj->extensible_attributes()->{$name} = $value ; } else { # convert the names to lowercase... $name = lc ( $name ) ; # and set these directly $obj->$name ( $cvalue ); } } # add the EA hash if required if ( require_api ( "4.3r2-0" ) and %{ $exts } ) { $obj->extensible_attributes( $exts ) ; } return ( $obj ) ; } # DHCP functions sub make_dhcp_option { my ( $DEBUG, $opt , $val ) = @_ ; # eat random dhcp options, # make intelligent decisions, # return an object # initially clean the value $val =~ s/"//g; $val = decode_comment( $val ) ; # strip the vendor class from the option name # we will add it back in once we've cleaned up the option my $class ; if ( $opt =~ /(.*)\.(.*)/ ) { $class = $1 ; $opt = $2 ; debug_cli(4,"mkdh: vendor class c($class) o($opt)"); } # do a lookup to get the real name, (workaround laziness) # if the option name is JUST a number, we will try that first # and use the newer calls my ( $onum , $oname ) = get_custom_options( $DEBUG , $opt ) ; debug_cli(4,"mkdh: took option o($opt) > o#($onum)on($oname)=($val)"); # this loop is the god, it renames a value and its type # it is also a list of what options we handle specifically # (as opposed to generic 'custom options') my $option ; # get the server version my $niosrev ; if ( $ONLINE ) { $niosrev = $SESSION->server_version(); } # all the routines can be replaced with a single call (finally) # as of 4.2r1 if ( require_api ( "4.2r1-0" ) ) { if ( $oname ) { $option = Infoblox::DHCP::Option->new ( "name" => $oname, "value" => $val, ); } elsif ( $opt =~ /^\d+$/ ) { $option = Infoblox::DHCP::Option->new ( "num" => $opt, "value" => $val, ); } } # then conditionally check the object and see if it it is special # or just an ordinary option # we have to convert all options to both a name and a number # this will save us pain in the long run # but we finally did this in 4.2r1. elsif ( $oname eq "routers" ) { $option = Infoblox::DHCP::Option->new ( "type" => "routers", "ipv4addrs" => [ split(/,\s*/,$val) ], ) } elsif ( $oname eq "domain-name-servers" ) { $option = Infoblox::DHCP::Option->new ( "type" => "nameservers", "ipv4addrs" => [ split(/,\s*/,$val) ], ) } elsif ( $oname eq "domain-name" ) { # conditional case for 4.1r2 options $option = Infoblox::DHCP::Option->new ( "type" => "domain-name", "name" => $val, ); # newer API call is cleaner if ( $niosrev =~ /4.1r[3-9]/ or $niosrev =~ /4.[2-9]/ ) { $option = Infoblox::DHCP::Option->new ( "name" => "domain-name", "value" => $val, ); } } elsif ( $oname eq "broadcast-address" ) { $option = Infoblox::DHCP::Option->new ( "type" => "broadcast", "ipv4addr" => $val, ) } elsif ( $oname =~ /^lease*/ ) { $option = Infoblox::DHCP::Option->new ( "type" => "lease-time", "seconds" => $val, ) } elsif ( $onum ) { # only do this if we have a real # debug_cli(4,"mkdh: generic option o($onum)=($val)"); $option = Infoblox::DHCP::Option->new ( "type" => $onum, "value" => $val, ) } else { print " Error : unknown DHCP option [$opt]\n"; } # lastly hack back in the vendor class if ( $class ) { $option->vendor_class ( $class ) ; } # return the object and the name of the object return ( $option , $oname ) ; } sub get_custom_options { # remap some variables my ( $DEBUG , $opt ) = @_ ; my %lookup = ( "subnet-mask" => "Option 1: subnet-mask", "time-offset" => "Option 2: time-offset", "routers" => "Option 3: routers", "time-servers" => "Option 4: time-servers", "ien116-name-servers" => "Option 5: ien116-name-servers", "domain-name-servers" => "Option 6: domain-name-servers", "nameservers" => "Option 6: domain-name-servers", "log-servers" => "Option 7: log-servers", "cookie-servers" => "Option 8: cookie-servers", "lpr-servers" => "Option 9: lpr-servers", "impress-servers" => "Option 10: impress-servers", "resource-location-servers" => "Option 11: resource-location-servers", "host-name" => "Option 12: host-name", "boot-size" => "Option 13: boot-size", "merit-dump" => "Option 14: merit-dump", "default-domain" => "Option 15: domain-name", "domain-name" => "Option 15: domain-name", "domain_name" => "Option 15: domain-name", "swap-server" => "Option 16: swap-server", "root-path" => "Option 17: root-path", "extensions-path" => "Option 18: extensions-path", "ip-forwarding" => "Option 19: ip-forwarding", "non-local-source-routing" => "Option 20: non-local-source-routing", "policy-filter" => "Option 21: policy-filter", "max-dgram-reassembly" => "Option 22: max-dgram-reassembly", "default-ip-ttl" => "Option 23: default-ip-ttl", "path-mtu-aging-timeout" => "Option 24: path-mtu-aging-timeout", "path-mtu-plateau-table" => "Option 25: path-mtu-plateau-table", "interface-mtu" => "Option 26: interface-mtu", "all-subnets-local" => "Option 27: all-subnets-local", "broadcast-address" => "Option 28: broadcast-address", "perform-mask-discovery" => "Option 29: perform-mask-discovery", "mask-supplier" => "Option 30: mask-supplier", "router-discovery" => "Option 31: router-discovery", "router-solicitation-address" => "Option 32: router-solicitation-address", "static-routes" => "Option 33: static-routes", "trailer-encapsulation" => "Option 34: trailer-encapsulation", "arp-cache-timeout" => "Option 35: arp-cache-timeout", "ieee802-3-encapsulation" => "Option 36: ieee802-3-encapsulation", "default-tcp-ttl" => "Option 37: default-tcp-ttl", "tcp-keepalive-interval" => "Option 38: tcp-keepalive-interval", "tcp-keepalive-garbage" => "Option 39: tcp-keepalive-garbage", "nis-domain" => "Option 40: nis-domain", "nis-servers" => "Option 41: nis-servers", "ntp-servers" => "Option 42: ntp-servers", "vendor-encapsulated-options" => "Option 43: vendor-encapsulated-options", "netbios-name-servers" => "Option 44: netbios-name-servers", "netbios-dd-server" => "Option 45: netbios-dd-server", "netbios-node-type" => "Option 46: netbios-node-type", "netbios-scope" => "Option 47: netbios-scope", "font-servers" => "Option 48: font-servers", "x-display-manager" => "Option 49: x-display-manager", "dhcp-requested-address" => "Option 50: dhcp-requested-address", "lease" => "Option 51: lease-time", "lease-time" => "Option 51: lease-time", "lease_time" => "Option 51: lease-time", "default-lease-time" => "Option 51: lease-time", "max-lease-time" => "Option 51: lease-time", "dhcp-option-overload" => "Option 52: dhcp-option-overload", "dhcp-message-type" => "Option 53: dhcp-message-type", "dhcp-server-identifier" => "Option 54: dhcp-server-identifier", "dhcp-parameter-request-list" => "Option 55: dhcp-parameter-request-list", "dhcp-message" => "Option 56: dhcp-message", "dhcp-max-message-size" => "Option 57: dhcp-max-message-size", "dhcp-renewal-time" => "Option 58: dhcp-renewal-time", "dhcp-rebinding-time" => "Option 59: dhcp-rebinding-time", "vendor-class-identifier" => "Option 60: vendor-class-identifier", "dhcp-client-identifier" => "Option 61: dhcp-client-identifier", "nwip-domain" => "Option 62: nwip-domain", "nwip-suboptions" => "Option 63: nwip-suboptions", "nisplus-domain" => "Option 64: nisplus-domain", "nisplus-servers" => "Option 65: nisplus-servers", "tftp-server-name" => "Option 66: tftp-server-name", "tftp-server" => "Option 66: tftp-server-name", "bootfile" => "Option 67: bootfile-name", "bootfile-name" => "Option 67: bootfile-name", "mobile-ip-home-agent" => "Option 68: mobile-ip-home-agent", "smtp-server" => "Option 69: smtp-server", "pop-server" => "Option 70: pop-server", "nntp-server" => "Option 71: nntp-server", "www-server" => "Option 72: www-server", "finger-server" => "Option 73: finger-server", "irc-server" => "Option 74: irc-server", "streettalk-server" => "Option 75: streettalk-server", "streettalk-directory-assistance-server" => "Option 76: streettalk-directory-assistance-server", "user-class" => "Option 77: user-class", "slp-directory-agent" => "Option 78: slp-directory-agent", "slp-service-scope" => "Option 79: slp-service-scope", "option-80" => "Option 80: option-80", "fqdn" => "Option 81: fqdn", "relay-agent-information" => "Option 82: relay-agent-information", "option-83" => "Option 83: option-83", "option-84" => "Option 84: option-84", "nds-servers" => "Option 85: nds-servers", "nds-tree-name" => "Option 86: nds-tree-name", "nds-context" => "Option 87: nds-context", "option-88" => "Option 88: option-88", "option-89" => "Option 89: option-89", "option-90" => "Option 90: option-90", "option-91" => "Option 91: option-91", "option-92" => "Option 92: option-92", "option-93" => "Option 93: option-93", "option-94" => "Option 94: option-94", "option-95" => "Option 95: option-95", "option-96" => "Option 96: option-96", "option-97" => "Option 97: option-97", "uap-servers" => "Option 98: uap-servers", "option-99" => "Option 99: option-99", "option-100" => "Option 100: option-100", "option-101" => "Option 101: option-101", "option-102" => "Option 102: option-102", "option-103" => "Option 103: option-103", "option-104" => "Option 104: option-104", "option-105" => "Option 105: option-105", "option-106" => "Option 106: option-106", "option-107" => "Option 107: option-107", "option-108" => "Option 108: option-108", "option-109" => "Option 109: option-109", "option-110" => "Option 110: option-110", "option-111" => "Option 111: option-111", "option-112" => "Option 112: option-112", "option-113" => "Option 113: option-113", "option-114" => "Option 114: option-114", "option-115" => "Option 115: option-115", "option-116" => "Option 116: option-116", "option-117" => "Option 117: option-117", "subnet-selection" => "Option 118: subnet-selection", "domain-search" => "Option 119: domain-search", "option-119" => "Option 119: option-119", "option-120" => "Option 120: option-120", "option-121" => "Option 121: option-121", "option-122" => "Option 122: option-122", "option-123" => "Option 123: option-123", "option-124" => "Option 124: option-124", "option-125" => "Option 125: option-125", "option-126" => "Option 126: option-126", "option-127" => "Option 127: option-127", "option-128" => "Option 128: TFTP-Server", "TFTP-Server" => "Option 128: TFTP-Server", "option-129" => "Option 129: option-129", "option-130" => "Option 130: option-130", "option-131" => "Option 131: option-131", "option-132" => "Option 132: option-132", "option-133" => "Option 133: option-133", "option-134" => "Option 134: option-134", "option-135" => "Option 135: option-135", "option-136" => "Option 136: option-136", "option-137" => "Option 137: option-137", "option-138" => "Option 138: option-138", "option-139" => "Option 139: option-139", "option-140" => "Option 140: option-140", "option-141" => "Option 141: option-141", "option-142" => "Option 142: option-142", "option-143" => "Option 143: option-143", "option-144" => "Option 144: option-144", "option-145" => "Option 145: option-145", "option-146" => "Option 146: option-146", "option-147" => "Option 147: option-147", "option-148" => "Option 148: option-148", "option-149" => "Option 149: option-149", "option-150" => "Option 150: option-150", "option-151" => "Option 151: option-151", "option-152" => "Option 152: option-152", "option-153" => "Option 153: option-153", "option-154" => "Option 154: option-154", "option-155" => "Option 155: option-155", "option-156" => "Option 156: option-156", "option-157" => "Option 157: option-157", "option-158" => "Option 158: option-158", "option-159" => "Option 159: option-159", "option-160" => "Option 160: option-160", "option-161" => "Option 161: option-161", "option-162" => "Option 162: option-162", "option-163" => "Option 163: option-163", "option-164" => "Option 164: option-164", "option-165" => "Option 165: option-165", "option-166" => "Option 166: option-166", "option-167" => "Option 167: option-167", "option-168" => "Option 168: option-168", "option-169" => "Option 169: option-169", "option-170" => "Option 170: option-170", "option-171" => "Option 171: option-171", "option-172" => "Option 172: option-172", "option-173" => "Option 173: option-173", "option-174" => "Option 174: option-174", "option-175" => "Option 175: option-175", "option-176" => "Option 176: option-176", "option-177" => "Option 177: option-177", "option-178" => "Option 178: option-178", "option-179" => "Option 179: option-179", "option-180" => "Option 180: option-180", "option-181" => "Option 181: option-181", "option-182" => "Option 182: option-182", "option-183" => "Option 183: option-183", "option-184" => "Option 184: option-184", "option-185" => "Option 185: option-185", "option-186" => "Option 186: option-186", "option-187" => "Option 187: option-187", "option-188" => "Option 188: option-188", "option-189" => "Option 189: option-189", "option-190" => "Option 190: option-190", "option-191" => "Option 191: option-191", "option-192" => "Option 192: option-192", "option-193" => "Option 193: option-193", "option-194" => "Option 194: option-194", "option-195" => "Option 195: option-195", "option-196" => "Option 196: option-196", "option-197" => "Option 197: option-197", "option-198" => "Option 198: option-198", "option-199" => "Option 199: option-199", "option-200" => "Option 200: option-200", "option-201" => "Option 201: option-201", "option-202" => "Option 202: option-202", "option-203" => "Option 203: option-203", "option-204" => "Option 204: option-204", "option-205" => "Option 205: option-205", "option-206" => "Option 206: option-206", "option-207" => "Option 207: option-207", "option-208" => "Option 208: option-208", "option-209" => "Option 209: option-209", "authenticate" => "Option 210: authenticate", "option-211" => "Option 211: option-211", "option-212" => "Option 212: option-212", "option-213" => "Option 213: option-213", "option-214" => "Option 214: option-214", "option-215" => "Option 215: option-215", "option-216" => "Option 216: option-216", "option-217" => "Option 217: option-217", "option-218" => "Option 218: option-218", "option-219" => "Option 219: option-219", "option-220" => "Option 220: option-220", "option-221" => "Option 221: option-221", "option-222" => "Option 222: option-222", "option-223" => "Option 223: option-223", "option-224" => "Option 224: option-224", "option-225" => "Option 225: option-225", "option-226" => "Option 226: option-226", "option-227" => "Option 227: option-227", "option-228" => "Option 228: option-228", "option-229" => "Option 229: option-229", "option-230" => "Option 230: option-230", "option-231" => "Option 231: option-231", "option-232" => "Option 232: option-232", "option-233" => "Option 233: option-233", "option-234" => "Option 234: option-234", "option-235" => "Option 235: option-235", "option-236" => "Option 236: option-236", "option-237" => "Option 237: option-237", "option-238" => "Option 238: option-238", "option-239" => "Option 239: option-239", "option-240" => "Option 240: option-240", "option-241" => "Option 241: option-241", "option-242" => "Option 242: option-242", "option-243" => "Option 243: option-243", "option-244" => "Option 244: option-244", "option-245" => "Option 245: option-245", "option-246" => "Option 246: option-246", "option-247" => "Option 247: option-247", "option-248" => "Option 248: option-248", "option-249" => "Option 249: option-249", "option-250" => "Option 250: option-250", "option-251" => "Option 251: option-251", "option-252" => "Option 252: option-252", "option-253" => "Option 253: option-253", "option-254" => "Option 254: option-254", ); # is it a number ? # then lookup and return the real name my $name ; my $number ; if ( $opt =~ /^\d+$/ ) { # look it up in the above list ( $name ) = grep ( / $opt:/ , sort values %lookup ); # clean it, return $name =~ s/.*: //; $number = $opt; } else { # find the number # fix it ? if ( $lookup{$opt} ) { $number = $lookup{$opt}; $name = $lookup{$opt}; # strip and clean the answers $number =~ s/:.*//g; $number =~ s/\D//g; $name =~ s/.*: //; } else { $number = 0 ; $name = "0" ; } } # my $DEBUG = 4 ; debug_cli(4,"gedh: ($number , $name) from [$opt]"); return ( $number , $name ) ; } sub cidr_to_mask { my ($cidr) = @_ ; my %clook = ( '0' => 0 , '1' => '128.0.0.0' , '2' => '192.0.0.0' , '3' => '224.0.0.0' , '4' => '240.0.0.0' , '5' => '248.0.0.0' , '6' => '252.0.0.0' , '7' => '254.0.0.0' , '8' => '255.0.0.0' , '9' => '255.128.0.0' , '10' => '255.192.0.0' , '11' => '255.224.0.0' , '12' => '255.240.0.0' , '13' => '255.248.0.0' , '14' => '255.252.0.0' , '15' => '255.254.0.0' , '16' => '255.255.0.0' , '17' => '255.255.128.0' , '18' => '255.255.192.0' , '19' => '255.255.224.0' , '20' => '255.255.240.0' , '21' => '255.255.248.0' , '22' => '255.255.252.0' , '23' => '255.255.254.0' , '24' => '255.255.255.0' , '25' => '255.255.255.128' , '26' => '255.255.255.192' , '27' => '255.255.255.224' , '28' => '255.255.255.240' , '29' => '255.255.255.248' , '30' => '255.255.255.252' , '31' => '255.255.255.254' , '32' => '255.255.255.255' , ); return ( $clook{$cidr} ) ; } sub mask_to_cidr { my ($mask) = @_ ; my %clook = ( '0' => 0 , '128.0.0.0' => 1 , '192.0.0.0' => 2 , '224.0.0.0' => 3 , '240.0.0.0' => 4 , '248.0.0.0' => 5 , '252.0.0.0' => 6 , '254.0.0.0' => 7 , '255.0.0.0' => 8 , '255.128.0.0' => 9 , '255.192.0.0' => 10 , '255.224.0.0' => 11 , '255.240.0.0' => 12 , '255.248.0.0' => 13 , '255.252.0.0' => 14 , '255.254.0.0' => 15 , '255.255.0.0' => 16 , '255.255.128.0' => 17 , '255.255.192.0' => 18 , '255.255.224.0' => 19 , '255.255.240.0' => 20 , '255.255.248.0' => 21 , '255.255.252.0' => 22 , '255.255.254.0' => 23 , '255.255.255.0' => 24 , '255.255.255.128' => 25 , '255.255.255.192' => 26 , '255.255.255.224' => 27 , '255.255.255.240' => 28 , '255.255.255.248' => 29 , '255.255.255.252' => 30 , '255.255.255.254' => 31 , '255.255.255.255' => 32 , ); return ( $clook{$mask} ) ; } sub by_dom { # sort a zone name by it's domain # this means we reverse the components, /then/ sort it # this works the same way as by_ip but we use 'cmp' instead of '<=>' # -1 means lower in the list # +1 means higher in the list my(@a) = reverse split(/[\.\/]/, $a); my(@b) = reverse split(/[\.\/]/, $b); # the arrays can be of different lengths while (@a or @b) { # if ( $a[0] ne $b[0] ) { print " mism\n"; } # else { print " matt\n"; } return $a[0] cmp $b[0] if ($a[0] ne $b[0]); shift @a; shift @b; } return 0; } sub by_ip { # used for sorting (by_ip) # &abs_ip($a) <=> &abs_ip($b) ; my(@a) = split(/[\.\/]/, $a); my(@b) = split(/[\.\/]/, $b); # The first return is only processed if $a[0] != $b[0]. # # It doesn't need to process the rest of @a/@b because it has # reached a point in the (parallel) vectors where the values # differ so it returns the -1, 0, or 1 based on a comparision # of those 2 values. while (@a) { return $a[0] <=> $b[0] if ($a[0] != $b[0]); shift @a; shift @b; } return 0; } sub abs_ip { # get an IP as a real absval my ( $ip ) = @_ ; # strip cidr crap $ip =~ s/\/.*//; my @octets = split(/\./,$ip) ; my $absval = $octets[3] + ( 2 ** 8 * $octets[2] ) ; $absval = $absval + ( 2 ** 16 * $octets[1] ) ; $absval = $absval + ( 2 ** 24 * $octets[0] ) ; return ($absval) ; } sub ip_abs { # convert an absval back again to an IP my ( $abs ) = @_ ; my $o1 = int ( $abs / ( 2 ** 24 ) ) ; my $r1 = $abs % ( 2 ** 24 ) ; my $o2 = int ( $r1 / ( 2 ** 16 ) ) ; my $r2 = $abs % ( 2 ** 16 ) ; my $o3 = int ( $r2 / ( 2 ** 8 ) ) ; my $o4 = $abs % ( 2 ** 8 ) ; my $ip = join ( "." , $o1 , $o2 , $o3 , $o4 ) ; return ($ip) ; } sub netbounds { my ($subnet) = @_ ; my ($netid,$cidr) = split ( /\// , $subnet ) ; # break the network out into octets my @net_octs = split(/\./,$netid) ; my @base_octs = @net_octs ; my @last_octs = @net_octs ; # based on the netmask and net_id we can calculate # gateway , broadcast et al ... # for any mask we need to determine which octet we're changing # and split the ip up and work on the octets # # /24 -> octet 3 , 0 dvisions # /23 -> octet 2 , 128 divisions # /22 -> octet 2 , 64 divisions # ..... # /16 -> octet 2 , 0 divisions # # we're going to use the original network and rewrite # the octets as they relate to the netmask # # we only need to modify octets that are outside the mask ($mod_oct) # we work out the value of that octet is by the delta. # my $delta = 256 / ( 2 ** ( $cidr % 8 ) ) ; my $mod_oct = int ( $cidr / 8 ); # given this we work the upper and lower limits my $oct_val = $net_octs[$mod_oct] ; my $first_oct = $oct_val - ( $oct_val % $delta ) ; my $last_oct = $first_oct + $delta - 1 ; # then set some temp arrays with potential values foreach my $o_cc ( $mod_oct .. 3 ) { if ( $o_cc == $mod_oct ) { $base_octs[$o_cc] = $first_oct ; $last_octs[$o_cc] = $last_oct ; } else { $last_octs[$o_cc] = 255 ; $base_octs[$o_cc] = 0 ; } } my $fip = join('.',@base_octs); my $lip = join('.',@last_octs); return($fip,$lip); } sub arpa_to_net { my ( $revzone ) = @_ ; # find all digits and reverse the order my @octs = reverse $revzone =~ /(\d+)/g ; # the cidr is a lookup based on array length # @cidr[$#octs] my @cidr_list = ( 8 , 16 , 24 ) ; my $cidr = $cidr_list[$#octs] ; # pad the array with zeroes push @octs , ( 0 , 0 , 0 , 0 ) ; # grab the first 4 , join them my $net = join "." , splice ( @octs , 0 , 4 ) ; return ( "$net/$cidr" ) ; } sub cli_exit { # quit and cleanup print "\nexiting...bye\n"; # $el->history_save(".clihistory") || die "cant" ; exit ; # return CC_EOF; } sub init_editline { # assume the worst my $use_el = 0 ; # now, try an catch the loading of editline and not abort when we die # errk # Inside an "eval()," the die() # error message is stuffed into $@ and the "eval" is terminated # with the undefined value. # This makes "die" the way to raise an exception. eval { # require Term::eEditLine; require Term::EditLine; } ; if ( $@ =~ /Can't locate Term.* in \@INC/ ) { # return NULL return ( 0 , 0 ); } else { # we loaded ok, git going. $use_el = 1 } # continue importing import Term::EditLine ; # create the object my $el = Term::EditLine->new('progname'); $el->set_prompt ("$PROMPT"); # other useful commands are # $el->insertstr("aaaa"); # inserts text, but may not print it. ########################################################## # sigint (ctrl-c) has to be caught by perl, before the term. # $SIG{INT} = sub { print "\nYou hit ^C... Aaayyy it hurts\n"; }; # $SIG{INT} = sub { $el->reset ; return CC_CURSOR }; # $el->add_fun ('undo','reset', # sub { print "\n" ; $el->reset ; return CC_REDISPLAY }) ; $SIG{INT} = 'IGNORE'; ######################################## # define input functions # # these occur based on character input... # ######################################## # quit on '^D' # CC_EOF kills the input (end of file) # $el->add_fun ('bye','desc',sub { # print "q\nquitting...bye\n"; return CC_EOF; }); $el->add_fun ('bye','desc', \&cli_exit ); # print help $el->add_fun ('help','desc', \&print_help ); # completion control $el->add_fun ('cmpcl','desc', \&cmpctl ) ; ######################################## # add key bindings # 'q' to quit is really an arg function # $el->parse('bind','q\n','bye'); $el->parse('bind','-e'); $el->parse('bind','^D','bye'); # "\" clear the line ( can't use CRTL-C ?? ) $el->parse('bind','\\\\','ed-start-over'); $el->parse('bind','^U','ed-start-over'); # inline help $el->parse('bind','?','help'); # tab ? $el->parse('bind','\t','cmpcl'); # show them all # $el->parse('bind','-a'); ######################################## # load the history $el->history_set_size ( 100 ) ; # $el->history_load(".clihistory"); ######################################## # did we succeed ? if ( $el ) { $use_el = 1 } # return the handler # return ( $el ); return ( $use_el , $el ); } sub init_commands { ########################################################## # # The word list lookup table # we'll create a big hash of all commandline alternatives then # abbreviate all of them. This will give you conditional cmpctl and still # hopefully be easy to read # the hash is $COMMANDS { "" } { "words|func|help" } # $COMMANDS { "" } { "words" } # the LHS is a regex matching the WHOLE expanded comand line # the RHS is the list of next possible words as a string which # will get carved into array values as part of get_context() # we grep from the LHS the WHOLE STRING (^..$) ensure uniqieness # [x] how do you handle options than can come in any order ? # use '|' syntax "word|word|word" # [x] how do you handle option/value pairs ? # use '|=' syntax "word=|word=|word=" # [ ] some options can be repeated, some need to end with them. # so we need a val|val syntax and a val?|val? syntax # ( use perlre's '?' for 0 or 1 ) # comm list # aliases %ALIASES = ( pwd => "show file path", cd => "configure file path", ls => "show file nodes", prop => "show file properties", info => "show file properties", ); my %COMMANDS = ( 'NULL' => { words => "help quit|bye|exit history restart upload download configure show test", }, 'test' => { # words => "configure zone network", words => "configure add adc|bb zone network", # words => "comment= delay= aa|bb bad|member=|import= zone network", func => "cli_test", }, 'quit' => { func => "cli_exit", }, 'bye' => { func => "cli_exit", }, 'exit' => { func => "cli_exit", }, 'history' => { func => "cli_history", }, 'show' => { words => "ipam member template record time file server grid admin views zone host network range radius debug", }, 'help' => { words => "all", func => "print_help", }, 'help all' => { func => "print_perldoc", }, 'show file' => { words => " summary|nodes|path|properties", func => "cli_show_file", }, # 'show file properties' => { # words => " ", # func => "cli_show_file", }, # 'show file properties ' => { # func => "cli_show_file", }, 'show member' => { words => "", }, 'show member ' => { words => "dns", }, 'show member dns' => { func => "cli_show_member_dns", }, 'show time' => { func => "cli_show_date", }, 'show debug' => { words => "session commands file pwd", }, 'show debug pwd' => { func => "debug_pwd", }, 'show debug file' => { func => "debug_file", }, 'show debug session' => { func => "debug_commands", }, 'show debug commands' => { words => " detailed", func => "debug_commands", }, 'show debug commands detailed' => { func => "debug_commands", }, # grid stuff 'show grid' => { words => " schedule attribute device_type", }, 'show grid attribute' => { words => " ", func => "cli_show_attributes", }, 'show grid attribute ' => { func => "cli_show_attributes", }, 'show grid device_type' => { words => " ", func => "cli_show_device_types", }, 'show grid device_type ' => { func => "cli_show_device_types", }, 'show grid schedule' => { func => "cli_show_schedule", }, 'show grid ' => { words => " dhcp dns member", func => "cli_show_grid", }, 'show grid member' => { words => " licenses status", func => "cli_show_member", }, 'show grid member licenses' => { words => " ", func => "cli_show_member", }, 'show grid member status' => { words => " ", func => "cli_show_member", }, 'show grid member licenses ' => { func => "cli_show_member", }, 'show grid member status ' => { func => "cli_show_member", }, 'show grid member ' => { words => "detailed status licenses", func => "cli_show_member", }, 'show grid member detailed' => { func => "cli_show_member", }, 'show grid member licenses' => { words => " ", func => "cli_show_member", }, 'show grid member status' => { words => " ", func => "cli_show_member", }, 'show grid member licenses ' => { func => "cli_show_member", }, 'show grid member status ' => { func => "cli_show_member", }, 'show grid dhcp' => { func => "cli_show_grid_dhcp", }, 'show grid dns' => { func => "cli_show_grid_dns", }, # show admins 'show admin' => { words => "role user admin_group", }, 'show admin role' => { words => "|", func => "cli_show_admin", }, 'show admin user' => { words => "|", func => "cli_show_admin", }, 'show admin admin_group' => { words => "|", func => "cli_show_admin", }, # 'show views ' => { # words => "", # func => "cli_show_view", }, # show views 'show views' => { words => " ", func => "cli_show_view", }, 'show views ' => { words => "", func => "cli_show_view", }, 'show host' => { # words => "|ipv4addr=|view=", }, words => " |mac=|ipv4addr=|view=", func => "cli_show_host", }, # 'show host ' => { # words => " view=", # func => "cli_show_host", }, # 'show host view ' => { # words => "", # func => "cli_show_host", }, # show DNS records 'show record' => { words => "info=|view=|ptr=|srv=|cname=|a_record=|aaaa=|bulkhost=|txt=|mx=", func => "cli_show_record", }, 'show zone' => { words => " |member=|secondary|detailed|forward|reverse|shared_record_group|ns_group|view=", func => "cli_show_zone", }, 'show zone member ' => { func => "cli_show_member_dns", }, 'show zone view' => { func => "cli_show_view", }, 'show zone ns_group' => { words => "", func => "cli_show_zone", }, 'show zone ns_group ' => { func => "cli_show_zone", }, 'show zone shared_record_group' => { words => "", func => "cli_show_zone", }, 'show zone shared_record_group ' => { func => "cli_show_zone", }, # show templates 'show template' => { words => "fixed network", }, 'show template fixed' => { words => " ", func => "cli_show_fixed_template", }, 'show template fixed ' => { func => "cli_show_fixed_template", }, 'show template network' => { words => " ", func => "cli_show_network_template", }, 'show template network ' => { func => "cli_show_network_template", }, # show networks 'show network' => { words => " info= details views ranges shared statistics lease options failover fixed filter", func => "cli_show_network", }, 'show network shared' => { words => " ", func => "cli_show_network_shared", }, 'show network shared ' => { func => "cli_show_network_shared", }, 'show network views' => { words => " ", func => "cli_show_network_views", }, 'show network views ' => { func => "cli_show_network_views", }, 'show network failover' => { func => "cli_show_failover", }, 'show network options' => { func => "cli_show_dhcp_options", }, 'show network info ' => { words => " info=|details", func => "cli_show_network", }, 'show network details' => { func => "cli_show_network", }, 'show network ' => { words => " view= subnets ipam range fixed statistics", func => "cli_show_network", }, 'show network subnets' => { func => "cli_show_network", }, 'show network view ' => { func => "cli_show_network", }, 'show network statistics' => { func => "cli_show_netstats", }, 'show network statistics' => { func => "cli_show_netstats", }, 'show network ipam' => { words => "used unused next_network= next_available", func => "cli_show_ipam", }, 'show network ipam used' => { func => "cli_show_ipam", }, 'show network ipam unused' => { func => "cli_show_ipam", }, 'show network ipam next_network ' => { func => "cli_show_ipam", }, 'show network ipam next_available' => { words => " ", func => "cli_show_ipam", }, 'show network ipam next_available ' => { func => "cli_show_ipam", }, 'show network fixed' => { words => " ||view=", func => "cli_show_fixed", }, # 'show network fixed ' => { # func => "cli_show_fixed", }, # 'show network fixed ' => { # func => "cli_show_fixed", }, # 'show network fixed view=' => { # func => "cli_show_fixed", }, # 'show network fixed view=' => { # func => "cli_show_fixed", }, 'show network fixed' => { words => " ||view=", func => "cli_show_fixed", }, # 'show network fixed ' => { # func => "cli_show_fixed", }, # 'show network fixed ' => { # func => "cli_show_fixed", }, 'show network filter' => { words => " ", func => "cli_show_filter", }, 'show network filter ' => { words => " entries|mac=|comment=", func => "cli_show_filter", }, # 'show network filter mac' => { # words => " ", # func => "cli_show_filter", }, # 'show network filter mac ' => { # func => "cli_show_filter", }, 'show network ranges' => { func => "cli_show_range", }, 'show network range' => { words => " |view=", func => "cli_show_range", }, # 'show network range ' => { # func => "cli_show_range", }, # alternate form of show range 'show range' => { words => " |view=", func => "cli_show_range", }, # 'show range ' => { # func => "cli_show_range", }, # - leases - 'show network lease' => { words => " ", func => "cli_show_lease", }, 'show network lease ' => { func => "cli_show_lease", }, 'show network lease ' => { func => "cli_show_lease", }, # uploads 'upload' => { words => "csv= leases= database= expert_dhcp_conf=", }, 'upload csv ' => { func => "cli_upload_file", }, 'upload leases ' => { func => "cli_upload_file", }, 'upload database ' => { func => "cli_upload_database", }, 'upload expert_dhcp_conf ' => { words => "member=", func => "cli_upload_dhcp", }, 'upload expert_dhcp_conf member ' => { func => "cli_upload_dhcp", }, # downloads 'download' => { words => "log_files=|csv=|lease_history=|support_bundle= merge_log= expert_dhcp_conf= dhcp_conf= database", }, 'download csv ' => { words => "object=", }, 'download csv object ' => { func => "cli_download_log", }, 'download database' => { words => "", }, 'download database ' => { func => "cli_download_database", }, # they all call the same generic function 'download log_files ' => { words => "" }, 'download log_files ' => { words => "member="}, 'download log_files member ' => { func => "cli_download_log", }, 'download lease_history ' => { func => "cli_download_log", }, 'download merge_log ' => { func => "cli_download_log", }, 'download support_bundle ' => { words => "member=" }, 'download support_bundle member ' => { words => " syslog", func => "cli_download_log", }, 'download support_bundle member syslog' => { words => "syslog", func => "cli_download_log", }, # 'download dhcp_conf' => { # words => "", }, 'download dhcp_conf ' => { words => "member=|", func => "cli_download_dhcp", }, 'download expert_dhcp_conf ' => { words => "member=|", func => "cli_download_dhcp", }, 'restart' => { words => "dns dhcp discovery", }, 'restart discovery' => { func => "cli_restart_discovery", }, 'restart dns' => { words => "delay=", func => "cli_restart", }, 'restart dhcp' => { words => "member=|delay=|test_dhcp", func => "cli_restart", }, 'configure' => { words => "ipam admin debug master= file server template zone network radius grid member" , }, 'configure master ' => { func => "cli_add_master", }, 'configure file' => { words => "load= path=", }, 'configure file load ' => { func => "cli_load_db", }, 'configure file path ' => { func => "cli_set_path", }, 'configure admin' => { words => "add modify delete ", }, 'configure admin modify' => { words => "user=|admin_group=", }, 'configure admin add' => { words => "permission=|role=|user=|admin_group=", }, # - permissons - 'configure admin add permission ' => { words => "|type=|sub_object=|role=|lease=|member=|view=|zone=|network=|range=|filter=|group=", func => "cli_add_permission", }, 'configure admin add user ' => { words => "disabled|password=|group=|comment=|email=", func => "cli_add_user", }, 'configure admin modify user ' => { words => "|disabled|password=|group=|comment=|email=|superuser", func => "cli_add_user", }, 'configure admin add role ' => { func => "cli_add_admin_role", }, 'configure admin add admin_group ' => { words => "|role=|superuser|comment=", func => "cli_add_admin_group", }, 'configure admin modify admin_group ' => { words => "|role=|superuser|comment=", func => "cli_add_admin_group", }, 'configure debug' => { words => "", }, 'configure debug ' => { func => "set_debug", }, # - server - 'configure server' => { words => "", }, 'configure server ' => { words => "user=", }, 'configure server user ' => { words => "password=", func => "cli_add_server", }, 'show server' => { words => "version error message", }, 'show server version' => { func => "cli_show_server", }, 'show server error' => { func => "cli_show_server", }, 'show server message' => { func => "cli_show_server", }, # - member - 'configure member' => { words => "", }, 'configure member ' => { words => "dns dhcp add delete modify", }, 'configure member add' => { words => "permission", }, 'configure member delete' => { words => "permission", }, 'configure member modify' => { words => "permission", }, # - member DNS 'configure member dns' => { words => "|set=", func => "cli_mod_member_dns", }, # - member DHCP 'configure member dhcp' => { words => "option=|set=", func => "cli_mod_member_dhcp", }, # - grid - 'configure grid' => { words => "add schedule ", }, # scheduled tasks 'configure grid schedule' => { words => "delete=" }, 'configure grid schedule delete ' => { func => "cli_del_schedule", }, # add device types : # configure grid add device_type label = 'configure grid add' => { words => " attribute device_type" }, 'configure grid add device_type' => { words => "" }, 'configure grid add device_type ' => { words => "|label=" , func => "cli_add_device_types", }, 'configure grid add attribute' => { words => "" }, 'configure grid add attribute ' => { words => "|value=|required|multiple|type=" , # sbe fixed typo in multpl - still having issues with type list and value func => "cli_add_attribute", }, # - grid - 'configure grid ' => { words => "dns dhcp disable enable set add delete modify", }, 'configure grid set' => { words => "",}, 'configure grid set ' => { func => "cli_mod_grid", }, 'configure grid disable' => { words => "ntp|ntp_server", func => "cli_mod_grid", }, 'configure grid enable' => { words => "ntp|ntp_server", func => "cli_mod_grid", }, 'configure grid modify' => { words => "member grid_name=|resolver=|session_timeout=|vpn_port=|shared_secret=", func => "cli_mod_grid", }, 'configure grid delete' => { words => "member= lcd_input|remote_console_access|support_access", func => "cli_mod_grid", }, 'configure grid add' => { words => "member= ntp_server=|lcd_input|remote_console_access|support_access", func => "cli_mod_grid", }, # 'configure grid add member ' => { # words => "ipaddress=", }, # 'configure grid add member ipaddress ' => { 'configure grid add member ' => { words => " ipaddress=|mgmt_ip_2=|mgmt_ip=|mgmt_gateway=|comment=|hapair=|gateway=|routerid=", func => "cli_add_member", }, 'configure grid delete member ' => { words => "ipaddress=", }, 'configure grid delete member ipaddress ' => { words => "gateway=", }, 'configure grid delete member ipaddress gateway ' => { func => "cli_del_member", }, 'configure grid dns' => { words => "add delete", }, 'configure grid dhcp' => { words => "add delete", }, 'configure grid dns add' => { words => " default_ttl=|recursion|ns_group= acl", func => "cli_mod_dns", }, # - NS groups - # configure grid foo dns add ns_group foo # configure zone add ns_group foo 'configure grid dns add ns_group ' => { words => "stealth_ext_secondary=|lead=|ext_secondary=|ext_primary=|stealth_primary=|primary=|secondary=", func => "cli_add_nsgroup", }, 'configure zone add ns_group ' => { words => "stealth_ext_secondary=|lead=|ext_secondary=|ext_primary=|stealth_primary=|primary=|secondary=", func => "cli_add_nsgroup", }, 'configure zone modify ns_group ' => { words => "stealth_ext_secondary=|lead=|ext_secondary=|ext_primary=|stealth_primary=|primary=|secondary=", func => "cli_add_nsgroup", }, 'configure zone delete ns_group ' => { func => "cli_del_nsgroup", }, # - Grid DNS acls - 'configure grid dns add acl' => { words => "allow deny", }, 'configure grid dns add acl allow' => { words => "recursion=|transfer=|update=|query=|forwarding=", func => "cli_add_grid_dns_acl", }, 'configure grid dns add acl deny' => { words => "transfer=|update=|query=|forwarding=", func => "cli_add_grid_dns_acl", }, 'configure grid dhcp add' => { words => "option=|space=|typeoption=", func => "cli_add_grid_option", }, # - grid member - 'configure grid modify member' => { words => "", }, 'configure grid modify member ' => { words => "ipaddress= disable enable", }, 'configure grid modify member ipaddress ' => { words => "name=|comment=|hapair=|gateway=|routerid=", func => "cli_mod_member", }, 'configure grid modify member disable' => { words => "ntp|dns|dhcp", func => "cli_mod_member", }, 'configure grid modify member enable' => { words => "ntp|dns|dhcp", func => "cli_mod_member", }, # - zones - 'configure zone' => { words => "add delete copy modify ", }, # copy a zone 'configure zone copy' => { words => "", }, 'configure zone copy ' => { words => "view=", }, 'configure zone copy view ' => { words => "to=", }, 'configure zone copy view to ' => { words => "dest_view=", }, 'configure zone copy view to dest_view ' => { func => "cli_copy_zone", }, 'configure zone delete' => { words => "|ns_group=|view=|shared_record_group=", }, 'configure zone delete ' => { words => " view=", func => "cli_delete_zone", }, 'configure zone add' => { words => "|ns_group=|view=|shared_record_group=", }, 'configure zone modify' => { words => "|ns_group=|view=", }, # - views - 'configure zone add view ' => { words => " info=|set=|disabled|comment=", func => "cli_add_view", }, 'configure zone modify view ' => { words => " info=|set=|disabled|comment=", func => "cli_add_view", }, # 'configure zone add view disabled' => { # func => "cli_add_view", }, 'configure zone delete view ' => { func => "cli_del_view", }, # - shared record groups - 'configure zone add shared_record_group ' => { words => " comment=", func => "cli_add_shared_group", }, 'configure zone delete shared_record_group ' => { func => "cli_del_shared_group", }, 'configure zone modify ' => { words => " info=|generate_hosts|nonauthorative|lead=|set=|prefix=|view=|comment=|mname=|serial=|email=|shared_record_group=|ns_group=|import=|member=|delegate_to=|stub_from=|forward_to=|stealth_ext_secondary=|ext_secondary=|ext_primary=|stealth_primary=|primary=|secondary=|stealth_secondary=", func => "cli_add_zone", }, 'configure zone add ' => { words => " info=|nonauthorative|lead=|set=|prefix=|view=|comment=|mname=|serial=|email=|shared_record_group=|ns_group=|import=|member=|delegate_to=|stub_from=|forward_to=|stealth_ext_secondary=|ext_secondary=|ext_primary=|stealth_primary=|primary=|secondary=|stealth_secondary=", func => "cli_add_zone", }, 'configure zone ' => { words => "add delete modify insert", }, 'configure zone add' => { words => "permission host a_record aaaa bulkhost txt cname ptr_record srv mx", }, 'configure zone delete' => { words => "permission host a_record aaaa bulkhost txt cname ptr_record srv mx", }, 'configure zone insert' => { words => "host=", }, 'configure zone modify' => { words => "permission host=", }, # - permissions - # I could do this in just one config line, but it doesn't force # the order, so we will do it the hard way... 'configure zone add permission' => { words => "|read|write|deny|group=", func => "cli_add_permission", }, 'configure zone delete permission' => { words => "|read|write|deny|group=", func => "cli_del_permission", }, 'configure zone modify permission' => { words => "|read|write|deny|group=", func => "cli_mod_permission", }, 'configure network add permission' => { words => "|read|write|deny|group=", func => "cli_add_permission", }, 'configure network delete permission' => { words => "|read|write|deny|group=", func => "cli_del_permission", }, 'configure network modify permission' => { words => "|read|write|deny|group=", func => "cli_mod_permission", }, 'configure member add permission' => { words => "|read|write|deny|group=", func => "cli_add_permission", }, 'configure member delete permission' => { words => "|read|write|deny|group=", func => "cli_del_permission", }, 'configure member modify permission' => { words => "|read|write|deny|group=", func => "cli_mod_permission", }, # - zone records - 'configure zone delete host' => { words => "", }, 'configure zone delete host ' => { words => "|view=", func => "cli_del_host", }, 'configure zone delete a_record' => { words => "=", }, 'configure zone delete a_record ' => { words => "|view=", func => "cli_del_a_rec", }, 'configure zone delete aaaa' => { words => "=", }, 'configure zone delete aaaa ' => { words => "|view=", func => "cli_del_aaaa", }, 'configure zone delete bulkhost' => { words => "", }, 'configure zone delete bulkhost ' => { words => "", }, 'configure zone delete bulkhost ' => { words => "", }, 'configure zone delete bulkhost ' => { words => "|view=", func => "cli_del_bulk", }, 'configure zone delete cname' => { words => "", }, 'configure zone delete cname ' => { words => "", }, 'configure zone delete cname ' => { words => "|view=", func => "cli_del_cname", }, 'configure zone delete txt' => { words => "=", }, 'configure zone delete txt ' => { words => "|view=", func => "cli_del_txt", }, 'configure zone delete mx' => { words => "", }, 'configure zone delete mx ' => { words => "=", }, 'configure zone delete mx ' => { words => "|view=", func => "cli_del_mx", }, 'configure zone delete ptr_record' => { words => "", }, 'configure zone delete ptr_record ' => { words => "", }, 'configure zone delete ptr_record ' => { words => "|view=", func => "cli_del_ptr", }, # [ ] need to all add/then modify # aka cli_add_sub_network 'configure zone insert host ' => { words => " at=|template=||set=|option=|view=|ttl=|info=|comment=|alias=|disabled|nodns", func => "cli_ins_host", }, 'configure zone modify host ' => { words => " at=|template=||host_set=|set=|option=|view=|ttl=|info=|comment=|alias=|name=|disabled|nodns", func => "cli_add_host", }, 'configure zone add host' => { words => "=", }, 'configure zone add host ' => { words => " at=|template=|view=|host_set=|set=|ttl=|info=|comment=|alias=|disabled|nodns|option=", func => "cli_add_host", }, 'configure zone add a_record' => { words => "=", }, 'configure zone add a_record ' => { words => " shared_record_group=|info=|set=|comment=|ttl=|disabled|view=", func => "cli_add_a_rec", }, 'configure zone add aaaa' => { words => "=", }, 'configure zone add aaaa ' => { words => " view=|shared_record_group=|comment=|disabled", func => "cli_add_aaaa", }, 'configure zone add bulkhost' => { words => "", }, 'configure zone add bulkhost ' => { words => "", }, 'configure zone add bulkhost ' => { words => "", }, 'configure zone add bulkhost ' => { words => " addreverse|comment=|view=", func => "cli_add_bulk", }, 'configure zone add cname' => { words => "", }, 'configure zone add cname ' => { words => "", }, 'configure zone add cname ' => { words => " view=|set=|comment=|ttl=", func => "cli_add_cname", }, 'configure zone add txt' => { words => "=", }, 'configure zone add txt ' => { words => " shared_record_group=|view=|comment=", func => "cli_add_txt", }, 'configure zone add mx' => { words => "", }, 'configure zone add mx ' => { words => "=", }, 'configure zone add mx ' => { words => " view=|set=|shared_record_group=|comment=", func => "cli_add_mx", }, # CBUIJS add_srv 'configure zone add srv' => { words => "=", }, 'configure zone add srv ' => { words => "=", }, 'configure zone add srv ' => { words => "", }, 'configure zone add srv ' => { words => " shared_record_group=|set=|comment=|disabled|view=", func => "cli_add_srv_rec", }, 'configure zone add ptr_record' => { words => "", }, 'configure zone add ptr_record ' => { words => "", }, 'configure zone add ptr_record ' => { words => " view=|comment=|ttl=", func => "cli_add_ptr", }, # template objects 'configure template' => { words => "add modify delete", }, 'configure template add' => { words => "fixed=", }, 'configure template delete' => { words => "network= fixed=", }, 'configure template modify' => { words => "fixed=", }, 'configure template add fixed ' => { words => " offset=|comment=|set=|info=|option=", func => "cli_add_fixed_template", }, 'configure template delete network ' => { func => "cli_del_network_template", }, 'configure template delete fixed ' => { func => "cli_del_fixed_template", }, # IPAM and discovery objects 'show ipam' => { words => "address= discovery", }, 'show ipam address ' => { func => "cli_show_ipam_address", }, 'show ipam discovery' => { func => "cli_show_discovery", }, 'configure ipam' => { words => "discovery", }, 'configure ipam discovery' => { words => "add", }, 'configure ipam discovery add' => { words => "network member", }, 'configure ipam discovery add network' => { words => "", }, 'configure ipam discovery add network ' => { func => "cli_add_discovery", }, 'configure ipam discovery add member' => { words => "", }, 'configure ipam discovery add member ' => { func => "cli_add_discovery", }, # network objects 'configure network' => { words => "move= add modify delete filter=", }, 'configure network delete' => { words => "range fixed= macfilter= shared=", }, 'configure network delete ' => { words => "netmask=|view=", func => "cli_delete_network", }, 'configure network delete netmask ' => { func => "cli_delete_network", }, 'configure network delete shared ' => { func => "cli_delete_shared_network", }, 'configure network add' => { words => "parent= container= view= space= optiondef= failover= macfilter= shared= fixed range", }, 'configure network modify' => { words => " container= fixed range", }, # - optiondef - 'configure network add view ' => { func => "cli_add_network_view", }, 'configure network add space ' => { func => "cli_add_option_space", }, 'configure network add optiondef ' => { words => "code=", }, 'configure network add optiondef code ' => { words => "type=", }, # string, boolean, ip-address, text, 8-bit unsigned integer, 16-bit unsigned # inte- ger, 32-bit unsigned integer, 8-bit signed integer, 16-bit signed # integer, or 32-bit signed integer. 'configure network add optiondef code type ' => { words => "|space=", func => "cli_add_option_def", }, # words => " code=|type=", # - Failover - 'configure network add failover ' => { words => " primary=|secondary=|set=", func => "cli_add_failover", }, # - macfilters - 'configure network add macfilter ' => { func => "cli_add_macfilter", }, 'configure network delete macfilter ' => { func => "cli_del_macfilter", }, 'configure network filter ' => { words => "add modify delete", }, 'configure network filter add' => { words => "macaddress=", }, 'configure network filter add macaddress ' => { words => " comment=", func => "cli_add_macfilteraddr", }, 'configure network filter modify' => { words => "macaddress=", }, 'configure network filter modify macaddress ' => { words => " comment=", func => "cli_add_macfilteraddr", }, 'configure network filter delete' => { words => "macaddress=", }, 'configure network filter delete macaddress ' => { func => "cli_del_macfilteraddr", }, # - containers 'configure network add parent ' => { words => " view=|info=|disabled|comment=", func => "cli_add_network_container", }, 'configure network add container ' => { words => " view=|info=|disabled|comment=", func => "cli_add_network_container", }, 'configure network modify container ' => { words => " view=|info=|disabled|comment=", func => "cli_add_network_container", }, # - sharednetworks - 'configure network add shared ' => { words => " view=|child_network=|comment=|member=|option=", func => "cli_add_shared", }, # - networks 'configure network add ' => { words => " view=|info=|template=|netmask=|addreverse|disabled|comment=|member=|set=|addoption=|option=", func => "cli_add_network", }, # - modify networks 'configure network modify ' => { words => " view=|info=|template=|netmask=|addreverse|disabled|comment=|member=|set=|addoption=|option=", func => "cli_add_network", }, # - move networks 'configure network move ' => { words => "member=|failover=", func => "cli_move_network", }, 'configure network ' => { words => "split add delete modify join=", }, 'configure network add' => { words => "permission network fixed", }, 'configure network delete' => { words => "permission network fixed", }, 'configure network modify' => { words => "fixed permission", }, 'configure network split' => { words => "", }, 'configure network split ' => { words => " required all view=", func => "cli_split_network", }, 'configure network split required' => { func => "cli_split_network", }, 'configure network split all' => { func => "cli_split_network", }, # - join networks - 'configure network join ' => { words => "network=", }, 'configure network join network ' => { words => "|network=", func => "cli_join_network", }, # [ ] do i need the MAC addr for deleting a network # - delete networks and stuff - # 'configure network delete fixed' => { # words => "=", }, # 'configure network delete fixed ' => { # words => "view=", # func => "cli_del_fixed_address", }, 'configure network delete fixed' => { words => "|=", }, 'configure network delete fixed ' => { words => " view=", func => "cli_del_fixed_address", }, 'configure network delete fixed ' => { words => "view=", func => "cli_del_fixed_address", }, 'configure network delete fixed' => { words => "|=", }, 'configure network delete fixed ' => { words => " view=", func => "cli_del_fixed_address", }, 'configure network delete fixed ' => { words => " view=", func => "cli_del_fixed_address", }, 'configure network delete range' => { words => "=", }, 'configure network delete range ' => { words => " view=", func => "cli_del_dhcp_range", }, 'configure network add network' => { words => "", }, 'configure network add network ' => { words => " addreverse|comment=|member=|option=", func => "cli_add_sub_network", }, 'configure network modify fixed' => { words => "=", }, 'configure network add fixed' => { words => "=", }, 'configure network add fixed ' => { words => " view=|template=|name=|comment=|set=|info=|option=", func => "cli_add_fixed_address", }, 'configure network modify fixed ' => { words => " view=|template=|name=|comment=|set=|info=|option=", func => "cli_add_fixed_address", }, # - fixed without parent 'configure network add fixed' => { words => "=", }, 'configure network modify fixed' => { words => "=", }, 'configure network add fixed ' => { words => " view=|template=|name=|comment=|set=|info=|option=", func => "cli_add_fixed_address", }, 'configure network modify fixed ' => { words => " view=|template=|name=|comment=|set=|info=|option=", func => "cli_add_fixed_address", }, # - ranges 'configure network add range' => { words => "=", }, 'configure network add range ' => { words => " disabled|info=|comment=|exclude=|failover=|member=|option=|filter_option=|macfilter=|view=", func => "cli_add_dhcp_range", }, 'configure network modify range' => { words => "", }, 'configure network modify range ' => { words => " view=|disabled|info=|comment=|exclude=|failover=|member=|option=|filter_option=|macfilter=", func => "cli_add_dhcp_range", }, # radius comm 'configure radius' => { words => "add delete", }, 'configure radius add' => { words => "user= device=", }, 'configure radius add user ' => { words => "password=|comment=", func => "cli_add_radius_user", }, 'configure radius add device ' => { words => "", }, 'configure radius add device ' => { words => " member=|shared_secret=|comment=", func => "cli_add_radius_device", }, 'configure radius delete' => { words => "device= user=", }, 'configure radius delete device ' => { words => " member=|name=", func => "cli_del_radius_device", }, 'configure radius delete user ' => { func => "cli_del_radius_user", }, 'show radius' => { words => "user device ", }, 'show radius user' => { words => " ", func => "cli_show_radius_user", }, 'show radius user ' => { func => "cli_show_radius_user", }, 'show radius device' => { words => " ", func => "cli_show_radius_device", }, 'show radius device ' => { func => "cli_show_radius_device", }, ); # print Dumper ( \%COMMANDS ) ; return %COMMANDS ; } sub debug_commands { my ( $line ) = @_ ; # get the args from the line ( my $type) = $line =~/debug (.*)/ ; if ( $type eq "commands detailed" ) { # dump crap to tty print Dumper ( \%COMMANDS ); } if ( $type eq "commands" ) { foreach my $comm ( sort keys %COMMANDS ) { if ( $COMMANDS{$comm}{'func'} and $COMMANDS{$comm}{'words'} ) { print " $comm $COMMANDS{$comm}{'words'}\n"; } else { print " $comm\n"; } } } if ( $type eq "session" ) { # dump crap to tty print Dumper ( \$SESSION ); } } sub set_debug { my ( $line ) = @_ ; # get the args from the line ( my $level) = $line =~/debug (\S+)/ ; print "DEBUG = $level\n"; $DEBUG = $level ; } =head2 Debugging : configure debug The higher the debug level, the more junk gets spat to the tty. You probably don't want anything higher than (2) unless you are debugging the completion control parser. =cut sub debug_cli { # we're passed a message and a level # if this level is <= the CURRENT DEBUG level # we print the data my $level = shift @_ ; if ($level <= $DEBUG) { # if ( $MAKE ) { print "# " ; } print "($level) DEBUG: @_\n"; } } ############################# # $Log: ibcli,v $ # Revision 3.48 2010/06/11 17:49:08 horne # more fixes for network views # # Revision 3.47 2010/06/11 00:08:50 horne # more fixes and features, see the relnotes # # Revision 3.46 2010/03/22 02:32:45 horne # *** empty log message *** # # Revision 3.45 2010/02/08 21:01:30 horne # *** empty log message *** # # Revision 3.44 2009/09/24 19:56:23 horne # # lots of features # # Revision 3.43 2009/07/27 17:39:31 horne # You can modify views and fixed addreses # Provisional support for roaming hosts # Showing and adding extensible attributes # Showing and adding permissions to objects # # Revision 3.42 2009/03/12 02:15:30 horne # Added UTF 8 support for EA and comments # TTL support for PTR, CNAME and A # corrected bugs in creating Ex Arrtibs # # Revision 3.41 2009/02/20 04:54:36 horne # # modified fixed addresses # # Revision 3.40 2009/01/19 19:50:01 horne # Changed 'Cluster' to 'Grid' # TAB completion and other commands # Extended attributes # Support for more zone types (stub) # Networks can now be added WITHOUT a default member # Move Networks # configure Member DNS settings # configure Member DHCP settings # # Revision 3.39 2008/12/11 22:16:49 horne # BugId: 8205 # Reviewer(s): Geoff # Description: Stub zone support # fixed address with templates # extensible attributes # views for RR sets # mgmt ports for members # adding options spaces and options for those spaces # # Revision 3.38 2008/10/16 09:15:44 horne # BugId: 8205 # Reviewer(s): Geoff # Description: Modify networks, hosts, ranges # patches to device types # upload database now works # # Revision 3.37 2008/08/05 01:20:32 horne # BugId: 8205 # Reviewer(s): Geoff # Description: added adding device types (for the EU folk!) # search for host by IP addr # deleting radius objetcts # # Revision 3.36 2008/07/23 00:41:24 horne # BugId: 8205 # Reviewer(s): Geoff # Description: next_ip and ipam searches # # Revision 3.35 2008/07/11 20:49:21 horne # BugId: 8205 # Reviewer(s): Geoff # Description: permissions and RADIUS stuff # # Revision 3.34 2008/05/07 17:35:25 horne # BugId: 8205 # Reviewer(s): Geoff, Chris B # Description: Added permissions commands # # Revision 3.33 2008/04/24 18:13:05 horne # BugId: 8205 # Reviewer(s): Geoff # Description: global ACL and DHCP opton fixes # Documentation corrections # fixes for the mgmt port # # Revision 3.32 2008/02/15 22:29:51 horne # BugId: 8205 # Reviewer(s): Geoff, Chris B # Description: SRV records, nsgroups and member mgmt # # Revision 3.31 2008/02/04 21:31:25 horne # BugId: 8205 # Reviewer(s): Chris B # Description: added views for other record types # added SRV records # added comments to views # fixed bug in adding members # added 'delete' members # # Revision 3.30 2008/01/28 02:49:23 horne # BugId: 8205 # Reviewer(s): Geoff # Description: # -e exec mode # -w cgi header # client API version checking for some routines # silent loading of a config file # adding ACLS on a member/grid # setting of global DNS settings # modify hosts # modify zones # 'set' operations on a zone # # Revision 3.29 2008/01/15 01:08:18 horne # BugId: 8205 # Reviewer(s): Geoff # Description: added support for shared record groups # you can not modify HOSTS # you can now modify ZONES # # Revision 3.28 2008/01/02 23:17:04 horne # BugId: 8205 # Reviewer(s): Geoff # Description: fixed doc bugs # fixed bugs for 4.2 API # added RADIUS commands # added some other random features # # Revision 3.27 2007/06/07 19:31:47 horne # BugId: 8205 # Reviewer(s): Geoff # Description: fixed some search routines # # Revision 3.26 2007/06/06 20:08:52 rpashby # BugId: 8205 # Added support for adding bulk hosts within a view # # Revision 3.26 2007/06/06 14:40:30 pashby # BugId: 8205 # Reviewer(s): Ron # Description: added fix to enable adding bulk host in views # # Revision 3.25 2007/06/01 20:51:49 horne # BugId: 8205 # Reviewer(s): Geoff # Description: added a patch for the new DHCP option format # # Revision 3.24 2007/06/01 18:28:42 horne # BugId: 8205 # Reviewer(s): Geoff # Description: adds more options for fixed addrs, better reading of db # files # # Revision 3.23 2007/04/27 19:22:30 horne # BugId: 8205 # Reviewer(s): Geoff, Dsmith # Description: added 'prefix' for rfc 2317 zones, added IPAM for fixed # addrs # # Revision 3.22 2007/03/29 11:14:32 rpashby # BugId: 8205 # Added view selection for delete zone # # Revision 3.22 2007/03/29 07:05:00 pashby # BugId: 8205 # Reviewer(s): Ron # Description: Added view selection for removing zones # # Revision 3.21 2007/03/28 19:47:28 horne # BugId: 8205 # Reviewer(s): Geoff # Description: whole lot of features for walking a db file - work is still # incomplete # # Revision 3.20 2007/02/08 04:55:01 horne # BugId: 8205 # Reviewer(s): Geoff (as usual) # Description: added more vervose docs on adding members # added 'disabled' for admins. # # Revision 3.19 2007/01/12 22:33:24 horne # BugId: 8205 # Reviewer(s): geoff # Description: added AAA support and additonal views support # # Revision 3.18 2006/12/01 20:32:23 horne # BugId: 8205 # Reviewer(s): Ron # Description: added disabled A records and non-auth zones # # Revision 3.17 2006/12/01 20:20:49 horne # BugId: 8205 # Reviewer(s): Geoff # Description: additional doc changes, admin users can now be inserted # # Revision 3.16 2006/11/21 21:23:22 horne # BugId: 8205 # Reviewer(s): geoff # Description: delegate_to, stealth_secondary, in-addr.arpa rev zones # # Revision 3.15 2006/11/14 21:53:47 horne # BugId: 8205 # Reviewer(s): geoff # Description: added ipam and DDPI # # Revision 3.14 2006/08/18 19:06:18 horne # BugId: 8205 # Reviewer(s): Geoff # Description: join networks and add members to a grid # # Revision 3.13 2006/08/06 19:08:40 milli # BugId: 8205 # Reviewer(s): milli # Description: conf zone add bulkhost .. ... addreverse # # Revision 3.12 2006/07/30 23:37:21 horne # BugId: 8205 # Reviewer(s): Geoff # Description: you can now set the IP of any membber ( and HA ) # # Revision 3.11 2006/07/27 20:21:01 horne # BugId: 8205 # Reviewer(s): Geoff # Description: added url encoding for comments # added stealth primaries # added disabled hosts # more show grid comands # # Revision 3.10 2006/07/19 15:35:59 horne # BugId: 8205 # Reviewer(s): Geoff # Description: extra show comands # # Revision 3.9 2006/07/14 23:25:43 horne # BugId: 8205 # Reviewer(s): Geoff # Description: added support for exclude ranges # Add lots of grid level features # # Revision 3.8 2006/06/18 04:44:50 horne # BugId: 8205 # Reviewer(s): Geoff # Description: rolled in fixed for networks, added mac filter support # fixed bugs with winders editline, fixed show network # # Revision 3.7 2006/06/05 17:40:34 horne # BugId: 8205 # Reviewer(s): Geoff # Description: added more features, shared networks, comments # # Revision 3.6 2006/06/01 01:58:47 horne # BugId: 8205 # Reviewer(s): Kevin # Description: fixed some require/import problems with constants # # Revision 3.5 2006/05/31 03:27:02 horne # BugId: 8205 # Reviewer(s): Geoff # Description: intial checkin of winders friendly version # # Revision 3.4 2006/05/01 22:58:26 horne # BugId: 8205 # Reviewer(s): Geoff # Description: reverse the default syntax for add zone # # Revision 3.3 2006/05/01 22:48:18 horne # BugId: 8205 # Reviewer(s): geoff # Description: merged in my branch # # Revision 3.2.2.4 2006/05/01 22:44:13 horne # # BugId: 8205 # Reviewer(s): Geoff # Description: added voews to show zone, added nozone to add networks # # Revision 3.2.2.3 2006/02/16 21:46:14 horne # BugId: 8209 # Reviewer(s): Geoff # Description: # added grid settings # added grid nsgroups # added zone nsgroups # added zone email # added zone eternal stealth secondaries # added zone forward_to with members # added mx record support # added multiple ip addresses for a host # # Revision 3.2.2.2 2005/11/23 23:52:14 horne # BugId: 8209 # Reviewer(s): Geoff # Description: views, batch and show support # # Revision 3.2.2.1 2005/09/29 17:20:24 horne # *** empty log message *** # # Revision 3.1.2.5 2005/09/29 17:18:13 horne # *** empty log message *** # # Revision 3.1.2.4 2005/09/26 22:16:29 horne # added support for failover and forwarders # # Revision 3.1.2.3 2005/08/15 20:44:42 horne # added the rest of the perldoc # # Revision 3.1.2.2 2005/08/15 01:23:12 horne # some rearranging of coments, mostly # # Revision 3.1.2.1 2005/08/14 23:31:43 horne # *** empty log message *** # # Revision 3.1 2005/08/14 23:14:23 horne # Bumped the rev# # # Revision 2.11 2005/08/14 23:13:11 horne # *** empty log message *** # # Revision 2.10 2005/08/14 23:07:23 horne # last version, this now gets replaced by complt with Readline # # Revision 2.9 2005/07/13 05:07:06 horne # major fixes of some of the parser bits # added additional functions to add zone # # Revision 2.8 2005/04/22 20:09:17 horne # *** empty log message *** # # Revision 2.7 2005/03/22 01:01:18 horne # *** empty log message *** # # Revision 2.6 2005/03/05 19:27:15 horne # added support for custom options # # Revision 2.5 2004/12/22 20:52:05 horne # fixed the commit prompt # changed commit to 'reatart' # really cleaned up the connection handler # changed all 'set' to 'conf' # cleaned up pod examples # # Revision 2.4 2004/12/20 15:57:03 horne # *** empty log message *** # # Revision 2.3 2004/12/18 12:53:46 horne # Ripped out all the recursive hash print stuff and replaced it with # Data::Dumper instead (much cleaner) # # Revision 2.2 2004/12/18 12:10:13 horne # this version has perldoc # # Revision 2.1 2004/12/14 05:09:01 horne # first round of bug testing # also added member support # also add import zone # # Revision 2.0 2004/11/03 19:58:48 horne # new code suporting the infoblox V3 API # major rewrite # # Revision 1.18 2004/10/20 16:44:39 horne # broken checkin - ignore # # Revision 1.17 2004/08/13 05:21:09 horne # Added correct connection handling # # Revision 1.16 2004/08/13 03:57:45 horne # Major changes to the arg parsers, args are now sent as arrays and the # do_ib_func does the formatting/prettyprinting # Added conection syntax to hanvle V3 connections # Added a whole bag of RADIUS commands # # Revision 1.15 2004/08/01 19:34:03 horne # Added 'peer_assoc' support for V2.5 # # Revision 1.14 2004/06/16 20:19:46 horne # added comments to ptrs # added modify global dhcp opts # conf dhcp global # added fixed addresses # added modify network options # conf net x.x.x.x/mm modify opt value # added non-auth networks # # Revision 1.13 2004/02/13 20:11:21 horne # added the command lister # # Revision 1.12 2003/11/26 01:29:09 horne # *** empty log message *** # # Revision 1.11 2003/11/06 07:31:28 horne # server and password now live on the same commandline # upload and download database # import zone # conf zone add now takes additional options # # the software now writes its own code # # Revision 1.10 2003/10/16 21:41:25 horne # un hardcoede dig # changed password syntax # added a -V -v option (version) # added download commands # # Revision 1.9 2003/09/24 01:43:08 horne # added dhcp range stuff # cleaned up the dig pre-processing # # Revision 1.8 2003/09/22 21:37:30 horne # added prt functions # cleaned up show network # # Revision 1.7 2003/09/22 19:24:30 horne # *** empty log message *** # # Revision 1.6 2003/09/21 23:04:29 horne # added ambiguous commands # prints now wrapped for cli or batch # batch commands now work # # Revision 1.5 2003/09/20 01:22:36 horne # First full working version with the abstracted argument parser. # there is still a lot of gumpf in here, but most of the repeatable code is # now packed into subroutines # # Revision 1.4 2003/09/18 02:28:56 horne # *** empty log message *** # # Revision 1.3 2003/09/16 23:17:25 horne # *** empty log message *** # # Revision 1.2 2003/09/09 22:58:27 horne # *** empty log message *** # # Revision 1.1 2003/08/11 23:31:57 horne # INFOBLOX version, re-set the CVS counters # # Revision 1.9 2001/06/07 16:45:47 horne # major rewrite of the arg parser - ripped out all the RE's to a much better # process. # delete commands disabled # # Revision 1.8 2001/05/31 05:28:15 horne # *** empty log message *** # # Revision 1.7 2001/05/24 00:17:54 horne # Added RE's to handle shortened commant words, not however that this is # still buggy, typos, as long as they are unique will still work. thus # - password, p , pa , ps , pdfgt = password # I need to find a better command parser # # Revision 1.6 2001/05/23 19:39:11 horne # working version - waiting for updates from infoblox to see if they have # changed the API syntax # # Revision 1.5 2001/05/19 06:08:13 horne # iblox ready version, but no connections to servers work # # Revision 1.4 2001/05/19 04:59:19 horne # All base commands are being parsed and netmasks are being applied. # Now we need to hit the infoblox engine for real # # Revision 1.3 2001/05/19 03:43:45 horne # added the DEBUG option before i started to drown in a lot of messy print # statements # # Revision 1.2 2001/05/19 03:34:34 horne # Basic version, config file parsing is functional. # # Revision 1.1 2001/05/19 03:33:56 horne # *** empty log message *** #