From ae857533a2ac3f74744b23752fa492a65e4f3bc6 Mon Sep 17 00:00:00 2001 From: Krystian Baniak Date: Tue, 2 Mar 2021 11:41:17 +0100 Subject: [PATCH] vesrion 1.4.2: added ZoneRunner support --- README.md | 74 ++++++++++++++-- f5-cfg | 169 ++++++++++++++++++++++++++++++++--- lib/CAudit.pm | 238 +++++++++++++++++++++++++++++++++++--------------- lib/COpts.pm | 6 ++ 4 files changed, 401 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 5fac54c..5c5fe80 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ Uses iControl SOAP and iControlREST API on F5 devices to facilitate operations a - automate configuration tasks using a batch mode. This tool accepts json formatted batch files that specify actions to perform on a remote F5 system(s), - run reports that collect information from selected configuration entities and present it in a tabular form, - REST API troubleshooting and debug. +- *new* manage ZoneRunner dns zones from command line and in a batch mode. -The primary application of this toll is to automate frequently used maintenance tasks, perform config audits and ensure maitenence window time is kept to minimum by eliminating human error factor. +The primary application of this tool is to automate frequently used maintenance tasks, perform config audits and ensure maitenence window time is kept to minimum by eliminating human error factor. Supported features and software versions: - F5 software versions >= 11.x @@ -29,9 +30,55 @@ Supported features and software versions: Developped in perl script language, may be used as standalone script or in a docker/podman container - see instalation notes below. +## Usage + +Just run `f5-cfg -h` to see all availabel options. + +One of the basic needs is to fast recon on the F5 device and provide summary of what we are dealing with. We may use `-i -k` options together with other mandatory switches: +- `-f` : asks for password +- `-u admin` : specify user account to connect with +- `-t IP_address` : target is the management interface's IP address of the F5 device + +The result of this command is the report of iRules their digests as well as allocations of iRules to virtual servers. + +``` +f5-cfg -t 10.10.10.10 -u admin -f -i -k +. runtime location: /Images/Shared/f5-rest-tools, rundir: /home/krystian + +(C) 2020, Krystian Baniak , F5 restful configuration tool, version: 1.4.9 +> enter f5 device password please : ++ host 172.16.24.29 mapped to: [ 172.16.24.29 ] +system properties: + baseMac: 00:0c:29:68:14:96 + bigipChassisSerialNum: 564dd107-0f67-11be-804044681496 + fovState: active + marketingName: BIG-IP Virtual Edition + platform: Z100 + active volume: HD1.1 + soft build: 0.0.9 + soft version: 15.1.2 ++ analysing iRules and their alloactions ... + + name | partition | hash +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + test_sip_sreening | Common | b72fa4e5723e3b4cbe4dac1215d44edea2161e54 +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + + name | partition | application service | list of numbered iRules +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + VS_DNS_UDP_GN | Common | - | + VS_SIP_TEST | Common | - | [ 1] /Common/test_sip_sreening +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + ++ unused iRules: +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +``` + ## Installation First, clone the repository on your computer. Afterwards, follow the procedure: -``` + +```{bash} git clone https://github.com/kbaniak/f5-cfg-tool cd f5-cfg-tool ./f5-cfg -h @@ -39,14 +86,19 @@ cd f5-cfg-tool ## Docker/podman installation and use Compile a podman image + ``` podman build --format=docker -t f5-cfg . ``` + Run a container: + ``` podman run --rm -it f5-cfg bash ``` + When inside a container (using bash shell) one may invoke a f5-cfg tool, like on these example that is used to create UCS archive on an F5: + ``` podman run -it --rm f5-cfg bash [root@d045ed96d5dd migration]# f5-cfg -t 10.128.1.47 -B MAKE_UCS -Oucs_secret=test123 @@ -64,14 +116,18 @@ podman run -it --rm f5-cfg bash ++ downloading resource: migrate-auto-10.128.1.47-020620-171800.ucs ++ total bytes transferred: 7538951 ``` + Now the file will be stored in the local directory: + ``` [root@e5728b9f8c42 migration]# ls migrate-auto-10.128.1.47-020620-171800.ucs [root@e5728b9f8c42 migration]# ``` + ### Notes for Fedora (32) podman selinux -When one wants to bind a local forlder to migration directory of the container/pod we have to relabel the source directory. + +When one wants to bind a local folder to a migration directory of the container/pod we have to relabel the source directory. Let's assume we have ./test directory. ``` @@ -87,7 +143,8 @@ test/ 0 directories, 1 file ``` -## Examples +## Examples and use cases + ### Inspect F5 device manifest and print list of iRules ``` ./f5-cfg -t 1.1.1.1 -u admin -p admin -i -k @@ -243,9 +300,9 @@ Batch file contains sections that govern how it is processed: supress_log : supress log audit to a file ucs_secret : passphrase used to encrypt ucs archive for MAKE_UCS batch command - list of known batch commands ABORT : abort at a given step + ADD_ZONE_A:zone:view:name:ip:ttl: add ZoneRunner A resource record COMMAND : execute shell comamnd: [ Array(command) ] COMPARE_DBSET : compare db vars on target system with definitions from a batch file COMPARE_RSETS : compare irule sets (partitions) with local files @@ -254,7 +311,11 @@ Batch file contains sections that govern how it is processed: CSET:name : command set reference, name indicated named command set to be invoked DELAY:seconds : wait seconds before continuing DELETIONS : delete objects from the f5: [ Hash(delete) of [type,priority] ] + DEL_ZONE_A:zone:view:name:ip:ttl: delete ZoneRunner A resource record DOWNLOAD : download file from remote system + GET_ZONES : list ZoneRunner zones + GET_ZONE_INFO:view : get ZoneRunner zone information + GET_ZONE_RRS:zone:view: get ZoneRunner resource records information LOADTMSH : load config file and merge it: [ Array(tmsh-merge) ] LOAD_DEFAULT : load sys config default LOAD_DG : load data groups type external: [ Hash(datagroup) of { source } ] @@ -265,8 +326,8 @@ Batch file contains sections that govern how it is processed: LOAD_RULES : load iRules from baseline directory: [ Hash(rules) of { priority } ] MAKE_SCF : create single configuration file backup (inlcuding a tar file) MAKE_UCS : create and download ucs archive - MSET:name : merge set reference, name indicates named mereg set to be invoked MCPD_FORCELOAD : marks mcpd forceload flag for the next reboot + MSET:name : merge set reference, name indicates named mereg set to be invoked REBIND_VS : attach iRule to virtual servers: [ Hash(virtuals) of { site, rules } or [] ] REBOOT : reboots current host (all blades) RECERT : create iRule certificates @@ -284,6 +345,7 @@ Batch file contains sections that govern how it is processed: VERIFY_SET:name : run verification procedure on a verifyset. Verify set must inlude a list of objects that specify tpe and set of items to check WAIT_FOR:event : waits for event to happen: cluster node become event = { online, standby, active } + ZRSET:name : process records from a named zonerunner list list of batch options to be used in options section in json definition: base_location : indicates directory where to look for resource files diff --git a/f5-cfg b/f5-cfg index 0b5b0c9..7f31298 100755 --- a/f5-cfg +++ b/f5-cfg @@ -46,7 +46,7 @@ use CAudit; use CCache; use COpts; -my $version = '1.4.0'; +my $version = '1.4.2'; # --------- DEB ----------------- my $debug_vs = 0; @@ -115,6 +115,7 @@ if ($opts{'h'}){ -s : save config -S : sync cluster -w : working directory + -l name : affect -a and -A by unifying the name of the resource -b name : batch mode that uses json file as input -B steps : semicolon delimited list of steps, mutually exclusive with -b option -Z step : select step set from a step set list, used only with a -b option @@ -558,9 +559,15 @@ sub update_host { } # update all globals - $URL_BARE = "https://$host/"; - $URL = "https://$host/mgmt/tm/"; - $URLB = "https://$host/mgmt/shared/authn/login"; + if ($cport != 443) { + $URL_BARE = "https://$host:$cport/"; + $URL = "https://$host:$cport/mgmt/tm/"; + $URLB = "https://$host:$cport/mgmt/shared/authn/login"; + } else { + $URL_BARE = "https://$host/"; + $URL = "https://$host/mgmt/tm/"; + $URLB = "https://$host/mgmt/shared/authn/login"; + } unless ($opts{'Q'}) { print $tee "+ host $host_name mapped to: [ $host ]\n"; } # perform auth cache scan if needed @@ -1589,7 +1596,7 @@ sub batch_mode { my $j = { options => { }, - steps => qw() + steps => [ ] }; if ($md eq 'file') { @@ -1704,7 +1711,7 @@ sub batch_mode { my @otgt = split( ':', $step); my $cindex = 0; my $cabrt = 1; - if ($#otgt == 1) { + if ($#otgt >= 1) { switch ($otgt[0]) { case 'DELAY' { sleep($otgt[1]); @@ -1793,6 +1800,104 @@ sub batch_mode { } } } + case 'GET_ZONE_INFO' { + my $view = $otgt[2] || 'external'; + my $zone = $otgt[1]; + print $tee "+-- [step: $step] geting ZoneRunner zone information: $view/$zone\n"; + my $k = new CAudit( $host, $usr, $pass, $cport ); + my $zs = $k->getZoneInfo($view, $zone); + print $tee "zone \033[32m$zone\033[0m properties in view: \033[32m$view\033[0m\n"; + foreach my $prop (sort keys %{ $zs->[0] }) { + if ($prop eq 'option_seq') { + my $seqs = join(@{ $zs->[0]{$prop} }, ' '); + print $tee " $prop: $seqs\n"; + } else { + print $tee " $prop: $zs->[0]{$prop}\n"; + } + } + } + case 'GET_ZONE_RRS' { + my $view = $otgt[2] || 'external'; + my $zone = $otgt[1]; + print $tee "+-- [step: $step] geting ZoneRunner rrs information: $view/$zone\n"; + my $k = new CAudit( $host, $usr, $pass, $cport ); + my $zs = $k->getZoneRecords($view, $zone); + print $tee "zone \033[32m$zone\033[0m properties in view: \033[32m$view\033[0m\n"; + foreach my $rec (sort @{ $zs->[0] }) { + print $tee " $rec\n"; + } + } + case 'ADD_ZONE_A' { + # params: zone:view:name:ip:ttl + my $zone = $otgt[1]; + my $view = $otgt[2]; + my $ttl = $otgt[5] || 0; + print $tee "+-- [step: $step] adding record of type A to ZoneRunner: $view/$zone\n"; + my $k = new CAudit( $host, $usr, $pass, $cport ); + my @rrs = qw(); + push @rrs, { domain_name => $otgt[3], ip_address => $otgt[4], ttl => $ttl }; + my $zs = $k->processZoneRecord('create', $view, $zone, 'A', \@rrs ); + print $tee "result: $zs\n"; + } + case 'DEL_ZONE_A' { + # params: zone:view:name:ip:ttl + my $zone = $otgt[1]; + my $view = $otgt[2]; + my $ttl = $otgt[5] || 0; + print $tee "+-- [step: $step] removing record of type A from ZoneRunner: $view/$zone\n"; + my $k = new CAudit( $host, $usr, $pass, $cport ); + my @rrs = qw(); + push @rrs, { domain_name => $otgt[3], ip_address => $otgt[4], ttl => $ttl }; + my $zs = $k->processZoneRecord('delete', $view, $zone, 'A', \@rrs ); + print $tee "result: $zs\n"; + } + case 'ZRSET' { + my $err = 0; + print $tee "+-- [step: $step] processing ZoneRunner set: $otgt[1]\n"; + if ((defined $j->{'zoneset'}) && (defined $j->{'zoneset'}{$otgt[1]})) { + my $k = new CAudit( $host, $usr, $pass, $cport ); + my $idx = 1; + if (exists $j->{'zoneset'}{$otgt[1]}) { + foreach my $kx ( @{ $j->{'zoneset'}{$otgt[1]} }) { + # process operation set + print $tee "opset [$idx] "; + if (exists($kx->{'type'}) and exists($kx->{'view'}) and exists($kx->{'db'})) { + # zone records can be given in a form of a file + if (exists($kx->{'items'}) and exists($kx->{'class'})) { + # items in a table and confined to given record type + my $result = $k->processZoneRecord($kx->{'type'}, $kx->{'view'}, $kx->{'db'}, $kx->{'class'}, $kx->{'items'}); + print $tee "--> $result\n"; + $idx++; + } elsif (exists($kx->{'itemSource'}) and ($kx->{'itemSource'} ne '')) { + # items in a separate file + my $js = parseJsonFile($j->{'options'}, $kx->{'itemSource'}); + foreach my $rtype (keys %{ $js->{'records'} }) { + print $tee "processing records: $rtype\n"; + my $result = $k->processZoneRecord($kx->{'type'}, $kx->{'view'}, $kx->{'db'}, uc($rtype), $js->{'records'}{$rtype}); + print $tee "($rtype)--> $result\n"; + } + $idx++; + } else { + $err = 3; + print $tee "--> \033[31mcorrupted\033[0m\n"; + last; + } + } else { + $err = 2; + print $tee "--> \033[31mfailed\033[0m\n"; + last; + } + } + } else { + $err = 1; + } + } + if ($err > 0) { + print $tee "\033[31m!malformed zoneset definition found ($err)! \033[0m\n"; + } else { + print $tee "complete \n"; + } + } case 'DON_PROVISION' { print $tee "+-- [step: $step] processing declarative onboarding set: $otgt[1]\n"; if ((defined $j->{'clusterset'}) && (defined $j->{'clusterset'}{$otgt[1]})) { @@ -1917,7 +2022,7 @@ sub batch_mode { } print $tee " . ucs saved into: $z->{name}\n"; if ($wDir ne '') { - $k->downloadResource('ucs', $z->{name} . ".ucs", $wDir . $z->{name} . ".ucs" ); + $k->downloadResource($j->{'options'}, 'ucs', $z->{name} . ".ucs", $wDir . $z->{name} . ".ucs" ); } else { print $tee "+-- note: working dir is not set, we do not download ucs!\n"; } @@ -1961,8 +2066,8 @@ sub batch_mode { } } if ($wDir ne '') { - $k->downloadResource('file', "/var/local/scf/" . $fname, $wDir . $fname ); - $k->downloadResource('file', "/var/local/scf/" . $fname . ".tar", $wDir . $fname . ".tar" ); + $k->downloadResource($j->{'options'}, 'file', "/var/local/scf/" . $fname, $wDir . $fname ); + $k->downloadResource($j->{'options'}, 'file', "/var/local/scf/" . $fname . ".tar", $wDir . $fname . ".tar" ); } else { print $tee "+-- note: working dir is not set, we do not download scf!\n"; } @@ -2310,8 +2415,20 @@ sub batch_mode { foreach my $fl (sort keys %{ $j->{'retrieve'} }) { print $tee " . resource from a location: $j->{'retrieve'}{$fl}\n"; my $k = new CAudit( $host, $usr, $pass, $cport ); - $k->downloadResource( 'file', $j->{'retrieve'}{$fl}, $j->{'options'}{'store_location'} . '/' . $fl ); + $k->downloadResource($j->{'options'}, 'file', $j->{'retrieve'}{$fl}, $j->{'options'}{'store_location'} . '/' . $fl ); } + } else { + print $tee "\033[31m! store_location or retrieve set not given\033[0m \n"; + } + } + case 'GET_ZONES' { + print $tee "+-- [step: $step] geting ZoneRunner zone names\n"; + my $k = new CAudit( $host, $usr, $pass, $cport ); + my $view = 'external'; + my $zs = $k->getZones($view); + print $tee "configured zones in view \033[32m$view\033[0m\n"; + foreach my $zn (sort @{ $zs }) { + print $tee " $zn->{'zone_name'}\n"; } } else { @@ -2320,6 +2437,38 @@ sub batch_mode { } # SWITCH } } + +# determine the file location in a batch +sub parseJsonFile +{ + my ($opts, $fname) = @_; + my $json = qw(); + if (defined($opts->{"base_location"})) { + my $localFile = $opts->{"base_location"} . $fname; + if (! -e $localFile) { + if (defined($opts->{"search_path"})) { + # + # TODO: cycle through search locations, so far only first item from the list + foreach my $subpath (@{ $opts->{'search_path'} }) { + $localFile = $opts->{"base_location"} . $subpath . "/" . $fname; + if (-e $localFile) { + last; + } + } + } + } + if (-e $localFile) { + my $content; + open my $fh, "<", $localFile or terminate_with_error($tee, 'file open', "Error opening resource file: $localFile"); + read $fh, my $buffer, -s $fh; + close($fh); + $json = decode_json( $buffer ); + } + } + return $json; +} + +# parse filename from a path sub parseFileToken { my @fn = split( '/', shift ); diff --git a/lib/CAudit.pm b/lib/CAudit.pm index 7b09f25..c0ecd98 100644 --- a/lib/CAudit.pm +++ b/lib/CAudit.pm @@ -1,16 +1,33 @@ package CAudit; # -# (C) 2016 Krystian Baniak -# +# (C) 2020 Krystian Baniak +# krystian.baniak@exios.pl +# Resource library for F5 iControl SOAP functions # +use Switch; +use Data::Dumper; +#use SOAP::Lite +trace => 'all'; use SOAP::Lite; use MIME::Base64; BEGIN { push (@INC, "./lib"); } use iControlTypeCast; +sub parseZoneBatch +{ + my ( $object ) = @_; + my $verdict = { + result => 1, + errstr => '' + }; + + return $verdict; +} + +# +# CAudit class sub new { my $class = shift; @@ -24,12 +41,15 @@ sub new return $self; } -sub getVersion +# +# create soap handle for given iControl scope +# by default it is a SystemInfo scope +sub createHandle { - my ($self) = @_; - + my ($self, $scope) = @_; + my $icrScope = $scope || 'System/SystemInfo'; my $icHandle = SOAP::Lite - -> uri('urn:iControl:System/SystemInfo') + -> uri('urn:iControl:' . $icrScope) -> readable(1) -> proxy("https://$self->{'_host'}:$self->{'_port'}/iControl/iControlPortal.cgi"); @@ -37,57 +57,150 @@ sub getVersion $icHandle->transport->http_request->header( 'Authorization' => 'Basic ' . MIME::Base64::encode("$self->{'_user'}:$self->{'_pass'}", '') ); + return $icHandle; +} +# +# verify soapResponse +sub verifySoapResponse +{ + my ($self, $soapResponse) = @_; + if ( $soapResponse->fault ) + { + return "soap call failed with: " . $soapResponse->faultcode . ", " . $soapResponse->faultstring; + } else { + return $soapResponse->result ? $soapResponse->result : 'done'; + } +} + +# +# get BIG-IP version +sub getVersion +{ + my ($self) = @_; + my $icHandle = $self->createHandle(); my $soapResponse = $icHandle->get_version(); + return $soapResponse->result; } -sub createUcs +# +# Get DNS ZoneRunner zones for given view +sub getZones { - my ($self, $name, $passw) = @_; + my ($self, $view) = @_; + my $icHandle = $self->createHandle('Management/Zone'); + my $soapResponse = $icHandle->get_zone_name + ( + SOAP::Data->name( 'view_names' => [ $view ] ) + ); + return $self->verifySoapResponse( $soapResponse ); +} - my $icHandle = SOAP::Lite - -> uri('urn:iControl:System/ConfigSync') - -> readable(1) - -> proxy("https://$self->{'_host'}:$self->{'_port'}/iControl/iControlPortal.cgi"); +# +# Get DNS Zone information +sub getZoneInfo +{ + my ($self, $view, $zone) = @_; + my $icHandle = $self->createHandle('Management/Zone'); + my $soapResponse = $icHandle->get_zone_v2 + ( + SOAP::Data->name( 'view_zones' => [ { 'zone_name' => $zone, 'view_name' => $view }] ) + ); + return $self->verifySoapResponse( $soapResponse ); +} - $icHandle->transport->ssl_opts( verify_hostname => 0, SSL_verify_mode => 0x00 ); - $icHandle->transport->http_request->header( - 'Authorization' => 'Basic ' . MIME::Base64::encode("$self->{'_user'}:$self->{'_pass'}", '') +# +# install new resource record for given zone and view +sub processZoneRecord +{ + my ($self, $action, $view, $zone, $rtype, $object) = @_; + my $icHandle = $self->createHandle('Management/ResourceRecord'); + my @records = @{ $object }; + my $soapResponse; + + unless ( grep( /^$action$/, ( 'create', 'delete' ) ) ) { + return "unsupported action"; + } + + unless ( grep( /^$rtype$/, ( 'A', 'AAAA', 'CNAME', 'NAPTR', 'SRV', 'MX' ) ) ) { + return "unsupported resource record type"; + } + + foreach my $rs (@records) { + unless ($rs->{'domain_name'} =~ /.+$zone\$/) { + $rs->{'domain_name'} .= ".$zone"; + } + unless (exists $rs->{'ttl'}) { + $rs->{'ttl'} = 0; + } + if ($rtype eq 'NAPTR' and $rs->{'regexp'} eq '') { + $rs->{'regexp'} = '""'; + } + } + + my $rt = lc ($rtype); + my $method = $action eq 'create' ? 'add' : 'delete'; + $method .= '_' . $rt; + + my @args = ( + SOAP::Data->name( 'view_zones' => [ { 'zone_name' => $zone, 'view_name' => $view } ] ), + SOAP::Data->name( "${rt}_records" => [ \@records ] ), + ); + + if ($rt eq 'a' || $rt eq 'aaaa') { + push @args, SOAP::Data->name( 'sync_ptrs' => [ 0 ]) + } + + $soapResponse = $icHandle->$method(@args); + return $self->verifySoapResponse( $soapResponse ); +} + +# +# Get Zone resource records +sub getZoneRecords +{ + my ($self, $view, $zone) = @_; + my $icHandle = $self->createHandle('Management/ResourceRecord'); + my $soapResponse = $icHandle->get_rrs + ( + SOAP::Data->name( 'view_zones' => [ { 'zone_name' => $zone, 'view_name' => $view }] ) ); + return $self->verifySoapResponse( $soapResponse ); +} +# +# create an ucs archive +sub createUcs +{ + my ($self, $name, $passw) = @_; + my $icHandle = $self->createHandle('System/ConfigSync'); + my $soapResponse; + if ($passw and $passw ne "") { - my $soapResponse = $icHandle->save_encrypted_configuration + $soapResponse = $icHandle->save_encrypted_configuration ( SOAP::Data->name( 'filename' => $name ), SOAP::Data->name( 'save_flag' => 'SAVE_FULL' ), SOAP::Data->name( 'passphrase' => $passw ) ); - return $soapResponse->result; } else { - my $soapResponse = $icHandle->save_configuration + $soapResponse = $icHandle->save_configuration ( SOAP::Data->name( 'filename' => $name ), SOAP::Data->name( 'save_flag' => 'SAVE_FULL' ) ); - return $soapResponse->result; } + + return $self->verifySoapResponse( $soapResponse ); } +# +# create scf archive sub createScf { my ($self, $name, $passw) = @_; - - my $icHandle = SOAP::Lite - -> uri('urn:iControl:System/ConfigSync') - -> readable(1) - -> proxy("https://$self->{'_host'}:$self->{'_port'}/iControl/iControlPortal.cgi"); - - $icHandle->transport->ssl_opts( verify_hostname => 0, SSL_verify_mode => 0x00 ); - $icHandle->transport->http_request->header( - 'Authorization' => 'Basic ' . MIME::Base64::encode("$self->{'_user'}:$self->{'_pass'}", '') - ); - + my $icHandle = $self->createHandle('System/ConfigSync'); my $soapResponse = $icHandle->save_single_configuration_file ( SOAP::Data->name( 'filename' => $name ), @@ -95,23 +208,15 @@ sub createScf SOAP::Data->name( 'passphrase' => $passw ), SOAP::Data->name( 'tarfile' => '' ) ); - return $soapResponse->result; + return $self->verifySoapResponse( $soapResponse ); } +# +# delete file from a remote system sub deleteResource { my ($self, $name, $type) = @_; - - my $icHandle = SOAP::Lite - -> uri('urn:iControl:System/ConfigSync') - -> readable(1) - -> proxy("https://$self->{'_host'}:$self->{'_port'}/iControl/iControlPortal.cgi"); - - $icHandle->transport->ssl_opts( verify_hostname => 0, SSL_verify_mode => 0x00 ); - $icHandle->transport->http_request->header( - 'Authorization' => 'Basic ' . MIME::Base64::encode("$self->{'_user'}:$self->{'_pass'}", '') - ); - + my $icHandle = $self->createHandle('System/ConfigSync'); my $soapResponse; print "++ removing resource: $name\n"; @@ -129,23 +234,25 @@ sub deleteResource return 0; } - return $soapResponse->result; - + return $self->verifySoapResponse( $soapResponse ); } +# +# download a file from a remote system sub downloadResource { - my ($self, $type, $configName, $localFile) = (@_); - - my $icHandle = SOAP::Lite - -> uri('urn:iControl:System/ConfigSync') - -> readable(1) - -> proxy("https://$self->{'_host'}:$self->{'_port'}/iControl/iControlPortal.cgi"); - - $icHandle->transport->ssl_opts( verify_hostname => 0, SSL_verify_mode => 0x00 ); - $icHandle->transport->http_request->header( - 'Authorization' => 'Basic ' . MIME::Base64::encode("$self->{'_user'}:$self->{'_pass'}", '') - ); + my ($self, $opts, $type, $configName, $fname) = (@_); + my $icHandle = $self->createHandle('System/ConfigSync'); + my $soapResponse; + + my $localFile = $fname; + if (defined($opts)) { + if (defined($opts->{"base_location"})) { + if (substr($fname,0,2) eq './') { + $localFile = $opts->{"base_location"} . $fname; + } + } + } open (FH, ">$localFile") or die("Can't open $localFile for output: $!"); binmode(FH); @@ -200,14 +307,17 @@ sub downloadResource return 1; } +# +# upload a file to a remote system sub uploadFile { my ($self, $opts, $fname, $fileName) = (@_); + my $icHandle = $self->createHandle('System/ConfigSync'); + my $soapResponse; my $bContinue = 1; my $chain_type = "FILE_FIRST"; - my $preferred_chunk_size = 65536/2; - my $chunk_size = 65536/2; + my $chunk_size = 1e3 * 1024; my $total_bytes = 0; my $localFile = $fname; @@ -229,18 +339,6 @@ sub uploadFile } } - # print "+ final local resource path is: $localFile\n"; - - my $icHandle = SOAP::Lite - -> uri('urn:iControl:System/ConfigSync') - -> readable(1) - -> proxy("https://$self->{'_host'}:$self->{'_port'}/iControl/iControlPortal.cgi"); - - $icHandle->transport->ssl_opts( verify_hostname => 0, SSL_verify_mode => 0x00 ); - $icHandle->transport->http_request->header( - 'Authorization' => 'Basic ' . MIME::Base64::encode("$self->{'_user'}:$self->{'_pass'}", '') - ); - open(FH, "< $localFile") or die("Can't open $localFile for input: $!"); binmode(FH); @@ -248,7 +346,7 @@ sub uploadFile $file_data = ""; $bytes_read = read(FH, $file_data, $chunk_size); - if ( $preferred_chunk_size != $bytes_read ) { + if ( $chunk_size != $bytes_read ) { if ( $total_bytes == 0 ) { $chain_type = "FILE_FIRST_AND_LAST"; } else { diff --git a/lib/COpts.pm b/lib/COpts.pm index 4fe5df8..9f8fe3f 100644 --- a/lib/COpts.pm +++ b/lib/COpts.pm @@ -66,6 +66,12 @@ my $BDEFS = { "RESTART" => { usage => "runs clsh bigstart restart on current host" }, "WAIT_FOR:event" => { usage => "waits for event to happen: cluster node become event = { online, standby, active }" }, "MCPD_FORCELOAD" => { usage => "marks mcpd forceload flag for the next reboot" }, + "GET_ZONES" => { usage => "list ZoneRunner zones" }, + "GET_ZONE_INFO:view" => { usage => "get ZoneRunner zone information" }, + "GET_ZONE_RRS:zone:view" => { usage => "get ZoneRunner resource records information" }, + "ADD_ZONE_A:zone:view:name:ip:ttl" => { usage => "add ZoneRunner A resource record" }, + "DEL_ZONE_A:zone:view:name:ip:ttl" => { usage => "delete ZoneRunner A resource record" }, + "ZRSET:name" => { usage => "process records from a named zonerunner list" }, }; my $BOPTS = {