bash
50 TopicsRemoving AAM/WAM for a successful upgrade
If you are wanting to upgrade to version 16 or 17 of BIG-IP, one thing that can cause your config not to load, is any element of AAM/WAM/WOM. As I discovered via a customer of mine, even removing all AAM/WAM items from traffic objects is not enough. While I know how to identify things in the conf files and can see them in iHealth, that doesn't help Admins in the field assess if this is an issue for them, and if it is, how to document what needs to be changed for the necessary approvals. With some help, I wrote this knowledge article to meet these needs as well as provide a way to quickly make the changes - https://my.f5.com/manage/s/article/K000149084 I am sharing this in the forum to not only advertise this, but explain some of the commands and help the community understand how they might be used for other tasks. From spending time running a few BIG-IPs myself in a prior life and working with hundreds of customers, I knew that my solution needed to address partitions and even iApps. My coworker Fernando C provided me the syntax to crawl every partition and I quickly found ways to morph that into this document. Lets take a look at the syntax that can read the lan TCP profiles in the Common partition and then see the changes needed to read all partitions. In order to filter the results a bit better we run these from bash so that we have access to a number of tools like grep, awk, sed, etc. # Return all virtual server names in Common that use a TCP Profile from wam or wom (aka AAM) # grep to find the profile prefixes and then piping that to AWK to grab the third word in the output of each line tmsh list ltm virtual one-line | grep -E "(profiles.*(w(a|o)m-tcp-lan*))" | awk '{print $3}' This simply returns the virtual server name without the partition name. Now to read all partitions, the tmsh portion of the command has to change. Specifically, we pass the -c option to tmsh to tell it to run multiple commands. When you enter tmsh, by default you are in the Common partition, so we have to back out to the root. Because we are in the root directory, we need to add the recursive option to read all subfolders which in this case are the partitions. #Read all partitions and filter for virtual servers that use the wam/wom TCP profiles on the lan or server side tmsh -c 'cd /; list ltm virtual recursive one-line' | grep -E "(profiles.*(w(a|o)m-tcp-lan*))" | awk '{print $3}' Now the output is the partition name and virtual server name, or if iApps are involved, the appservice name as well. You can take the output from the first command and pass it to xarg to use your output as a variable in a command to execute. CAUTION, the following command will attempt to make changes to your config. #Read all partitions and filter for virtual servers that use the wam/wom TCP profiles on the lan or server side then insert new profiles and delete the original profile #This will cause an error tmsh -c 'cd /; list ltm virtual recursive one-line' | grep -E "(profiles.*(w(a|o)m-tcp-lan*))" | awk '{print $3}' | xargs -t -I vsName tmsh modify ltm virtual vsName profiles add { f5-tcp-lan { context serverside } } profiles delete { wam-tcp-lan-optimized } If you run this command, it will error out, because without the proper syntax, tmsh assumes you are referencing objects in the /Common partition and as a result it will help you by implicitly adding that to the beginning of every object in your xarg command. I added the -t option to xarg to output the command that it will execute. To correct the syntax error, in the awk command, you add a forward slash and now tmsh will treat your command as if you have explicitly declared the partition name for every object. Caution - This will make changes to your configuration, very fast... #Read all partitions and filter for virtual servers that use the wam/wom TCP profiles on the lan or server side then insert new profiles and delete the original profile #CAUTION - This will make changes to your system. tmsh -c 'cd /; list ltm virtual recursive one-line' | grep -E "(profiles.*(w(a|o)m-tcp-lan*))" | awk '{print "/" $3}' | xargs -t -I vsName tmsh modify ltm virtual vsName profiles add { f5-tcp-lan { context serverside } } profiles delete { wam-tcp-lan-optimized } When I first hit the wall with xarg beyond the /Common partition, I did not realize what the fix was. However my OCD wanted to see a slash in front of the partition name and I had modified the awk to add it, but had given up on the xarg to modify things outside of /Common. It wasn't until I went to show the error to a peer, Chad T., that I discovered I stumbled upon the proper syntax, and realized I could simplify the instructions quite a bit. Where I would love some help from the community would be on ways to crawl the iApps to quickly disable Strict Updates. The xarg commands to modify/delete objects associated with an iApp will fail if the default setting of "Strict Updates" is enabled. Hope this helps, Carl132Views3likes3CommentsVIPRION external monitor
Problem this snippet solves: This VIPRION specific external monitor script is written in bash and utilizes TMSH to extend the built-in monitoring functionality of BIG-IP version 10.2.3. This write-up assumes the reader has working knowledge writing BIG-IP LTM external monitors. The following link is a great starting point LTM External Monitors: The Basics | DevCentral Logical network diagram: NOTE: The monitor is written to meet very specific environmental requirements. Therefore, your implementation may vary greatly. This post is inteded to show you some requirements for writing external monitors on the VIPRION platform while offering some creative ways to extend the functionality of external monitors using TMSH. The VIPRION acts as a hop in the default path of traffic destined for the Internet. Specific application flows are vectored to optimization servers and all other traffic is passed to the next hop router (Router C) toward the Internet. Router A and Router C are BGP neighbors through the VIPRION. Router B is a BGP neighbor with the VIPRION via ZebOS. A virtual address has route health injection enabled. The script monitors a user defined (agrument to the script) pool and transitions into the failed state when the available pool member count drops below a threshold value (argument to the script). In the failed state the following actions are performed once, effectively stopping client traffic flow through the VIPRION. Two virtual servers (arguments to the script) are disable to stop traffic through VIPRION. A virtual address (argument to the script) is disabled to disable route health injection of the address. All non Self-IP BGP connections are found in the connection table and deleted. NOTE: Manual intervention is required to enable virtual servers and virtual address when the monitor transitions from failed state to successful state before normal traffic flows will proceed. How to use this snippet: The monitor definition: monitor eavbgpv3 { defaults from external interval 20 timeout 61 args "poolhttp 32 vsforward1 vsforward2 10.10.10.1"v DEBUG "0"v run "rhi_v3.bsh" } This external monitor is configured to check for available members in the pool "poolhttp". When the available members falls below 32 the monitor transistions into the failed state and disables the virtual servers "vsforward1" and "vs_forward2" and disables the virtual address "10.10.10.1". When the available pool members increases above 32 neither the virtuals servers nor the virtual address is enabled. This will require manual intervention. The external monitor is assigned to a phantom pool with a single member "1.1.1.1:4353". No traffic is sent to the pool member. This pool and pool member are in place so the operator can see the current status of the external monitor. The Pool definition: pool bgpmonitor { monitor all eavbgp_v3 members 1.1.1.1:f5-iquery {} } You can download the script here: rhi_v3.bsh CODE: #!/bin/bash # (c) Copyright 1996-2007 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # author: Paul DeHerrera pauld@f5.com # # these arguments supplied automatically for all external monitors: # $1 = IP (nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) -- not used in this monitor, assumes default port 53 # # these arguments must be supplied in the monitor configuration: # $3 = name of pool to monitor # $4 = threshold value of the pool. If the available pool member count drops below this value the monitor will respond in 'failed' state # $5 = first Virtual server to disable # $6 = second Virtual server to disable # $7 = first Virtual address to disable # $8 = second Virtual address to disable ### Check for the 'DEBUG' variable, set it here if not present. # is the DEBUG variable passed as a variable? if [ -z "$DEBUG" ] then # If the monitor config didn't specify debug as a variable then enable/disable it here DEBUG=0 fi ### If Debug is on, output the script start time to /var/log/ltm # capture and log (when debug is on) a timestamp when this eav starts export ST=`date +%Y%m%d-%H:%M:%S` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): started at $ST" | logger -p local0.debug; fi ### Do not execute this script within the first 300 seconds after BIG-IP boot. This is a customer specific requirement # this section is used to introduce a delay of 300 seconds after system boot before executing this eav for the first time BOOT_DATE=`who -b | grep -i 'system boot' | awk {'print $3 " " $4 " " $5'}` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): boot_date: ($BOOT_DATE)" | logger -p local0.debug; fi EPOCH_DATE=`date -d "$BOOT_DATE" +%s` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): epoch_date: ($EPOCH_DATE)" | logger -p local0.debug; fi EPOCH_DATE=$((${EPOCH_DATE}+300)) if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): epoch_date +300: ($EPOCH_DATE)" | logger -p local0.debug; fi CUR_DATE=`date +%s` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): current_date: ($CUR_DATE)" | logger -p local0.debug; fi if [ $CUR_DATE -ge $EPOCH_DATE ] then ### Assign a value to variables. The VIPRION requires some commands to be executed on the Primary slot as you will see later in this script # export some variables if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): exporting variables..." | logger -p local0.debug; fi export REMOTEUSER="root" export HOME="/root" export IP=`echo $1 | sed 's/::ffff://'` export PORT=$2 export POOL=$3 export MEMBER_THRESHOLD=$4 export VIRTUAL_SERVER1=$5 export VIRTUAL_SERVER2=$6 export VIRTUAL_ADDRESS1=$7 export VIRTUAL_ADDRESS2=$8 export PIDFILE="/var/run/`basename $0`.$IP.$PORT.pid" export TRACKING_FILENAME=/var/tmp/rhi_bsh_monitor_status export PRIMARY_SLOT=`tmsh list sys db cluster.primary.slot | grep -i 'value' | sed -e 's/\"//g' | awk {'print $NF'}` ### Output the Primary slot to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): the primary blade is in slot number: ($PRIMARY_SLOT)..." | logger -p local0.debug; fi ### This section is for debugging only. Check to see if this script is executing on the Primary blade and output to /var/log/ltm if [ $DEBUG -eq 1 ]; then export PRIMARY_BLADE=`tmsh list sys db cluster.primary | grep -i "value" | sed -e 's/\"//g' | awk {'print $NF'}`; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): is this monitor executing on the primary blade: ($PRIMARY_BLADE)" | logger -p local0.debug; fi ### Standard EAV check to see if an instance of this script is already running for the memeber. If so, kill the previous instance and output to /var/log/ltm # is there already an instance of this EAV running for this member? if [ -f $PIDFILE ] then if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): pid file is present, killing process..." | logger -p local0.debug; fi kill -9 `cat $PIDFILE` > /dev/null 2>&1 echo "EAV `basename $0` ($$): exceeded monitor interval, needed to kill ${IP}:${PORT} with PID `cat $PIDFILE`" | logger -p local0.error fi ### Create a new pid file to track this instance of the monitor for the current member # create a pidfile if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): creating new pid file..." | logger -p local0.debug; fi echo "$$" > $PIDFILE ### Export variables for available pool members and total pool members # export more variables (these require tmsh) export AVAILABLE=`tmsh show /ltm pool $POOL members all-properties | grep -i "Availability" | awk {'print $NF'} | grep -ic "available"` export TOTAL_POOL_MEMBERS=`tmsh show /ltm pool $POOL members all-properties | grep -c "Pool Member"` let "AVAILABLE-=1" ### If Debug is on, output some variables to /var/log/ltm - helps with troubleshooting if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Pool ($POOL) has ($AVAILABLE) available of ($TOTAL_POOL_MEMBERS) total members." | logger -p local0.debug; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Pool ($POOL) threshold = ($MEMBER_THRESHOLD) members. Virtual server1 ($VIRTUAL_SERVER1) and Virtual server2 ($VIRTUAL_SERVER2)" | logger -p local0.debug; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Member Threshold ($MEMBER_THRESHOLD)" | logger -p local0.debug; fi ### If the available members is less than the threshold then we are in a 'failed' state. # main monitor logic if [ "$AVAILABLE" -lt "$MEMBER_THRESHOLD" ] then ### If Debug is on, output status to /var/log/ltm ### notify log - below threshold and disabling virtual server1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): AVAILABLE < MEMBER_THRESHOLD, disabling the virtual server..." | logger -p local0.debug; fi if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Server 1 ($VIRTUAL_SERVER1)" | logger -p local0.debug; fi ### Disable the first virtual server, which may exist in an administrative partition. For version 10.2.3 (possibly others) the script is required to change the 'update-partition' before disabling the virtual server. To accomplish this we first determine the administrative partition name where the virtual is configured then we build a list construct to execute both commands consecutively. ### disable virtual server 1 ### obtain the administrative partition for the virtual. if no administrative partition is found, assume common export VS1_PART=`tmsh list ltm virtual $VIRTUAL_SERVER1 | grep 'partition' | awk {'print $NF'}` if [ -z ${VS1_PART} ]; then ### no administrative partition was found so execute a list construct to change the update-partition to Common and disable the virtual server consecutively export DISABLE1=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled"` ### If Debug is on, output the command to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd1: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled'" | logger -p local0.debug; fi else ### the administrative partition was found so execute a list construct to change the update-partition and disable the virtual server consecutively. The command is sent to the primary slot via SSH export DISABLE1=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VS1_PART && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled"` ### If Debug is on, output the command to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd1: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition $VS1_PART && tmsh modify /ltm virtual $VIRTUAL_SERVER1 disabled'" | logger -p local0.debug; fi fi ### If Debug is on, output status to /var/log/ltm if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Server 2 ($VIRTUAL_SERVER2)" | logger -p local0.debug; fi ### Disable the second virtual server. This section is the same as above, so I will skip the detailed comments here. ### disable virtual server 2 export VS2_PART=`tmsh list ltm virtual $VIRTUAL_SERVER2 | grep 'partition' | awk {'print $NF'}` if [ -z ${VS2_PART} ]; then export DISABLE2=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER2 disabled"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd2: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition Common && tmsh modify /ltm virtual $VIRTUAL_SERVER2 disabled'" | logger -p local0.debug; fi else export DISABLE2=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VS2_PART && tmsh modify /ltm virtual $VIRTUAL_SERVER2 disabled"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disable cmd2: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT 'tmsh modify cli admin-partitions update-partition $VS2_PART && tmsh modify ltm virtual $VIRTUAL_SERVER2 disabled'" | logger -p local0.debug; fi fi ### notify log - disconnecting all BGP connection if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): Pool ($POOL) disconnecting all BGP connections..." | logger -p local0.debug; fi ### acquire a list of self IPs SELF_IPS=(`tmsh list net self | grep 'net self' | sed -e 's/\//\ /g' | awk {'print $3'}`) ### start to build our TMSH command excluding self IPs BGP_CONNS="tmsh show sys conn cs-server-port 179 | sed -e 's/\:/\ /g' | egrep -v '" COUNT=1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 1 - ${BGP_CONNS}" | logger -p local0.debug; fi ### loop through the self IPs for ip in "${SELF_IPS[@]}" do if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 2 - ${ip}" | logger -p local0.debug; fi ### continue to build our TMSH command - append self IPs to ignore if [ ${COUNT} -gt 1 ] then BGP_CONNS=${BGP_CONNS}"|${ip}" else BGP_CONNS=${BGP_CONNS}"${ip}" fi (( COUNT++ )) done ### if debug is on log a message with the TMSH command up until this point if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 3 - ${BGP_CONNS}" | logger -p local0.debug; fi ### finish the TMSH command to show BGP connections not including self IPs BGP_CONNS=${BGP_CONNS}"' | egrep -v 'Sys|Total' | awk {'print \$1'}" if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 4 - ${BGP_CONNS}" | logger -p local0.debug; fi ### gather all BGP connection not including those to self IPs DISCONNS=(`eval $BGP_CONNS`) DISCMD='' NEWCOUNT=1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 5 - ${DISCONNS}" | logger -p local0.debug; fi ### loop through the resulting BGP connections and build another TMSH command to delete these connections from the connection table for newip in "${DISCONNS[@]}" do if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 6" | logger -p local0.debug; fi if [ ${NEWCOUNT} -gt 1 ] then DISCMD=${DISCMD}" && tmsh delete sys connection cs-client-addr ${newip} cs-server-port 179" else DISCMD=${DISCMD}"tmsh delete sys connection cs-client-addr ${newip} cs-server-port 179" fi (( NEWCOUNT++ )) done ### if debug is on log the command we just assembled if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 7 - ${DISCMD}" | logger -p local0.debug; fi ### One the primary slot execute the command to delete the non self IP BGP connections. export CONNECTIONS=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "${DISCMD}"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): BGP Step 8 - $CONNECTIONS" | logger -p local0.debug; fi ### disable virtual address 1 if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): VA1 ($VIRTUAL_ADDRESS1)" | logger -p local0.debug; fi if [ ! -z "$VIRTUAL_ADDRESS1" ]; then if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Address 1 ($VIRTUAL_ADDRESS1)" | logger -p local0.debug; fi export VA1_PART=`tmsh list ltm virtual-address $VIRTUAL_ADDRESS1 | grep 'partition' | awk {'print $NF'}` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): cmd: ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT tmsh modify cli admin-partitions update-partition $VA1_PART && tmsh modify /ltm virtual-address $VIRTUAL_ADDRESS1 enabled no " | logger -p local0.debug; fi export VA2_UPCMD=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VA1_PART && tmsh modify /ltm virtual-address $VIRTUAL_ADDRESS1 enabled no"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): virtual address 1 disabled?" | logger -p local0.debug; fi fi ### disable virtual address 2 if [ ! -z "$VIRTUAL_ADDRESS2" ]; then if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): disabling Virtual Address 2 ($VIRTUAL_ADDRESS2)" | logger -p local0.debug; fi export VA2_PART=`tmsh list ltm virtual-address $VIRTUAL_ADDRESS2 | grep 'partition' | awk {'print $NF'}` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): update-partition - $VA2_PART" | logger -p local0.debug; fi export VA2_UPCMD=`ssh -o StrictHostKeyChecking=no root\@slot$PRIMARY_SLOT "tmsh modify cli admin-partitions update-partition $VA2_PART && tmsh modify /ltm virtual-address $VIRTUAL_ADDRESS2 enabled no"` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): cmd: virtual address 2 disabled?" | logger -p local0.debug; fi fi ### track number of times this monitor has failed if [ -e "$TRACKING_FILENAME" ] then export COUNT=`cat $TRACKING_FILENAME` export NEW_COUNT=$((${COUNT}+1)) echo $NEW_COUNT > $TRACKING_FILENAME else echo 1 > $TRACKING_FILENAME export NEW_COUNT=1 fi ### notify log - failure count echo "EAV `basename $0` ($$): Pool $POOL only has $AVAILABLE available of $TOTAL_POOL_MEMBERS total members, failing site. Virtual servers ($VIRTUAL_SERVER1 and $VIRTUAL_SERVER2) will be disabled and all connections with destination port 179 will be terminated. Virtual servers must be manually enabled after pool $MEMBER_THRESHOLD or more pool members are available. This monitor has failed $NEW_COUNT times." | logger -p local0.debug # remove the pidfile if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): removing the pidfile..." | logger -p local0.debug; fi export PIDBGONE=`rm -f $PIDFILE` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): pidfile has been removed ($PIDBGONE)" | logger -p local0.debug; fi export END=`date +%Y%m%d-%H:%M:%S` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): stopped at $END" | logger -p local0.debug; fi else if [ -e "$TRACKING_FILENAME" ] then ### log the status echo "EAV `basename $0` ($$): Pool $POOL has $AVAILABLE members of $TOTAL_POOL_MEMBERS total members. No change to virtual servers ($VIRTUAL_SERVER1 and $VIRTUAL_SERVER2). No change to port 179 connections. Virtual servers must be manually enabled to pass traffic if they are disabled." | logger -p local0.debug rm -f $TRACKING_FILENAME fi ### remove the pidfile if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): removing the pidfile..." | logger -p local0.debug; fi export PIDBGONE=`rm -f $PIDFILE` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): pidfile has been removed ($PIDBGONE)" | logger -p local0.debug; fi export END=`date +%Y%m%d-%H:%M:%S` if [ $DEBUG -eq 1 ]; then echo "EAV `basename $0` ($$): stopped at $END" | logger -p local0.debug; fi echo "UP" fi fi307Views0likes0CommentsLet's Encrypt with Cloudflare DNS and F5 REST API
Hi all This is a followup on the now very old Let's Encrypt on a Big-IP article. It has served me, and others, well but is kind of locked to a specific environment and doesn't scale well. I have been going around it for some time but couldn't find the courage (aka time) to get started. However, due to some changes to my DNS provider (they were aquired and shut down) I finally took the plunges and moved my domains to a provider with an API and that gave me the opportunity to make a more nimble solution. To make things simple I chose Cloudflare as the community proliferation is enormous and it is easy to find examples and tools. I though think that choosing another provide with an open API isn't such a big deal. After playing around with different tools I realized that I didn't need them as it ended up being much easier to just use curl. So, if the other providers have just a somewhat close resemblance it shouldn't be such a big task converting the scripts to fit. There might be finer and more advanced solutions out there, but my goal was that I needed a solution that had as few dependencies as possible and if I could make that only Bash and Curl it would be perfect. And that is what I ended up with 😎 Just put 5 files in the same directory, adjust the config to your environment, and BAM you're good to go!!😻 And if you need to run it somewhere else just copy the directory over and continue like nothing was changed. That is what I call portability 😁 Find all the details here: Let's Encrypt with Cloudflare DNS and F5 REST API Please just drop me a line if you have any questions or feedback or find any bugs.2.4KViews1like8CommentsBigIP UCS Backup script; looking for some guidance on design
Greetings, I've began to work on a bash script, intended to be ran locally on each F5 appliance via a cron task. The criteria for this script has been, Saves the UCS /w encryption using {Hostname}-YYYY-MM-DD.ucs naming format. Uploads the generated UCS file to a SFTP server SFTP native commands are a MUST, SCP will not work due to it's reliance on command shell/login. Rollover after X # of saved files in order to prevent storage exhaustion on the target SFTP Server I strongly doubt any form of deduplication will work with a encrypted UCS Sends an email notification if the backup failed I've so far written a script that addresses the first 3 criteria and have been waiting for those to go through their paces in testing before adding in notification logic. The commands and logic being used have gotten more complex, the further I've gotten into the script's development. This has lead to some concerns about whether this is the best approach given the nature of the F5 BigIP systems being a vendor appliance and worry that there's a large possibility commands may stop working correctly after a major x. version update, requiring an overhaul of a fairly complex script. I'm almost wondering if setting up an AWX/Tower host in our environment and then using the f5networks Ansible Module for the majority of the heavy lifting followed by some basic logic for file rotation, would be a better long term approach. Ansible would also be a bit more flexible in that I wouldn't have to hardcore values that diverge between individual hosts into the script itself. It's however not clear if the F5networks ansible module supports SFTP as I only see SCP referenced. https://my.f5.com/manage/s/article/K35454259 Advice and insight is much appreciated! #!/bin/bash # F5 backup script based on https://my.f5.com/manage/s/article/K000138297 # User-configurable Variables UCS_DIR="/var/ucs" REMOTE_USER="svc_f5backup" REMOTE_HOST="myhost.contoso.local" REMOTE_DIR="/data/f5/dev" SSH_KEY="/shared/scripts/f5-backup/mykeys/f5user" ENCRYPTION_PASSPHRASE='' # Blank out the value to not encrypt the UCS backup. LOG_FILE="/var/log/backupscript.log" MAX_FILES=45 # Maximum number of backup files to keep # Dynamic Variables (do not edit) HOSTNAME=$(/bin/hostname) DATE=$(date +%Y-%m-%d) UCS_FILE="${UCS_DIR}/${HOSTNAME}-${DATE}.ucs" # Start logging echo "$(date +'%Y-%m-%d %H:%M:%S') - Starting backup script." >> ${LOG_FILE} # Save the UCS backup file if [ -n "${ENCRYPTION_PASSPHRASE}" ]; then echo "Running the UCS save operation (encrypted)." >> ${LOG_FILE} tmsh save /sys ucs ${UCS_FILE} passphrase "${ENCRYPTION_PASSPHRASE}" >> ${LOG_FILE} 2>&1 else echo "Running the UCS save operation (not encrypted)." >> ${LOG_FILE} tmsh save /sys ucs ${UCS_FILE} >> ${LOG_FILE} 2>&1 fi # Create a temporary batch file for SFTP commands BATCH_FILE=$(mktemp) echo "cd ${REMOTE_DIR}" > $BATCH_FILE echo "put ${UCS_FILE}" >> $BATCH_FILE echo "bye" >> $BATCH_FILE # Log that the transfer is starting echo "Starting SFTP transfer." >> ${LOG_FILE} # Execute SFTP command and capture the output transfer_command_output=$(sftp -b "$BATCH_FILE" -i "${SSH_KEY}" -oBatchMode=no "${REMOTE_USER}@${REMOTE_HOST}" 2>&1) transfer_status=$? # Extract the "Transferred:" line transfer_summary=$(echo "$transfer_command_output" | grep "^Transferred: sent") if [ $transfer_status -eq 0 ]; then if [ -n "$transfer_summary" ]; then echo "UCS file copied to the SFTP server successfully (remote:${REMOTE_HOST}:${REMOTE_DIR}/${UCS_FILE}). $transfer_summary" >> ${LOG_FILE} else echo "UCS file copied to the SFTP server successfully (remote:${REMOTE_HOST}:${REMOTE_DIR}/${UCS_FILE}). Please check the log for details." >> ${LOG_FILE} fi else echo "$transfer_command_output" >> ${LOG_FILE} echo "UCS SFTP copy operation failed. Please read the log for details." >> ${LOG_FILE} rm -f $BATCH_FILE exit 1 fi # Clean up the temporary batch file rm -f $BATCH_FILE # Rollover backup files if the number exceeds MAX_FILES echo "Checking and maintaining the maximum number of backup files." >> ${LOG_FILE} # Create a list of files to delete sftp -i "${SSH_KEY}" -oBatchMode=no "${REMOTE_USER}@${REMOTE_HOST}" <<EOF > file_list.txt cd ${REMOTE_DIR} ls -1 ${HOSTNAME}-*.ucs bye EOF # Filter out unwanted lines and sort the files alphanumerically grep -v 'sftp>' file_list.txt | grep -v '^cd ' | sort > filtered_file_list.txt # Determine files to delete files_to_delete=$(head -n -${MAX_FILES} filtered_file_list.txt) if [ -n "$files_to_delete" ]; then # Create a temporary batch file for SFTP cleanup commands CLEANUP_BATCH_FILE=$(mktemp) echo "cd ${REMOTE_DIR}" > $CLEANUP_BATCH_FILE for file in $files_to_delete; do echo "Deleting $file" >> ${LOG_FILE} echo "rm $file" >> $CLEANUP_BATCH_FILE done echo "bye" >> $CLEANUP_BATCH_FILE # Execute SFTP cleanup command and log the output cleanup_command_output=$(sftp -b "$CLEANUP_BATCH_FILE" -i "${SSH_KEY}" -oBatchMode=no "${REMOTE_USER}@${REMOTE_HOST}" 2>&1) echo "$cleanup_command_output" >> ${LOG_FILE} # Clean up the temporary batch file rm -f $CLEANUP_BATCH_FILE else echo "No files to delete. Total files within limit." >> ${LOG_FILE} fi # Clean up the file lists rm -f file_list.txt filtered_file_list.txt # Delete the local copy of the UCS archive tmsh delete /sys ucs ${UCS_FILE} >> ${LOG_FILE} 2>&1 echo "$(date +'%Y-%m-%d %H:%M:%S') - Backup script completed." >> ${LOG_FILE}266Views0likes2CommentsList BIG-IP Next Instance Backups on Central Manager
In the Central Manager GUI, you can create/schedule BIG-IP Next Instance backups, but outside of the listing shown there, you can't download the files from that view if you want to archive them for off-box requirements. Finding them in the Central Manager command line to download them via secure copy (scp) requires some kubernetes-fu knowhow, mainly, interrogating the persistent volume claims and persistent volumes: kubectl get pvc mbiq-local-storage-pv-claim -o yaml | grep volumeName kubectl get pv <volumename result> -o yaml | grep "path: " This script takes the guesswork out of all that and let's you focus on more important things. Example output: admin@cm1:~$ ./lbu.sh Backup path: /var/lib/rancher/k3s/storage/pvc-ae75faee-101e-49eb-89f7-b66542da1281_default_mbiq-local-storage-pv-claim/backup total 3860 4 drwxrwxrwx 2 root root 4096 Mar 7 19:33 . 4 drwxrwxrwx 7 root root 4096 Feb 2 00:01 .. 1780 -rw-r--r-- 1 ubuntu lxd 1821728 Feb 28 18:40 3b9ef4d8-0f0b-453d-b350-c8720a30db16.2024-02-28.18-39-59.backup.tar.gz 288 -rw-r--r-- 1 ubuntu lxd 292464 Feb 28 18:39 7bf4e3ac-e8a2-44a3-bead-08be6c590071.2024-02-28.18-39-15.backup.tar.gz 1784 -rw-r--r-- 1 ubuntu lxd 1825088 Mar 7 19:33 7bf4e3ac-e8a2-44a3-bead-08be6c590071.2024-03-07.19-32-56.backup.tar.gz Script Source132Views1like0CommentsIssue with a simple Bash Script for adding an iRule to a list of Virtual Servers.
Hello Community, I am having an issue with a bash script for an F5 BIG-IP Load Balancer, intended to read and iterate over a .txt list of Virtual Server names, look up the partition for a given VS, and add an iRule to it. When running the script I am only hitting the outermost 'else' statement for being unable to find the partition and VS name. My script logic is based on F5 Support solution K41961653: <p> #!/bin/bash # Prompt the user for the iRule name and read it into the 'new' variable echo "Please enter the iRule name:" read new noneRules='rules none' while IFS= read -r vs_name; do # Retrieve the partition and virtual server name full_vs_info=$(tmsh -c "cd /; list ltm virtual recursive" | grep "$vs_name" | grep -m1 "^ltm virtual") echo "Full VS Info Debug: $full_vs_info" # Extract the partition and virtual server name from the retrieved information if [[ $full_vs_info =~ ltm\ virtual\ (.+)/(.+) ]]; then partition="${BASH_REMATCH[1]}" vs_name="${BASH_REMATCH[2]}" # Format the tmsh command to include the partition rule=$(tmsh list ltm virtual /$partition/$vs_name rules | egrep -v "\{|\}" | xargs) if [[ "$rule" == "$noneRules" ]]; then tmsh modify ltm virtual /$partition/$vs_name rules { $new } echo "iRule $new was added to $vs_name in partition $partition" else# tmsh modify ltm virtual /$partition/$vs_name rules { $rule $new } echo "iRules $rule were conserved and added $new to $vs_name in partition $partition" fi else echo "Could not find partition and virtual server name for $vs_name" fi done < /shared/tmp/test_list.txt tmsh save sys config </p> As far as I was able to troubleshoot, the problem I am encountering appears to be with line 11 of my script where I attempt to assign the string "ltm virtual SomePartition/VS_Example.com {" to the "full_vs_info" variable using: full_vs_info=$(tmsh -c "cd /; list ltm virtual recursive" | grep "$vs_name" | grep -m1 "^ltm virtual") When I run the tmsh command [tmsh -c "cd /; list ltm virtual recursive" | grep "VS_Example.com" | grep -m1 "^ltm virtual"] on its own, from the F5's Bash shell, I am getting the output I expect: "ltm virtual SomePartition/VS_Example.com {" However, when I run the script with the debug echo , it only outputs "Full VS Info Debug:", and ends the script with "Could not find partition and virtual server name for $vs_name" and a sys config save. I am attempting to run this on a BIG-IP, version 15.1.10.2, build 0.44.2. I am quite new to both Bash scripting and F5 LBs. All feedback and criticism is highly appreciated! Thanks in advance!118Views0likes2CommentsF5 Automation - TCL & Bash
Problem this snippet solves: This is a really simple way to automate CLI command execution on multiple F5 devices using Bash & TCL scripting. How to use this snippet: On a linux machine that is utilized to connect to the F5 device: Create a directory mkdir F5_Check Within the "F5_Check" directory, create the following 3 files: F5_Host.txt (This file contains F5's IP address) F5_Bash_v1 (This is the bash script used to collect username/password for F5) F5_Out_v1.exp (This is the TCL script executes the relevant commands on F5) Explanation of the 3 files: File Content: F5_Out_v1.exp is provided as code share. This is the main TCL script that is utiliezd to execute CLI commands on multiple F5 devices. File Content: F5_Bash_v1 #!/bin/bash # Collect the username and password for F5 access echo -n "Enter the username " read -s -e user echo -ne '\n' echo -n "Enter the password " read -s -e password echo -ne '\n' # Feed the expect script a device list & the collected username & passwords for device in `cat ~/F5_Check/F5_Host.txt`; do ./F5_Out_v1.exp $device $password $user ; done File Contents: F5_Host.txt This contains the management IP of the F5 devices. Example: cat F5_Host.txt 10.12.12.200 10.12.12.201 10.12.12.202 10.12.12.203 Code : #!/usr/bin/expect -f # Set variables set hostname [lindex $argv 0] set password [lindex $argv 1] set username [lindex $argv 2] # Log results log_file -a ~/F5_Check/F5LOG.log # Announce which device we are working on and the time send_user "\n" send_user ">>>>> Working on $hostname @ [exec date] <<<<<\n" send_user "\n" # SSH access to device spawn ssh $username@$hostname expect { "no)? " { send "yes\n" expect "*assword: " sleep 1 send "$password\r" } "*assword: " { sleep 1 send "$password\r" } } expect "(tmos)#" send "sys\n" expect "(tmos.sys)#" send "show software\n" expect "#" send "exit\n" expect "#" send "quit\n" expect ":~\$" exit Tested this on version: 11.52KViews0likes2CommentsUse F5 LTM as HTTP Proxy
Problem this snippet solves: LTM product can be used as a HTTP Proxy for servers and PC. This code explains minimum requirements to configure proxy feature without SWG module (configurations from Explicit Forward Proxy documentation without documentation ) and without explicit proxy iApp. How to use this snippet: All these commands must be run in bash shell. Create HTTP PROXY VIRTUAL SERVER Configure variables used in next commands Variable HTTPBaseName is used to create : Resolver object : RESOLVER_${HTTPBaseName} HTTP profile : http_${HTTPBaseName} virtual server : VS_${HTTPBaseName} HTTPBaseName="HTTP_FORWARD_PROXY" VS_IP="192.168.2.80" VS_PORT="8080" create DNS resolver with your DNS server (1.1.1.1 is for demo using cloudflare) tmsh create net dns-resolver RESOLVER_${HTTPBaseName} { forward-zones replace-all-with { . { nameservers replace-all-with { 1.1.1.1:domain { } } } } route-domain 0 } create HTTP profile type explicit, using DNS resolver. The parameter default-connect-handling allow enables HTTPS connections without SSL inspection tmsh create ltm profile http http_${HTTPBaseName} { defaults-from http-explicit explicit-proxy { default-connect-handling allow dns-resolver RESOLVER_${HTTPBaseName} } proxy-type explicit } create HTTP proxy Virtual server tmsh create ltm virtual VS_${HTTPBaseName} { destination ${VS_IP}:${VS_PORT} ip-protocol tcp mask 255.255.255.255 profiles replace-all-with { http_${HTTPBaseName} { } tcp } source 0.0.0.0/0 source-address-translation { type automap } translate-address enabled translate-port enabled} ENABLE SSL FORWARD PROXY This section is not required to forward HTTPS requests but only to enable SSL inspection on HTTPS requests. Note : Following configuration requires SSL, Forward Proxy License. Configure variables used in next commands Variable SSLBaseName is used to create : certificate / key pair : ${SSLBaseName} Client SSL profile : clientssl_${SSLBaseName} Server SSL profile : serverssl_${SSLBaseName} virtual server : VS_${SSLBaseName} SSLBaseName="SSL_FORWARD_PROXY" dirname="/var/tmp" CASubject="/C=FR/O=DEMO\ COMPANY/CN=SSL\ FORWARD\ PROXY\ CA" Create self-signed certificate for CA purpose (not available in WebUI) Self-signed certificates created in WebUI doesn't have CA capability required for SSL FORWARD PROXY. openssl genrsa -out ${dirname}/${SSLBaseName}.key 4094 openssl req -sha512 -new -x509 -days 3650 -key ${dirname}/${SSLBaseName}.key -out ${dirname}/${SSLBaseName}.crt -subj "${CASubject}" Import certificates in TMOS tmsh install sys crypto key ${SSLBaseName}.key from-local-file ${dirname}/${SSLBaseName}.key; tmsh install sys crypto cert ${SSLBaseName}.crt from-local-file ${dirname}/${SSLBaseName}.crt; After CA Certificate is imported, browse in WebUI, retrieve it and import it in client browsers trusted CA Create SSL profiles for SSL FORWARD PROXY tmsh create ltm profile client-ssl clientssl_${SSLBaseName} { cert-lookup-by-ipaddr-port disabled defaults-from clientssl mode enabled proxy-ca-cert ${SSLBaseName}.crt proxy-ca-key ${SSLBaseName}.key ssl-forward-proxy enabled } tmsh create ltm profile server-ssl serverssl_${SSLBaseName} { defaults-from serverssl ssl-forward-proxy enabled } create SSL FORWARD PROXY Virtual server tmsh create ltm virtual VS_${SSLBaseName} { destination 0.0.0.0:https ip-protocol tcp profiles replace-all-with { clientssl_${SSLBaseName} { context clientside } serverssl_${SSLBaseName} { context serverside } http { } tcp { } } source 0.0.0.0/0 translate-address disabled translate-port disabled vlans replace-all-with { http-tunnel } vlans-enabled } Change HTTP EXPLICIT PROXY Default Connect Handling to Deny tmsh modify ltm profile http http_${HTTPBaseName} explicit-proxy { default-connect-handling deny } Note : These commands were tested in both 12.1 and 13.1 versions. Code : No Code11KViews1like24Comments