Skip to content

Commit

Permalink
vesrion 1.4.2: added ZoneRunner support
Browse files Browse the repository at this point in the history
  • Loading branch information
kbaniak committed Mar 2, 2021
1 parent a7ef4bc commit ae85753
Show file tree
Hide file tree
Showing 4 changed files with 401 additions and 86 deletions.
74 changes: 68 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -29,24 +30,75 @@ 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 <[email protected]>, 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
```

## 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
Expand All @@ -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.
```
Expand All @@ -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
Expand Down Expand Up @@ -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 <dbvars> from a batch file
COMPARE_RSETS : compare irule sets (partitions) with local files
Expand All @@ -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 } ]
Expand All @@ -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
Expand All @@ -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
Expand Down
169 changes: 159 additions & 10 deletions f5-cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1589,7 +1596,7 @@ sub batch_mode {

my $j = {
options => { },
steps => qw()
steps => [ ]
};

if ($md eq 'file') {
Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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]})) {
Expand Down Expand Up @@ -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";
}
Expand Down Expand Up @@ -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";
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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 );
Expand Down
Loading

0 comments on commit ae85753

Please sign in to comment.