Python script to install license and configuration for IPSec tunneling on a VE instance
Problem this snippet solves:
This script can be called from the command line and used to reach out to a VE to license it, perform minimum configuration and provision an IPSec interface-mode tunnel.
This is not best practice, not hardened, etc. This is a foundation, not a solution.
How to use this snippet:
The script has built-in CLI help.
Code :
#!/usr/bin/env python3
import argparse,json,requests,time
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
def icontrol_get(host,username,password,path):
apiCall = requests.session()
apiCall.headers.update({'Content-type':'application/json'})
apiCall.auth = (username,password)
apiUri = 'https://' + host + path
try:
apiResponse = apiCall.get(apiUri,verify=False)
except requests.exceptions.RequestException as e:
print('{"responseStatus":"error","action":"get","host":"' + host + '","username":"' + username + '","path":"' + path + '","errorMsg":"' + str(e) + '"}')
return(apiResponse.text)
def icontrol_post(host,username,password,path,api_payload):
apiCall = requests.session()
apiCall.headers.update({'Content-type':'application/json'})
apiCall.auth = (username,password)
apiUri = 'https://' + host + path
try:
apiResponse = apiCall.post(apiUri,verify=False,data=json.dumps(api_payload))
except requests.exceptions.RequestException as e:
print('{"responseStatus":"error","action":"post","host":"' + host + '","username":"' + username + '","path":"' + path + '"},"payload":"' + api_payload + '","errorMsg":"' + str(e) + '"}')
return(apiResponse.text)
def icontrol_put(host,username,password,path,api_payload):
apiCall = requests.session()
apiCall.headers.update({'Content-type':'application/json'})
apiCall.auth = (username,password)
apiUri = 'https://' + host + path
try:
apiResponse = apiCall.put(apiUri,verify=False,data=json.dumps(api_payload))
except requests.exceptions.RequestException as e:
print('{"responseStatus":"error","action":"put","host":"' + host + '","username":"' + username + '","path":"' + path + '"},"payload":"' + api_payload + '","errorMsg":"' + str(e) + '"}')
return(apiResponse.text)
def icontrol_patch(host,username,password,path,api_payload):
apiCall = requests.session()
apiCall.headers.update({'Content-type':'application/json'})
apiCall.auth = (username,password)
apiUri = 'https://' + host + path
try:
apiResponse = apiCall.patch(apiUri,verify=False,data=json.dumps(api_payload))
except requests.exceptions.RequestException as e:
print('{"responseStatus":"error","action":"patch","host":"' + host + '","username":"' + username + '","path":"' + path + '"},"payload":"' + api_payload + '","errorMsg":"' + str(e) + '"}')
return(apiResponse.text)
def icontrol_delete(host,username,password,path):
apiCall = requests.session()
apiCall.headers.update({'Content-type':'application/json'})
apiCall.auth = (username,password)
apiUri = 'https://' + host + path
try:
apiResponse = apiCall.delete(apiUri,verify=False)
except requests.exceptions.RequestException as e:
print('{"responseStatus":"error","action":"delete","host":"' + host + '","username":"' + username + '","path":"' + path + '"},"errorMsg":"' + str(e) + '"}')
return(apiResponse.text)
## Parse the command line arguments#cmdargs = argparse.ArgumentParser()
cmdargs.add_argument('--f5restip',action='store',required=True,type=str,help='ip of BIG-IP REST interface, typically the mgmt ip')
cmdargs.add_argument('--username',action='store',required=True,type=str,help='username for REST authentication')
cmdargs.add_argument('--password',action='store',required=True,type=str,help='password for REST authentication')
cmdargs.add_argument('--licensekey',action='store',required=False,type=str,help='license key for licensing')
cmdargs.add_argument('--hostname',action='store',required=False,type=str,help='hostname to configure on the BIG-IP (must be FQDN)')
cmdargs.add_argument('--ntpserver',action='store',required=False,type=str,help='NTP server to add to the BIG-IP (IP or FQDN)')
cmdargs.add_argument('--dnsserver',action='store',required=False,type=str,help='DNS server to add to the BIG-IP (IP address)')
cmdargs.add_argument('--newlocalselfip',action='store',required=True,type=str,help='creates the private self-ip address of the VE where the public address is mapped (CIDR notation)')
cmdargs.add_argument('--newlocalselfipgateway',action='store',required=True,type=str,help='creates the private self-ip subnet gateway for server communications')
cmdargs.add_argument('--localserversubnet',action='store',required=True,type=str,help='the local VM subnet in CIDR notation')
cmdargs.add_argument('--localselfip',action='store',required=True,type=str,help='the private self-ip address of the VE where the public address is mapped')
cmdargs.add_argument('--localpublicip',action='store',required=True,type=str,help='the public IP address of the VNA')
cmdargs.add_argument('--remotepublicip',action='store',required=True,type=str,help='the public IP address of the remote IPSec peer')
cmdargs.add_argument('--presharedkey',action='store',required=True,type=str,help='the pre-shared key for encryption')
cmdargs.add_argument('--tunnelsourcenet',action='store',required=True,type=str,help='CIDR notated source network, can be 0.0.0.0/0 to tunnel all traffic')
cmdargs.add_argument('--tunneldestinationnet',action='store',required=True,type=str,help='CIDR notated destination network, the remote destination subnet to tunnel, can be 0.0.0.0/0 to let static routes control tunneling')
cmdargs.add_argument('--tunnelstaticroutenet',action='store',required=True,type=str,help='CIDR notated destination network to statically route across the tunnel (this makes the decision to tunnel or not)')
cmdargs.add_argument('--tunnelselfiplocal',action='store',required=True,type=str,help='CIDR notated tunnel self-IP address, a new address assigned to the tunnel (ie 192.168.1.1/30')
cmdargs.add_argument('--tunnelselfipremote',action='store',required=True,type=str,help='CIDR notated tunnel self-IP address on the remote end, a new address assigned to the tunnel (ie 192.168.1.1/30')
cmdargs.add_argument('--bgpas',action='store',required=True,type=str,help='AS number for BGP process and neighbor')
parsed_args = cmdargs.parse_args()
host = parsed_args.f5restip
username = parsed_args.username
password = parsed_args.password
license_key = parsed_args.licensekey
hostname = parsed_args.hostname
ntpserver = parsed_args.ntpserver
dnsserver = parsed_args.dnsserver
vpn = {}
vpn['local-self-ip'] = parsed_args.localselfip
vpn['local-public-ip'] = parsed_args.localpublicip
vpn['remote-public-ip'] = parsed_args.remotepublicip
vpn['pre-shared-key'] = parsed_args.presharedkey
vpn['tunnel-source-network'] = parsed_args.tunnelsourcenet
vpn['tunnel-destination-network'] = parsed_args.tunneldestinationnet
vpn['tunnel-static-route-network'] = parsed_args.tunnelstaticroutenet
vpn['tunnel-name'] = 'ipsec_peer-' + vpn['remote-public-ip']
vpn['tunnel-self-ip'] = parsed_args.tunnelselfiplocal
vpn['new-local-self-ip'] = parsed_args.newlocalselfip
vpn['new-local-self-ip-gateway'] = parsed_args.newlocalselfipgateway
vpn['local-server-subnet'] = parsed_args.localserversubnet
vpn['tunnel-self-ip-remote'] = parsed_args.tunnelselfipremote
vpn['bgp-as'] = parsed_args.bgpas
# License the VEif license_key is not None:
print('Licensing ' + host + ' with key ' + license_key)
apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/sys/license',{"command":"install","registrationKey":license_key})
if 'New license installed' in apiCallResponse:
print('License installed successfully. Waiting for service restart.')
time.sleep(60)
else:
print('Error! ' + apiCallResponse)
quit()
else:
print("License key not specified - skipping licensing")
# Disable the GUI setup wizard since we're configuring via RESTprint('Disabling the GUI Setup Wizard')
apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/global-settings',{"guiSetup":"disabled"})
if '"kind":"tm:sys:global-settings:global-settingsstate"' and '"guiSetup":"disabled"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
# Set the hostname if specifiedif hostname is not None:
print('Setting the hostname to ' + hostname + ' on ' + host)
apiCallResponse = icontrol_patch(host, username, password, '/mgmt/tm/sys/global-settings',{"hostname":hostname})
if '"kind":"tm:sys:global-settings:global-settingsstate"' and '"hostname":"' + hostname + '"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Hostname not specified - skipping hostname configuration')
# Set the DNS server if specifiedif dnsserver is not None:
print('Adding DNS server ' + dnsserver + ' to ' + host)
apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/dns',{"nameServers":[dnsserver]})
if '"kind":"tm:sys:dns:dnsstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('DNS server not specified - skipping DNS server configuration')
# Set the NTP server if specifiedif ntpserver is not None:
print('Adding NTP server ' + ntpserver + ' to ' + host)
apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/ntp',{"servers":[ntpserver]})
if '"kind":"tm:sys:ntp:ntpstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('NTP server not specified - skipping NTP server configuration')
# Configure the local VLAN and self-IP if specifiedif vpn['new-local-self-ip'] is not None:
# create external VLAN untagged print('Creating the external VLAN')
apiPayload = {}
apiPayload['name'] = 'external' apiPayload['interfaces'] = ['1.1']
apiPayload['partition'] = 'Common' apiPayload['mtu'] = 1500 apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/vlan',apiPayload)
if '"kind":"tm:net:vlan:vlanstate"' in apiCallResponse :
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; skipping item configuration')
else:
print('Error! ' + apiCallResponse)
quit()
# create the data interface self-IP print('Creating the self-IP address ' + vpn['local-self-ip'])
apiPayload = {}
apiPayload['name'] = vpn['new-local-self-ip']
apiPayload['address'] = vpn['new-local-self-ip']
apiPayload['allowService'] = 'all' apiPayload['interfaces'] = ['1.1']
apiPayload['partition'] = 'Common' apiPayload['trafficGroup'] = 'traffic-group-local-only' apiPayload['vlan'] = 'external' apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/self',apiPayload)
if '"kind":"tm:net:self:selfstate"' in apiCallResponse :
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; skipping item configuration')
else:
print('Error! ' + apiCallResponse)
quit()
## Begin IPSec tunnel configuration steps## Create the IPSec policyprint('Creating the IPSec Policy (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['ikePhase2AuthAlgorithm'] = 'aes-gcm128'apiPayload['ikePhase2EncryptAlgorithm'] = 'aes-gcm128'apiPayload['ikePhase2PerfectForwardSecrecy'] = 'none'apiPayload['ikePhase2Lifetime'] = 1440apiPayload['ikePhase2LifetimeKilobytes'] = 0apiPayload['mode'] = 'interface'apiPayload['protocol'] = 'esp'apiPayload['ipcomp'] = 'none'apiPayload['partition'] = 'Common'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/ipsec-policy',apiPayload)
if '"kind":"tm:net:ipsec:ipsec-policy:ipsec-policystate"' in apiCallResponse :
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/ipsec-policy/' + vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:ipsec:ipsec-policy:ipsec-policystate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the IPSec traffic selectorprint('Creating the IPSec traffic selector (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['partition'] = 'Common'apiPayload['action'] = 'protect'apiPayload['sourceAddress'] = vpn['tunnel-source-network']
apiPayload['sourcePort'] = 0apiPayload['destinationAddress'] = vpn['tunnel-destination-network']
apiPayload['destinationPort'] = 0apiPayload['direction'] = 'both'apiPayload['ipProtocol'] = 255apiPayload['ipsecPolicy'] = vpn['tunnel-name']
apiPayload['order'] = 0apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/traffic-selector',apiPayload)
if '"kind":"tm:net:ipsec:traffic-selector:traffic-selectorstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/traffic-selector/' + vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:ipsec:traffic-selector:traffic-selectorstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Set the default IPSec traffic selector to high orderprint('Setting the default IPSec traffic selector to order 100')
apiPayload = {}
apiPayload['order'] = 100apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/net/ipsec/traffic-selector/default-traffic-selector-interface',apiPayload)
if '"kind":"tm:net:ipsec:traffic-selector:traffic-selectorstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
# Create the IKE peerprint('Creating the IKE peer (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['myIdType'] = 'address'apiPayload['myIdValue'] = vpn['local-public-ip']
apiPayload['peersIdType'] = 'address'apiPayload['peersIdValue'] = vpn['remote-public-ip']
apiPayload['phase1AuthMethod'] = 'pre-shared-key'apiPayload['phase1EncryptAlgorithm'] = 'aes256'apiPayload['phase1HashAlgorithm'] = 'sha256'apiPayload['phase1PerfectForwardSecrecy'] = 'modp1024'apiPayload['prf'] = 'sha256'apiPayload['presharedKey'] = vpn['pre-shared-key']
apiPayload['remoteAddress'] = vpn['remote-public-ip']
apiPayload['version'] = ['v2']
apiPayload['dpdDelay'] = 30apiPayload['lifetime'] = 1440apiPayload['mode'] = 'main'apiPayload['natTraversal'] = 'off'apiPayload['passive'] = 'false'apiPayload['generatePolicy'] = 'off'apiPayload['trafficSelector'] = [vpn['tunnel-name']]
apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/ike-peer',apiPayload)
if '"kind":"tm:net:ipsec:ike-peer:ike-peerstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/ike-peer/' + vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:ipsec:ike-peer:ike-peerstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the tunnel profileprint('Creating the tunnel profile (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['defaultsFrom'] = '/Common/ipsec'apiPayload['trafficSelector'] = vpn['tunnel-name']
apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/tunnels/ipsec',apiPayload)
if '"kind":"tm:net:tunnels:ipsec:ipsecstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/tunnels/ipsec/' + vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:tunnels:ipsec:ipsecstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the tunnel objectprint('Creating the tunnel object (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['autoLasthop'] = 'default'apiPayload['idleTimeout'] = 300apiPayload['key'] = 0apiPayload['localAddress'] = vpn['local-self-ip']
apiPayload['mode'] = 'bidirectional'apiPayload['mtu'] = 0apiPayload['profile'] = vpn['tunnel-name']
apiPayload['remoteAddress'] = vpn['remote-public-ip']
apiPayload['tos'] = 'preserve'apiPayload['transparent'] = 'disabled'apiPayload['usePmtu'] = 'enabled'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/tunnels/tunnel',apiPayload)
if '"kind":"tm:net:tunnels:tunnel:tunnelstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/tunnels/tunnel/' + vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:tunnels:tunnel:tunnelstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the self-IP for the tunnel-routeprint('Creating the local tunnel interface self-IP (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['address'] = vpn['tunnel-self-ip']
apiPayload['vlan'] = vpn['tunnel-name']
apiPayload['traffic-group'] = 'traffic-group-local-only'apiPayload['allowService'] = 'all'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/self',apiPayload)
if '"kind":"tm:net:self:selfstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/self/' + vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:self:selfstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Point the peer route over the tunnelprint('Creating the peer static route (' + vpn['tunnel-name'] + ')')
apiPayload = {}
apiPayload['name'] = vpn['tunnel-name']
apiPayload['tmInterface'] = vpn['tunnel-name']
apiPayload['network'] = vpn['tunnel-self-ip-remote'] + '/32'apiPayload['mtu'] = 0apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/route',apiPayload)
if '"kind":"tm:net:route:routestate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~'+vpn['tunnel-name'], apiPayload)
if '"kind":"tm:net:route:routestate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the local server routeprint('Creating the default static route to ' + vpn['new-local-self-ip-gateway'])
apiPayload = {}
apiPayload['name'] = 'local-servers'apiPayload['gw'] = vpn['new-local-self-ip-gateway']
apiPayload['network'] = vpn['local-server-subnet']
apiCallResponse = icontrol_post(host, username, password, '/mgmt/tm/net/route', apiPayload)
if '"kind":"tm:net:route:routestate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~local-servers', apiPayload)
if '"kind":"tm:net:route:routestate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the remote peer routerprint('Creating the IPSec peer route to ' + vpn['remote-public-ip'])
apiPayload = {}
apiPayload['name'] = 'remote-peer'apiPayload['gw'] = vpn['new-local-self-ip-gateway']
apiPayload['network'] = vpn['remote-public-ip']
apiCallResponse = icontrol_post(host, username, password, '/mgmt/tm/net/route', apiPayload)
if '"kind":"tm:net:route:routestate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~remote-peer', apiPayload)
if '"kind":"tm:net:route:routestate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Create the default IP forwarding virtual serverprint('Creating the default IP forwarding virtual server')
apiPayload = {}
apiPayload['name'] = 'ipsec_forwarding_vs'apiPayload['destination'] = '/Common/0.0.0.0:0'apiPayload['ipProtocol'] = 'any'apiPayload['mask'] = '0.0.0.0'apiPayload['source'] = '0.0.0.0/0'apiPayload['ipForward'] = TrueapiPayload['sourceAddressTranslation'] = {'type':'none'}
apiPayload['sourcePort'] = 'preserve-strict'apiPayload['profiles'] = ['/Common/fastL4']
apiPayload['enabled'] = TrueapiPayload['translateAddress'] = 'disabled'apiPayload['translatePort'] = 'disabled'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/ltm/virtual',apiPayload)
if '"kind":"tm:ltm:virtual:virtualstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Object exists; updating with new configuration')
apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/ltm/virtual/~Common~ipsec_forwarding_vs', apiPayload)
if '"kind":"tm:ltm:virtual:virtualstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
else:
print('Error! ' + apiCallResponse)
quit()
# Disable the ipsec.if.checkpolicy db variableprint('Disabling the db variable ipsec.if.checkpolicy to allow BGP routing')
apiPayload = {}
apiPayload['value'] = 'disable'apiCallResponse = icontrol_put(host,username,password,'/mgmt/tm/sys/db/ipsec.if.checkpolicy',apiPayload)
if '"kind":"tm:sys:db:dbstate"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
# Enable BGP and BFD on route domain 0print('Enabling routing protocols on default route domain 0')
apiPayload['routingProtocol'] = ['BFD','BGP']
apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/net/route-domain/~Common~0',apiPayload)
if '"kind":"tm:net:route-domain:route-domainstate"' in apiCallResponse:
print('Success!')
else:
print('Error!' + apiCallResponse)
quit()
# Sleep to let BGP start upprint('Waiting for BGP to start')
time.sleep(15)
# Create the BGP configuration CLI scriptprint('Creating the BGP configuration CLI script on the BIG-IP')
apiPayload = {}
apiPayload['kind'] = 'kind":"tm:cli:script:scriptstate'apiPayload['name'] = 'bgp_configuration'apiPayload['partition'] = 'Common'apiPayload['apiAnonymous'] = 'proc script::init {} {\n}\n\nproc script::run {} {\n set neighboraddr [lindex $tmsh::argv 1]\n set asnumber [lindex $tmsh::argv 2]\n set imishscript [open "/var/tmp/imish_script.tmp" "w"]\n puts $imishscript "enable"\n puts $imishscript "configure terminal"\n puts $imishscript "router bgp $asnumber"\n puts $imishscript "neighbor $neighboraddr remote-as $asnumber"\n puts $imishscript "neighbor $neighboraddr activate"\n puts $imishscript "neighbor $neighboraddr soft-reconfiguration inbound"\n puts $imishscript "neighbor $neighboraddr timers 10 30"\n puts $imishscript "neighbor $neighboraddr timers connect 10"\n puts $imishscript "end"\n puts $imishscript "write mem"\n close $imishscript\n set rc [ catch { exec imish -f /var/tmp/imish_script.tmp } result ]\n puts $rc\n puts $result\n}\n\nproc script::help {} {\n}\n\nproc script::tabc {} {\n}\n'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/cli/script',apiPayload)
if '"kind":"tm:cli:script:scriptstate"' in apiCallResponse:
print('Success!')
elif 'already exists' in apiCallResponse:
print('Script already exists! Skipping creation')
else:
print('Error! ' + apiCallResponse)
# Create the BGP configurationprint('Creating BGP AS ' + vpn['bgp-as'] + ' with neighbor ' + vpn['tunnel-self-ip-remote'])
apiPayload = {}
apiPayload['command'] = 'run'apiPayload['name'] = 'bgp_configuration'apiPayload['utilCmdArgs'] = vpn['tunnel-self-ip-remote'] + ' ' + vpn['bgp-as']
apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/cli/script',apiPayload)
print('Script executed but BGP configuration should be manually confirmed!')
# Save the configurationprint('Saving the configuration')
apiPayload = {}
apiPayload['command'] = 'save'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/sys/config',apiPayload)
if '"kind":"tm:sys:config:savestate","command":"save"' in apiCallResponse:
print('Success!')
else:
print('Error! ' + apiCallResponse)
quit()
"}},"componentScriptGroups({\"componentId\":\"custom.widget.Beta_Footer\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"component({\"componentId\":\"custom.widget.Tag_Manager_Helper\"})":{"__typename":"Component","render({\"context\":{\"component\":{\"entities\":[],\"props\":{}},\"page\":{\"entities\":[\"message:283490\"],\"name\":\"TkbMessagePage\",\"props\":{},\"url\":\"https://community.f5.com/kb/codeshare/python-script-to-install-license-and-configuration-for-ipsec-tunneling-on-a-ve-i/283490\"}}})":{"__typename":"ComponentRenderResult","html":" "}},"componentScriptGroups({\"componentId\":\"custom.widget.Tag_Manager_Helper\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"component({\"componentId\":\"custom.widget.Consent_Blackbar\"})":{"__typename":"Component","render({\"context\":{\"component\":{\"entities\":[],\"props\":{}},\"page\":{\"entities\":[\"message:283490\"],\"name\":\"TkbMessagePage\",\"props\":{},\"url\":\"https://community.f5.com/kb/codeshare/python-script-to-install-license-and-configuration-for-ipsec-tunneling-on-a-ve-i/283490\"}}})":{"__typename":"ComponentRenderResult","html":""}},"componentScriptGroups({\"componentId\":\"custom.widget.Consent_Blackbar\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/common/QueryHandler\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/community/NavbarDropdownToggle\"]})":[{"__ref":"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageView/MessageViewStandard\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageView/MessageViewStandard-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/ThreadedReplyList\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/ThreadedReplyList-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageReplyCallToAction\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageReplyCallToAction-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageSubject\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageSubject-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageBody\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageBody-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageCustomFields\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageCustomFields-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageRevision\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageRevision-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageReplyButton\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageReplyButton-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageAuthorBio\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageAuthorBio-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/guides/GuideBottomNavigation\"]})":[{"__ref":"CachedAsset:text:en_US-components/guides/GuideBottomNavigation-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/customComponent/CustomComponent\"]})":[{"__ref":"CachedAsset:text:en_US-components/customComponent/CustomComponent-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"components/users/UserLink\"]})":[{"__ref":"CachedAsset:text:en_US-components/users/UserLink-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserRank\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserRank-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserAvatar\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1748938994216"}],"cachedText({\"lastModified\":\"1748938994216\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/ranks/UserRankLabel\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/ranks/UserRankLabel-1748938994216"}]},"Theme:customTheme1":{"__typename":"Theme","id":"customTheme1"},"User:user:-1":{"__typename":"User","id":"user:-1","entityType":"USER","eventPath":"community:zihoc95639/user:-1","uid":-1,"login":"Former Member","email":"","avatar":null,"rank":null,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":"ANONYMOUS","registrationTime":null,"confirmEmailStatus":false,"registrationAccessLevel":"VIEW","ssoRegistrationFields":[]},"ssoId":null,"profileSettings":{"__typename":"ProfileSettings","dateDisplayStyle":{"__typename":"InheritableStringSettingWithPossibleValues","key":"layout.friendly_dates_enabled","value":"false","localValue":"true","possibleValues":["true","false"]},"dateDisplayFormat":{"__typename":"InheritableStringSetting","key":"layout.format_pattern_date","value":"dd-MMM-yyyy","localValue":"MM-dd-yyyy"},"language":{"__typename":"InheritableStringSettingWithPossibleValues","key":"profile.language","value":"en-US","localValue":null,"possibleValues":["en-US","en-GB","de-DE","pt-PT","pt-BR","es-ES"]},"repliesSortOrder":{"__typename":"InheritableStringSettingWithPossibleValues","key":"config.user_replies_sort_order","value":"DEFAULT","localValue":"DEFAULT","possibleValues":["DEFAULT","LIKES","PUBLISH_TIME","REVERSE_PUBLISH_TIME"]}},"deleted":false},"CachedAsset:pages-1748940063101":{"__typename":"CachedAsset","id":"pages-1748940063101","value":[{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetInvolved.MvpProgram","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved/mvp-program","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"BlogViewAllPostsPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId/all-posts/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CasePortalPage","type":"CASE_PORTAL","urlPath":"/caseportal","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CreateGroupHubPage","type":"GROUP_HUB","urlPath":"/groups/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CaseViewPage","type":"CASE_DETAILS","urlPath":"/case/:caseId/:caseNumber","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"InboxPage","type":"COMMUNITY","urlPath":"/inbox","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetInvolved.AdvocacyProgram","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved/advocacy-program","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetHelp.NonCustomer","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/non-customer","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HelpFAQPage","type":"COMMUNITY","urlPath":"/help","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetHelp.F5Customer","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/f5-customer","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"IdeaMessagePage","type":"IDEA_POST","urlPath":"/idea/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"IdeaViewAllIdeasPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/all-ideas/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"LoginPage","type":"USER","urlPath":"/signin","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"WorkstreamsPage","type":"COMMUNITY","urlPath":"/workstreams","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"BlogPostPage","type":"BLOG","urlPath":"/category/:categoryId/blogs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetInvolved","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.Learn","type":"COMMUNITY","urlPath":"/c/how-do-i/learn","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1739501996000,"localOverride":null,"page":{"id":"Test","type":"CUSTOM","urlPath":"/custom-test-2","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ThemeEditorPage","type":"COMMUNITY","urlPath":"/designer/themes","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TkbViewAllArticlesPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId/all-articles/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"OccasionEditPage","type":"EVENT","urlPath":"/event/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"OAuthAuthorizationAllowPage","type":"USER","urlPath":"/auth/authorize/allow","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"PageEditorPage","type":"COMMUNITY","urlPath":"/designer/pages","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"PostPage","type":"COMMUNITY","urlPath":"/category/:categoryId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForumBoardPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TkbBoardPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"EventPostPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"UserBadgesPage","type":"COMMUNITY","urlPath":"/users/:login/:userId/badges","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"GroupHubMembershipAction","type":"GROUP_HUB","urlPath":"/membership/join/:nodeId/:membershipType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"MaintenancePage","type":"COMMUNITY","urlPath":"/maintenance","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"IdeaReplyPage","type":"IDEA_REPLY","urlPath":"/idea/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"UserSettingsPage","type":"USER","urlPath":"/mysettings/:userSettingsTab","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"GroupHubsPage","type":"GROUP_HUB","urlPath":"/groups","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForumPostPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"OccasionRsvpActionPage","type":"OCCASION","urlPath":"/event/:boardId/:messageSubject/:messageId/rsvp/:responseType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"VerifyUserEmailPage","type":"USER","urlPath":"/verifyemail/:userId/:verifyEmailToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"AllOccasionsPage","type":"OCCASION","urlPath":"/category/:categoryId/events/:boardId/all-events/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"EventBoardPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TkbReplyPage","type":"TKB_REPLY","urlPath":"/kb/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"IdeaBoardPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CommunityGuideLinesPage","type":"COMMUNITY","urlPath":"/communityguidelines","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CaseCreatePage","type":"SALESFORCE_CASE_CREATION","urlPath":"/caseportal/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TkbEditPage","type":"TKB","urlPath":"/kb/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForgotPasswordPage","type":"USER","urlPath":"/forgotpassword","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"IdeaEditPage","type":"IDEA","urlPath":"/idea/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TagPage","type":"COMMUNITY","urlPath":"/tag/:tagName","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"BlogBoardPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"OccasionMessagePage","type":"OCCASION_TOPIC","urlPath":"/event/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ManageContentPage","type":"COMMUNITY","urlPath":"/managecontent","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ClosedMembershipNodeNonMembersPage","type":"GROUP_HUB","urlPath":"/closedgroup/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetHelp.Community","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/community","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CommunityPage","type":"COMMUNITY","urlPath":"/","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetInvolved.ContributeCode","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved/contribute-code","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForumMessagePage","type":"FORUM_TOPIC","urlPath":"/discussions/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"IdeaPostPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"BlogMessagePage","type":"BLOG_ARTICLE","urlPath":"/blog/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"RegistrationPage","type":"USER","urlPath":"/register","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"EditGroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForumEditPage","type":"FORUM","urlPath":"/discussions/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ResetPasswordPage","type":"USER","urlPath":"/resetpassword/:userId/:resetPasswordToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TkbMessagePage","type":"TKB_ARTICLE","urlPath":"/kb/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.Learn.AboutIrules","type":"COMMUNITY","urlPath":"/c/how-do-i/learn/about-irules","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"BlogEditPage","type":"BLOG","urlPath":"/blog/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetHelp.F5Support","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/f5-support","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ManageUsersPage","type":"USER","urlPath":"/users/manage/:tab?/:manageUsersTab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForumReplyPage","type":"FORUM_REPLY","urlPath":"/discussions/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"PrivacyPolicyPage","type":"COMMUNITY","urlPath":"/privacypolicy","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"NotificationPage","type":"COMMUNITY","urlPath":"/notifications","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"UserPage","type":"USER","urlPath":"/users/:login/:userId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HealthCheckPage","type":"COMMUNITY","urlPath":"/health","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"OccasionReplyPage","type":"OCCASION_REPLY","urlPath":"/event/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ManageMembersPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/manage/:tab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"SearchResultsPage","type":"COMMUNITY","urlPath":"/search","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"BlogReplyPage","type":"BLOG_REPLY","urlPath":"/blog/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"GroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TermsOfServicePage","type":"COMMUNITY","urlPath":"/termsofservice","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetHelp","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI.GetHelp.SecurityIncident","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/security-incident","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"CategoryPage","type":"CATEGORY","urlPath":"/category/:categoryId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"ForumViewAllTopicsPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/all-topics/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"TkbPostPage","type":"TKB","urlPath":"/category/:categoryId/kbs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"GroupHubPostPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1748940063101,"localOverride":null,"page":{"id":"HowDoI","type":"COMMUNITY","urlPath":"/c/how-do-i","__typename":"PageDescriptor"},"__typename":"PageResource"}],"localOverride":false},"CachedAsset:text:en_US-components/context/AppContext/AppContextProvider-0":{"__typename":"CachedAsset","id":"text:en_US-components/context/AppContext/AppContextProvider-0","value":{"noCommunity":"Cannot find community","noUser":"Cannot find current user","noNode":"Cannot find node with id {nodeId}","noMessage":"Cannot find message with id {messageId}","userBanned":"We're sorry, but you have been banned from using this site.","userBannedReason":"You have been banned for the following reason: {reason}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-0":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-0","value":{"title":"Loading..."},"localOverride":false},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/cmstMjgtQ3U0RXo2\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/cmstMjgtQ3U0RXo2","height":0,"width":0,"mimeType":"image/svg+xml"},"Rank:rank:28":{"__typename":"Rank","id":"rank:28","position":5,"name":"Employee","color":"C20025","icon":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/cmstMjgtQ3U0RXo2\"}"},"rankStyle":"OUTLINE"},"User:user:190002":{"__typename":"User","id":"user:190002","uid":190002,"login":"G-Rob","deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://community.f5.com/t5/s/zihoc95639/images/dS0xOTAwMDItMjE1NDVpQzdCNEVGMTE0QUVGRkI3OA"},"rank":{"__ref":"Rank:rank:28"},"email":"","messagesCount":63,"biography":"F5 Solutions Engineer focused on cloud, automation, modern apps and excellent customer-focused service.","topicsCount":17,"kudosReceivedCount":31,"kudosGivenCount":43,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":null,"registrationTime":"2019-05-16T14:13:21.000-07:00","confirmEmailStatus":null},"followersCount":null,"solutionsCount":6},"Category:category:CrowdSRC":{"__typename":"Category","id":"category:CrowdSRC","entityType":"CATEGORY","displayId":"CrowdSRC","nodeType":"category","depth":1,"title":"CrowdSRC","shortTitle":"CrowdSRC","parent":{"__ref":"Category:category:top"},"categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:top":{"__typename":"Category","id":"category:top","entityType":"CATEGORY","displayId":"top","nodeType":"category","depth":0,"title":"Top","shortTitle":"Top"},"Tkb:board:codeshare":{"__typename":"Tkb","id":"board:codeshare","entityType":"TKB","displayId":"codeshare","nodeType":"board","depth":2,"conversationStyle":"TKB","repliesProperties":{"__typename":"RepliesProperties","sortOrder":"PUBLISH_TIME","repliesFormat":"threaded"},"tagProperties":{"__typename":"TagNodeProperties","tagsEnabled":{"__typename":"PolicyResult","failureReason":null}},"requireTags":true,"tagType":"FREEFORM_AND_PRESET","description":"Have some code. Share some code.","title":"CodeShare","shortTitle":"CodeShare","parent":{"__ref":"Category:category:CrowdSRC"},"ancestors":{"__typename":"CoreNodeConnection","edges":[{"__typename":"CoreNodeEdge","node":{"__ref":"Community:community:zihoc95639"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:CrowdSRC"}}]},"userContext":{"__typename":"NodeUserContext","canAddAttachments":false,"canUpdateNode":false,"canPostMessages":false,"isSubscribed":false},"theme":{"__ref":"Theme:customTheme1"},"boardPolicies":{"__typename":"BoardPolicies","canViewSpamDashBoard":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.feature.moderation_spam.action.access_spam_quarantine.allowed.accessDenied","key":"error.lithium.policies.feature.moderation_spam.action.access_spam_quarantine.allowed.accessDenied","args":[]}},"canArchiveMessage":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.content_archivals.enable_content_archival_settings.accessDenied","key":"error.lithium.policies.content_archivals.enable_content_archival_settings.accessDenied","args":[]}},"canPublishArticleOnCreate":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","args":[]}},"canReadNode":{"__typename":"PolicyResult","failureReason":null}},"isManualSortOrderAvailable":false,"tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"linkProperties":{"__typename":"LinkProperties","isExternalLinkWarningEnabled":false}},"TkbTopicMessage:message:283490":{"__typename":"TkbTopicMessage","uid":283490,"subject":"Python script to install license and configuration for IPSec tunneling on a VE instance","id":"message:283490","entityType":"TKB_ARTICLE","eventPath":"category:CrowdSRC/community:zihoc95639board:codeshare/message:283490","revisionNum":1,"repliesCount":0,"author":{"__ref":"User:user:190002"},"depth":0,"hasGivenKudo":false,"helpful":null,"board":{"__ref":"Tkb:board:codeshare"},"conversation":{"__ref":"Conversation:conversation:283490"},"messagePolicies":{"__typename":"MessagePolicies","canPublishArticleOnEdit":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_edit_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_edit_workflow_action.accessDenied","args":[]}},"canModerateSpamMessage":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.feature.moderation_spam.action.moderate_entity.allowed.accessDenied","key":"error.lithium.policies.feature.moderation_spam.action.moderate_entity.allowed.accessDenied","args":[]}}},"contentWorkflow":{"__typename":"ContentWorkflow","state":"PUBLISH","scheduledPublishTime":null,"scheduledTimezone":null,"userContext":{"__typename":"MessageWorkflowContext","canSubmitForReview":null,"canEdit":false,"canRecall":null,"canSubmitForPublication":null,"canReturnToAuthor":null,"canPublish":null,"canReturnToReview":null,"canSchedule":false},"shortScheduledTimezone":null},"readOnly":false,"editFrozen":false,"showMoveIndicator":false,"moderationData":{"__ref":"ModerationData:moderation_data:283490"},"teaser":"","body":"
Problem this snippet solves:
This script can be called from the command line and used to reach out to a VE to license it, perform minimum configuration and provision an IPSec interface-mode tunnel.
This is not best practice, not hardened, etc. This is a foundation, not a solution.
How to use this snippet:
The script has built-in CLI help.
Code :
#!/usr/bin/env python3\n\nimport argparse,json,requests,time\nfrom requests.packages.urllib3.exceptions import InsecureRequestWarning\n\nrequests.packages.urllib3.disable_warnings(InsecureRequestWarning)\n\ndef icontrol_get(host,username,password,path):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.get(apiUri,verify=False)\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"get\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_post(host,username,password,path,api_payload):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.post(apiUri,verify=False,data=json.dumps(api_payload))\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"post\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"payload\":\"' + api_payload + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_put(host,username,password,path,api_payload):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.put(apiUri,verify=False,data=json.dumps(api_payload))\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"put\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"payload\":\"' + api_payload + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_patch(host,username,password,path,api_payload):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.patch(apiUri,verify=False,data=json.dumps(api_payload))\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"patch\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"payload\":\"' + api_payload + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_delete(host,username,password,path):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.delete(apiUri,verify=False)\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"delete\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\n## Parse the command line arguments#cmdargs = argparse.ArgumentParser()\ncmdargs.add_argument('--f5restip',action='store',required=True,type=str,help='ip of BIG-IP REST interface, typically the mgmt ip')\ncmdargs.add_argument('--username',action='store',required=True,type=str,help='username for REST authentication')\ncmdargs.add_argument('--password',action='store',required=True,type=str,help='password for REST authentication')\ncmdargs.add_argument('--licensekey',action='store',required=False,type=str,help='license key for licensing')\ncmdargs.add_argument('--hostname',action='store',required=False,type=str,help='hostname to configure on the BIG-IP (must be FQDN)')\ncmdargs.add_argument('--ntpserver',action='store',required=False,type=str,help='NTP server to add to the BIG-IP (IP or FQDN)')\ncmdargs.add_argument('--dnsserver',action='store',required=False,type=str,help='DNS server to add to the BIG-IP (IP address)')\ncmdargs.add_argument('--newlocalselfip',action='store',required=True,type=str,help='creates the private self-ip address of the VE where the public address is mapped (CIDR notation)')\ncmdargs.add_argument('--newlocalselfipgateway',action='store',required=True,type=str,help='creates the private self-ip subnet gateway for server communications')\ncmdargs.add_argument('--localserversubnet',action='store',required=True,type=str,help='the local VM subnet in CIDR notation')\ncmdargs.add_argument('--localselfip',action='store',required=True,type=str,help='the private self-ip address of the VE where the public address is mapped')\ncmdargs.add_argument('--localpublicip',action='store',required=True,type=str,help='the public IP address of the VNA')\ncmdargs.add_argument('--remotepublicip',action='store',required=True,type=str,help='the public IP address of the remote IPSec peer')\ncmdargs.add_argument('--presharedkey',action='store',required=True,type=str,help='the pre-shared key for encryption')\ncmdargs.add_argument('--tunnelsourcenet',action='store',required=True,type=str,help='CIDR notated source network, can be 0.0.0.0/0 to tunnel all traffic')\ncmdargs.add_argument('--tunneldestinationnet',action='store',required=True,type=str,help='CIDR notated destination network, the remote destination subnet to tunnel, can be 0.0.0.0/0 to let static routes control tunneling')\ncmdargs.add_argument('--tunnelstaticroutenet',action='store',required=True,type=str,help='CIDR notated destination network to statically route across the tunnel (this makes the decision to tunnel or not)')\ncmdargs.add_argument('--tunnelselfiplocal',action='store',required=True,type=str,help='CIDR notated tunnel self-IP address, a new address assigned to the tunnel (ie 192.168.1.1/30')\ncmdargs.add_argument('--tunnelselfipremote',action='store',required=True,type=str,help='CIDR notated tunnel self-IP address on the remote end, a new address assigned to the tunnel (ie 192.168.1.1/30')\ncmdargs.add_argument('--bgpas',action='store',required=True,type=str,help='AS number for BGP process and neighbor')\nparsed_args = cmdargs.parse_args()\n\nhost = parsed_args.f5restip\nusername = parsed_args.username\npassword = parsed_args.password\nlicense_key = parsed_args.licensekey\nhostname = parsed_args.hostname\nntpserver = parsed_args.ntpserver\ndnsserver = parsed_args.dnsserver\nvpn = {}\nvpn['local-self-ip'] = parsed_args.localselfip\nvpn['local-public-ip'] = parsed_args.localpublicip\nvpn['remote-public-ip'] = parsed_args.remotepublicip\nvpn['pre-shared-key'] = parsed_args.presharedkey\nvpn['tunnel-source-network'] = parsed_args.tunnelsourcenet\nvpn['tunnel-destination-network'] = parsed_args.tunneldestinationnet\nvpn['tunnel-static-route-network'] = parsed_args.tunnelstaticroutenet\nvpn['tunnel-name'] = 'ipsec_peer-' + vpn['remote-public-ip']\nvpn['tunnel-self-ip'] = parsed_args.tunnelselfiplocal\nvpn['new-local-self-ip'] = parsed_args.newlocalselfip\nvpn['new-local-self-ip-gateway'] = parsed_args.newlocalselfipgateway\nvpn['local-server-subnet'] = parsed_args.localserversubnet\nvpn['tunnel-self-ip-remote'] = parsed_args.tunnelselfipremote\nvpn['bgp-as'] = parsed_args.bgpas\n\n# License the VEif license_key is not None:\n print('Licensing ' + host + ' with key ' + license_key)\n apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/sys/license',{\"command\":\"install\",\"registrationKey\":license_key})\n if 'New license installed' in apiCallResponse:\n print('License installed successfully. Waiting for service restart.')\n time.sleep(60)\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print(\"License key not specified - skipping licensing\")\n\n# Disable the GUI setup wizard since we're configuring via RESTprint('Disabling the GUI Setup Wizard')\napiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/global-settings',{\"guiSetup\":\"disabled\"})\nif '\"kind\":\"tm:sys:global-settings:global-settingsstate\"' and '\"guiSetup\":\"disabled\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Set the hostname if specifiedif hostname is not None:\n print('Setting the hostname to ' + hostname + ' on ' + host)\n apiCallResponse = icontrol_patch(host, username, password, '/mgmt/tm/sys/global-settings',{\"hostname\":hostname})\n if '\"kind\":\"tm:sys:global-settings:global-settingsstate\"' and '\"hostname\":\"' + hostname + '\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Hostname not specified - skipping hostname configuration')\n\n# Set the DNS server if specifiedif dnsserver is not None:\n print('Adding DNS server ' + dnsserver + ' to ' + host)\n apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/dns',{\"nameServers\":[dnsserver]})\n if '\"kind\":\"tm:sys:dns:dnsstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('DNS server not specified - skipping DNS server configuration')\n\n# Set the NTP server if specifiedif ntpserver is not None:\n print('Adding NTP server ' + ntpserver + ' to ' + host)\n apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/ntp',{\"servers\":[ntpserver]})\n if '\"kind\":\"tm:sys:ntp:ntpstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('NTP server not specified - skipping NTP server configuration')\n\n# Configure the local VLAN and self-IP if specifiedif vpn['new-local-self-ip'] is not None:\n # create external VLAN untagged print('Creating the external VLAN')\n apiPayload = {}\n apiPayload['name'] = 'external' apiPayload['interfaces'] = ['1.1']\n apiPayload['partition'] = 'Common' apiPayload['mtu'] = 1500 apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/vlan',apiPayload)\n if '\"kind\":\"tm:net:vlan:vlanstate\"' in apiCallResponse :\n print('Success!')\n elif 'already exists' in apiCallResponse:\n print('Object exists; skipping item configuration')\n else:\n print('Error! ' + apiCallResponse)\n quit()\n # create the data interface self-IP print('Creating the self-IP address ' + vpn['local-self-ip'])\n apiPayload = {}\n apiPayload['name'] = vpn['new-local-self-ip']\n apiPayload['address'] = vpn['new-local-self-ip']\n apiPayload['allowService'] = 'all' apiPayload['interfaces'] = ['1.1']\n apiPayload['partition'] = 'Common' apiPayload['trafficGroup'] = 'traffic-group-local-only' apiPayload['vlan'] = 'external' apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/self',apiPayload)\n if '\"kind\":\"tm:net:self:selfstate\"' in apiCallResponse :\n print('Success!')\n elif 'already exists' in apiCallResponse:\n print('Object exists; skipping item configuration')\n else:\n print('Error! ' + apiCallResponse)\n quit()\n\n## Begin IPSec tunnel configuration steps## Create the IPSec policyprint('Creating the IPSec Policy (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['ikePhase2AuthAlgorithm'] = 'aes-gcm128'apiPayload['ikePhase2EncryptAlgorithm'] = 'aes-gcm128'apiPayload['ikePhase2PerfectForwardSecrecy'] = 'none'apiPayload['ikePhase2Lifetime'] = 1440apiPayload['ikePhase2LifetimeKilobytes'] = 0apiPayload['mode'] = 'interface'apiPayload['protocol'] = 'esp'apiPayload['ipcomp'] = 'none'apiPayload['partition'] = 'Common'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/ipsec-policy',apiPayload)\nif '\"kind\":\"tm:net:ipsec:ipsec-policy:ipsec-policystate\"' in apiCallResponse :\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/ipsec-policy/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:ipsec:ipsec-policy:ipsec-policystate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the IPSec traffic selectorprint('Creating the IPSec traffic selector (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['partition'] = 'Common'apiPayload['action'] = 'protect'apiPayload['sourceAddress'] = vpn['tunnel-source-network']\napiPayload['sourcePort'] = 0apiPayload['destinationAddress'] = vpn['tunnel-destination-network']\napiPayload['destinationPort'] = 0apiPayload['direction'] = 'both'apiPayload['ipProtocol'] = 255apiPayload['ipsecPolicy'] = vpn['tunnel-name']\napiPayload['order'] = 0apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/traffic-selector',apiPayload)\nif '\"kind\":\"tm:net:ipsec:traffic-selector:traffic-selectorstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/traffic-selector/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:ipsec:traffic-selector:traffic-selectorstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Set the default IPSec traffic selector to high orderprint('Setting the default IPSec traffic selector to order 100')\napiPayload = {}\napiPayload['order'] = 100apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/net/ipsec/traffic-selector/default-traffic-selector-interface',apiPayload)\nif '\"kind\":\"tm:net:ipsec:traffic-selector:traffic-selectorstate\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the IKE peerprint('Creating the IKE peer (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['myIdType'] = 'address'apiPayload['myIdValue'] = vpn['local-public-ip']\napiPayload['peersIdType'] = 'address'apiPayload['peersIdValue'] = vpn['remote-public-ip']\napiPayload['phase1AuthMethod'] = 'pre-shared-key'apiPayload['phase1EncryptAlgorithm'] = 'aes256'apiPayload['phase1HashAlgorithm'] = 'sha256'apiPayload['phase1PerfectForwardSecrecy'] = 'modp1024'apiPayload['prf'] = 'sha256'apiPayload['presharedKey'] = vpn['pre-shared-key']\napiPayload['remoteAddress'] = vpn['remote-public-ip']\napiPayload['version'] = ['v2']\napiPayload['dpdDelay'] = 30apiPayload['lifetime'] = 1440apiPayload['mode'] = 'main'apiPayload['natTraversal'] = 'off'apiPayload['passive'] = 'false'apiPayload['generatePolicy'] = 'off'apiPayload['trafficSelector'] = [vpn['tunnel-name']]\napiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/ike-peer',apiPayload)\nif '\"kind\":\"tm:net:ipsec:ike-peer:ike-peerstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/ike-peer/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:ipsec:ike-peer:ike-peerstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the tunnel profileprint('Creating the tunnel profile (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['defaultsFrom'] = '/Common/ipsec'apiPayload['trafficSelector'] = vpn['tunnel-name']\napiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/tunnels/ipsec',apiPayload)\nif '\"kind\":\"tm:net:tunnels:ipsec:ipsecstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/tunnels/ipsec/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:tunnels:ipsec:ipsecstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the tunnel objectprint('Creating the tunnel object (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['autoLasthop'] = 'default'apiPayload['idleTimeout'] = 300apiPayload['key'] = 0apiPayload['localAddress'] = vpn['local-self-ip']\napiPayload['mode'] = 'bidirectional'apiPayload['mtu'] = 0apiPayload['profile'] = vpn['tunnel-name']\napiPayload['remoteAddress'] = vpn['remote-public-ip']\napiPayload['tos'] = 'preserve'apiPayload['transparent'] = 'disabled'apiPayload['usePmtu'] = 'enabled'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/tunnels/tunnel',apiPayload)\nif '\"kind\":\"tm:net:tunnels:tunnel:tunnelstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/tunnels/tunnel/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:tunnels:tunnel:tunnelstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the self-IP for the tunnel-routeprint('Creating the local tunnel interface self-IP (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['address'] = vpn['tunnel-self-ip']\napiPayload['vlan'] = vpn['tunnel-name']\napiPayload['traffic-group'] = 'traffic-group-local-only'apiPayload['allowService'] = 'all'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/self',apiPayload)\nif '\"kind\":\"tm:net:self:selfstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/self/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:self:selfstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Point the peer route over the tunnelprint('Creating the peer static route (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['tmInterface'] = vpn['tunnel-name']\napiPayload['network'] = vpn['tunnel-self-ip-remote'] + '/32'apiPayload['mtu'] = 0apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/route',apiPayload)\nif '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~'+vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the local server routeprint('Creating the default static route to ' + vpn['new-local-self-ip-gateway'])\napiPayload = {}\napiPayload['name'] = 'local-servers'apiPayload['gw'] = vpn['new-local-self-ip-gateway']\napiPayload['network'] = vpn['local-server-subnet']\napiCallResponse = icontrol_post(host, username, password, '/mgmt/tm/net/route', apiPayload)\nif '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~local-servers', apiPayload)\n if '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the remote peer routerprint('Creating the IPSec peer route to ' + vpn['remote-public-ip'])\napiPayload = {}\napiPayload['name'] = 'remote-peer'apiPayload['gw'] = vpn['new-local-self-ip-gateway']\napiPayload['network'] = vpn['remote-public-ip']\napiCallResponse = icontrol_post(host, username, password, '/mgmt/tm/net/route', apiPayload)\nif '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~remote-peer', apiPayload)\n if '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n\n# Create the default IP forwarding virtual serverprint('Creating the default IP forwarding virtual server')\napiPayload = {}\napiPayload['name'] = 'ipsec_forwarding_vs'apiPayload['destination'] = '/Common/0.0.0.0:0'apiPayload['ipProtocol'] = 'any'apiPayload['mask'] = '0.0.0.0'apiPayload['source'] = '0.0.0.0/0'apiPayload['ipForward'] = TrueapiPayload['sourceAddressTranslation'] = {'type':'none'}\napiPayload['sourcePort'] = 'preserve-strict'apiPayload['profiles'] = ['/Common/fastL4']\napiPayload['enabled'] = TrueapiPayload['translateAddress'] = 'disabled'apiPayload['translatePort'] = 'disabled'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/ltm/virtual',apiPayload)\nif '\"kind\":\"tm:ltm:virtual:virtualstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/ltm/virtual/~Common~ipsec_forwarding_vs', apiPayload)\n if '\"kind\":\"tm:ltm:virtual:virtualstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Disable the ipsec.if.checkpolicy db variableprint('Disabling the db variable ipsec.if.checkpolicy to allow BGP routing')\napiPayload = {}\napiPayload['value'] = 'disable'apiCallResponse = icontrol_put(host,username,password,'/mgmt/tm/sys/db/ipsec.if.checkpolicy',apiPayload)\nif '\"kind\":\"tm:sys:db:dbstate\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Enable BGP and BFD on route domain 0print('Enabling routing protocols on default route domain 0')\napiPayload['routingProtocol'] = ['BFD','BGP']\napiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/net/route-domain/~Common~0',apiPayload)\nif '\"kind\":\"tm:net:route-domain:route-domainstate\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error!' + apiCallResponse)\n quit()\n\n# Sleep to let BGP start upprint('Waiting for BGP to start')\ntime.sleep(15)\n\n# Create the BGP configuration CLI scriptprint('Creating the BGP configuration CLI script on the BIG-IP')\napiPayload = {}\napiPayload['kind'] = 'kind\":\"tm:cli:script:scriptstate'apiPayload['name'] = 'bgp_configuration'apiPayload['partition'] = 'Common'apiPayload['apiAnonymous'] = 'proc script::init {} {\\n}\\n\\nproc script::run {} {\\n set neighboraddr [lindex $tmsh::argv 1]\\n set asnumber [lindex $tmsh::argv 2]\\n set imishscript [open \"/var/tmp/imish_script.tmp\" \"w\"]\\n puts $imishscript \"enable\"\\n puts $imishscript \"configure terminal\"\\n puts $imishscript \"router bgp $asnumber\"\\n puts $imishscript \"neighbor $neighboraddr remote-as $asnumber\"\\n puts $imishscript \"neighbor $neighboraddr activate\"\\n puts $imishscript \"neighbor $neighboraddr soft-reconfiguration inbound\"\\n puts $imishscript \"neighbor $neighboraddr timers 10 30\"\\n puts $imishscript \"neighbor $neighboraddr timers connect 10\"\\n puts $imishscript \"end\"\\n puts $imishscript \"write mem\"\\n close $imishscript\\n set rc [ catch { exec imish -f /var/tmp/imish_script.tmp } result ]\\n puts $rc\\n puts $result\\n}\\n\\nproc script::help {} {\\n}\\n\\nproc script::tabc {} {\\n}\\n'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/cli/script',apiPayload)\nif '\"kind\":\"tm:cli:script:scriptstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Script already exists! Skipping creation')\nelse:\n print('Error! ' + apiCallResponse)\n\n# Create the BGP configurationprint('Creating BGP AS ' + vpn['bgp-as'] + ' with neighbor ' + vpn['tunnel-self-ip-remote'])\napiPayload = {}\napiPayload['command'] = 'run'apiPayload['name'] = 'bgp_configuration'apiPayload['utilCmdArgs'] = vpn['tunnel-self-ip-remote'] + ' ' + vpn['bgp-as']\napiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/cli/script',apiPayload)\nprint('Script executed but BGP configuration should be manually confirmed!')\n\n# Save the configurationprint('Saving the configuration')\napiPayload = {}\napiPayload['command'] = 'save'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/sys/config',apiPayload)\nif '\"kind\":\"tm:sys:config:savestate\",\"command\":\"save\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()
Tested this on version:
12.1","body@stringLength":"25421","rawBody":"
Problem this snippet solves:
This script can be called from the command line and used to reach out to a VE to license it, perform minimum configuration and provision an IPSec interface-mode tunnel.
This is not best practice, not hardened, etc. This is a foundation, not a solution.
How to use this snippet:
The script has built-in CLI help.
Code :
#!/usr/bin/env python3\n\nimport argparse,json,requests,time\nfrom requests.packages.urllib3.exceptions import InsecureRequestWarning\n\nrequests.packages.urllib3.disable_warnings(InsecureRequestWarning)\n\ndef icontrol_get(host,username,password,path):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.get(apiUri,verify=False)\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"get\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_post(host,username,password,path,api_payload):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.post(apiUri,verify=False,data=json.dumps(api_payload))\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"post\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"payload\":\"' + api_payload + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_put(host,username,password,path,api_payload):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.put(apiUri,verify=False,data=json.dumps(api_payload))\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"put\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"payload\":\"' + api_payload + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_patch(host,username,password,path,api_payload):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.patch(apiUri,verify=False,data=json.dumps(api_payload))\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"patch\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"payload\":\"' + api_payload + '\",\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\ndef icontrol_delete(host,username,password,path):\n apiCall = requests.session()\n apiCall.headers.update({'Content-type':'application/json'})\n apiCall.auth = (username,password)\n apiUri = 'https://' + host + path\n try:\n apiResponse = apiCall.delete(apiUri,verify=False)\n except requests.exceptions.RequestException as e:\n print('{\"responseStatus\":\"error\",\"action\":\"delete\",\"host\":\"' + host + '\",\"username\":\"' + username + '\",\"path\":\"' + path + '\"},\"errorMsg\":\"' + str(e) + '\"}')\n return(apiResponse.text)\n\n## Parse the command line arguments#cmdargs = argparse.ArgumentParser()\ncmdargs.add_argument('--f5restip',action='store',required=True,type=str,help='ip of BIG-IP REST interface, typically the mgmt ip')\ncmdargs.add_argument('--username',action='store',required=True,type=str,help='username for REST authentication')\ncmdargs.add_argument('--password',action='store',required=True,type=str,help='password for REST authentication')\ncmdargs.add_argument('--licensekey',action='store',required=False,type=str,help='license key for licensing')\ncmdargs.add_argument('--hostname',action='store',required=False,type=str,help='hostname to configure on the BIG-IP (must be FQDN)')\ncmdargs.add_argument('--ntpserver',action='store',required=False,type=str,help='NTP server to add to the BIG-IP (IP or FQDN)')\ncmdargs.add_argument('--dnsserver',action='store',required=False,type=str,help='DNS server to add to the BIG-IP (IP address)')\ncmdargs.add_argument('--newlocalselfip',action='store',required=True,type=str,help='creates the private self-ip address of the VE where the public address is mapped (CIDR notation)')\ncmdargs.add_argument('--newlocalselfipgateway',action='store',required=True,type=str,help='creates the private self-ip subnet gateway for server communications')\ncmdargs.add_argument('--localserversubnet',action='store',required=True,type=str,help='the local VM subnet in CIDR notation')\ncmdargs.add_argument('--localselfip',action='store',required=True,type=str,help='the private self-ip address of the VE where the public address is mapped')\ncmdargs.add_argument('--localpublicip',action='store',required=True,type=str,help='the public IP address of the VNA')\ncmdargs.add_argument('--remotepublicip',action='store',required=True,type=str,help='the public IP address of the remote IPSec peer')\ncmdargs.add_argument('--presharedkey',action='store',required=True,type=str,help='the pre-shared key for encryption')\ncmdargs.add_argument('--tunnelsourcenet',action='store',required=True,type=str,help='CIDR notated source network, can be 0.0.0.0/0 to tunnel all traffic')\ncmdargs.add_argument('--tunneldestinationnet',action='store',required=True,type=str,help='CIDR notated destination network, the remote destination subnet to tunnel, can be 0.0.0.0/0 to let static routes control tunneling')\ncmdargs.add_argument('--tunnelstaticroutenet',action='store',required=True,type=str,help='CIDR notated destination network to statically route across the tunnel (this makes the decision to tunnel or not)')\ncmdargs.add_argument('--tunnelselfiplocal',action='store',required=True,type=str,help='CIDR notated tunnel self-IP address, a new address assigned to the tunnel (ie 192.168.1.1/30')\ncmdargs.add_argument('--tunnelselfipremote',action='store',required=True,type=str,help='CIDR notated tunnel self-IP address on the remote end, a new address assigned to the tunnel (ie 192.168.1.1/30')\ncmdargs.add_argument('--bgpas',action='store',required=True,type=str,help='AS number for BGP process and neighbor')\nparsed_args = cmdargs.parse_args()\n\nhost = parsed_args.f5restip\nusername = parsed_args.username\npassword = parsed_args.password\nlicense_key = parsed_args.licensekey\nhostname = parsed_args.hostname\nntpserver = parsed_args.ntpserver\ndnsserver = parsed_args.dnsserver\nvpn = {}\nvpn['local-self-ip'] = parsed_args.localselfip\nvpn['local-public-ip'] = parsed_args.localpublicip\nvpn['remote-public-ip'] = parsed_args.remotepublicip\nvpn['pre-shared-key'] = parsed_args.presharedkey\nvpn['tunnel-source-network'] = parsed_args.tunnelsourcenet\nvpn['tunnel-destination-network'] = parsed_args.tunneldestinationnet\nvpn['tunnel-static-route-network'] = parsed_args.tunnelstaticroutenet\nvpn['tunnel-name'] = 'ipsec_peer-' + vpn['remote-public-ip']\nvpn['tunnel-self-ip'] = parsed_args.tunnelselfiplocal\nvpn['new-local-self-ip'] = parsed_args.newlocalselfip\nvpn['new-local-self-ip-gateway'] = parsed_args.newlocalselfipgateway\nvpn['local-server-subnet'] = parsed_args.localserversubnet\nvpn['tunnel-self-ip-remote'] = parsed_args.tunnelselfipremote\nvpn['bgp-as'] = parsed_args.bgpas\n\n# License the VEif license_key is not None:\n print('Licensing ' + host + ' with key ' + license_key)\n apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/sys/license',{\"command\":\"install\",\"registrationKey\":license_key})\n if 'New license installed' in apiCallResponse:\n print('License installed successfully. Waiting for service restart.')\n time.sleep(60)\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print(\"License key not specified - skipping licensing\")\n\n# Disable the GUI setup wizard since we're configuring via RESTprint('Disabling the GUI Setup Wizard')\napiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/global-settings',{\"guiSetup\":\"disabled\"})\nif '\"kind\":\"tm:sys:global-settings:global-settingsstate\"' and '\"guiSetup\":\"disabled\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Set the hostname if specifiedif hostname is not None:\n print('Setting the hostname to ' + hostname + ' on ' + host)\n apiCallResponse = icontrol_patch(host, username, password, '/mgmt/tm/sys/global-settings',{\"hostname\":hostname})\n if '\"kind\":\"tm:sys:global-settings:global-settingsstate\"' and '\"hostname\":\"' + hostname + '\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Hostname not specified - skipping hostname configuration')\n\n# Set the DNS server if specifiedif dnsserver is not None:\n print('Adding DNS server ' + dnsserver + ' to ' + host)\n apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/dns',{\"nameServers\":[dnsserver]})\n if '\"kind\":\"tm:sys:dns:dnsstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('DNS server not specified - skipping DNS server configuration')\n\n# Set the NTP server if specifiedif ntpserver is not None:\n print('Adding NTP server ' + ntpserver + ' to ' + host)\n apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/sys/ntp',{\"servers\":[ntpserver]})\n if '\"kind\":\"tm:sys:ntp:ntpstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('NTP server not specified - skipping NTP server configuration')\n\n# Configure the local VLAN and self-IP if specifiedif vpn['new-local-self-ip'] is not None:\n # create external VLAN untagged print('Creating the external VLAN')\n apiPayload = {}\n apiPayload['name'] = 'external' apiPayload['interfaces'] = ['1.1']\n apiPayload['partition'] = 'Common' apiPayload['mtu'] = 1500 apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/vlan',apiPayload)\n if '\"kind\":\"tm:net:vlan:vlanstate\"' in apiCallResponse :\n print('Success!')\n elif 'already exists' in apiCallResponse:\n print('Object exists; skipping item configuration')\n else:\n print('Error! ' + apiCallResponse)\n quit()\n # create the data interface self-IP print('Creating the self-IP address ' + vpn['local-self-ip'])\n apiPayload = {}\n apiPayload['name'] = vpn['new-local-self-ip']\n apiPayload['address'] = vpn['new-local-self-ip']\n apiPayload['allowService'] = 'all' apiPayload['interfaces'] = ['1.1']\n apiPayload['partition'] = 'Common' apiPayload['trafficGroup'] = 'traffic-group-local-only' apiPayload['vlan'] = 'external' apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/self',apiPayload)\n if '\"kind\":\"tm:net:self:selfstate\"' in apiCallResponse :\n print('Success!')\n elif 'already exists' in apiCallResponse:\n print('Object exists; skipping item configuration')\n else:\n print('Error! ' + apiCallResponse)\n quit()\n\n## Begin IPSec tunnel configuration steps## Create the IPSec policyprint('Creating the IPSec Policy (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['ikePhase2AuthAlgorithm'] = 'aes-gcm128'apiPayload['ikePhase2EncryptAlgorithm'] = 'aes-gcm128'apiPayload['ikePhase2PerfectForwardSecrecy'] = 'none'apiPayload['ikePhase2Lifetime'] = 1440apiPayload['ikePhase2LifetimeKilobytes'] = 0apiPayload['mode'] = 'interface'apiPayload['protocol'] = 'esp'apiPayload['ipcomp'] = 'none'apiPayload['partition'] = 'Common'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/ipsec-policy',apiPayload)\nif '\"kind\":\"tm:net:ipsec:ipsec-policy:ipsec-policystate\"' in apiCallResponse :\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/ipsec-policy/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:ipsec:ipsec-policy:ipsec-policystate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the IPSec traffic selectorprint('Creating the IPSec traffic selector (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['partition'] = 'Common'apiPayload['action'] = 'protect'apiPayload['sourceAddress'] = vpn['tunnel-source-network']\napiPayload['sourcePort'] = 0apiPayload['destinationAddress'] = vpn['tunnel-destination-network']\napiPayload['destinationPort'] = 0apiPayload['direction'] = 'both'apiPayload['ipProtocol'] = 255apiPayload['ipsecPolicy'] = vpn['tunnel-name']\napiPayload['order'] = 0apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/traffic-selector',apiPayload)\nif '\"kind\":\"tm:net:ipsec:traffic-selector:traffic-selectorstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/traffic-selector/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:ipsec:traffic-selector:traffic-selectorstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Set the default IPSec traffic selector to high orderprint('Setting the default IPSec traffic selector to order 100')\napiPayload = {}\napiPayload['order'] = 100apiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/net/ipsec/traffic-selector/default-traffic-selector-interface',apiPayload)\nif '\"kind\":\"tm:net:ipsec:traffic-selector:traffic-selectorstate\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the IKE peerprint('Creating the IKE peer (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['myIdType'] = 'address'apiPayload['myIdValue'] = vpn['local-public-ip']\napiPayload['peersIdType'] = 'address'apiPayload['peersIdValue'] = vpn['remote-public-ip']\napiPayload['phase1AuthMethod'] = 'pre-shared-key'apiPayload['phase1EncryptAlgorithm'] = 'aes256'apiPayload['phase1HashAlgorithm'] = 'sha256'apiPayload['phase1PerfectForwardSecrecy'] = 'modp1024'apiPayload['prf'] = 'sha256'apiPayload['presharedKey'] = vpn['pre-shared-key']\napiPayload['remoteAddress'] = vpn['remote-public-ip']\napiPayload['version'] = ['v2']\napiPayload['dpdDelay'] = 30apiPayload['lifetime'] = 1440apiPayload['mode'] = 'main'apiPayload['natTraversal'] = 'off'apiPayload['passive'] = 'false'apiPayload['generatePolicy'] = 'off'apiPayload['trafficSelector'] = [vpn['tunnel-name']]\napiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/ipsec/ike-peer',apiPayload)\nif '\"kind\":\"tm:net:ipsec:ike-peer:ike-peerstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/ipsec/ike-peer/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:ipsec:ike-peer:ike-peerstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the tunnel profileprint('Creating the tunnel profile (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['defaultsFrom'] = '/Common/ipsec'apiPayload['trafficSelector'] = vpn['tunnel-name']\napiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/tunnels/ipsec',apiPayload)\nif '\"kind\":\"tm:net:tunnels:ipsec:ipsecstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/tunnels/ipsec/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:tunnels:ipsec:ipsecstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the tunnel objectprint('Creating the tunnel object (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['autoLasthop'] = 'default'apiPayload['idleTimeout'] = 300apiPayload['key'] = 0apiPayload['localAddress'] = vpn['local-self-ip']\napiPayload['mode'] = 'bidirectional'apiPayload['mtu'] = 0apiPayload['profile'] = vpn['tunnel-name']\napiPayload['remoteAddress'] = vpn['remote-public-ip']\napiPayload['tos'] = 'preserve'apiPayload['transparent'] = 'disabled'apiPayload['usePmtu'] = 'enabled'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/tunnels/tunnel',apiPayload)\nif '\"kind\":\"tm:net:tunnels:tunnel:tunnelstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/tunnels/tunnel/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:tunnels:tunnel:tunnelstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the self-IP for the tunnel-routeprint('Creating the local tunnel interface self-IP (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['address'] = vpn['tunnel-self-ip']\napiPayload['vlan'] = vpn['tunnel-name']\napiPayload['traffic-group'] = 'traffic-group-local-only'apiPayload['allowService'] = 'all'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/self',apiPayload)\nif '\"kind\":\"tm:net:self:selfstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/self/' + vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:self:selfstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Point the peer route over the tunnelprint('Creating the peer static route (' + vpn['tunnel-name'] + ')')\napiPayload = {}\napiPayload['name'] = vpn['tunnel-name']\napiPayload['tmInterface'] = vpn['tunnel-name']\napiPayload['network'] = vpn['tunnel-self-ip-remote'] + '/32'apiPayload['mtu'] = 0apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/net/route',apiPayload)\nif '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~'+vpn['tunnel-name'], apiPayload)\n if '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the local server routeprint('Creating the default static route to ' + vpn['new-local-self-ip-gateway'])\napiPayload = {}\napiPayload['name'] = 'local-servers'apiPayload['gw'] = vpn['new-local-self-ip-gateway']\napiPayload['network'] = vpn['local-server-subnet']\napiCallResponse = icontrol_post(host, username, password, '/mgmt/tm/net/route', apiPayload)\nif '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~local-servers', apiPayload)\n if '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Create the remote peer routerprint('Creating the IPSec peer route to ' + vpn['remote-public-ip'])\napiPayload = {}\napiPayload['name'] = 'remote-peer'apiPayload['gw'] = vpn['new-local-self-ip-gateway']\napiPayload['network'] = vpn['remote-public-ip']\napiCallResponse = icontrol_post(host, username, password, '/mgmt/tm/net/route', apiPayload)\nif '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/net/route/~Common~remote-peer', apiPayload)\n if '\"kind\":\"tm:net:route:routestate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n\n# Create the default IP forwarding virtual serverprint('Creating the default IP forwarding virtual server')\napiPayload = {}\napiPayload['name'] = 'ipsec_forwarding_vs'apiPayload['destination'] = '/Common/0.0.0.0:0'apiPayload['ipProtocol'] = 'any'apiPayload['mask'] = '0.0.0.0'apiPayload['source'] = '0.0.0.0/0'apiPayload['ipForward'] = TrueapiPayload['sourceAddressTranslation'] = {'type':'none'}\napiPayload['sourcePort'] = 'preserve-strict'apiPayload['profiles'] = ['/Common/fastL4']\napiPayload['enabled'] = TrueapiPayload['translateAddress'] = 'disabled'apiPayload['translatePort'] = 'disabled'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/ltm/virtual',apiPayload)\nif '\"kind\":\"tm:ltm:virtual:virtualstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Object exists; updating with new configuration')\n apiCallResponse = icontrol_put(host, username, password, '/mgmt/tm/ltm/virtual/~Common~ipsec_forwarding_vs', apiPayload)\n if '\"kind\":\"tm:ltm:virtual:virtualstate\"' in apiCallResponse:\n print('Success!')\n else:\n print('Error! ' + apiCallResponse)\n quit()\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Disable the ipsec.if.checkpolicy db variableprint('Disabling the db variable ipsec.if.checkpolicy to allow BGP routing')\napiPayload = {}\napiPayload['value'] = 'disable'apiCallResponse = icontrol_put(host,username,password,'/mgmt/tm/sys/db/ipsec.if.checkpolicy',apiPayload)\nif '\"kind\":\"tm:sys:db:dbstate\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()\n\n# Enable BGP and BFD on route domain 0print('Enabling routing protocols on default route domain 0')\napiPayload['routingProtocol'] = ['BFD','BGP']\napiCallResponse = icontrol_patch(host,username,password,'/mgmt/tm/net/route-domain/~Common~0',apiPayload)\nif '\"kind\":\"tm:net:route-domain:route-domainstate\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error!' + apiCallResponse)\n quit()\n\n# Sleep to let BGP start upprint('Waiting for BGP to start')\ntime.sleep(15)\n\n# Create the BGP configuration CLI scriptprint('Creating the BGP configuration CLI script on the BIG-IP')\napiPayload = {}\napiPayload['kind'] = 'kind\":\"tm:cli:script:scriptstate'apiPayload['name'] = 'bgp_configuration'apiPayload['partition'] = 'Common'apiPayload['apiAnonymous'] = 'proc script::init {} {\\n}\\n\\nproc script::run {} {\\n set neighboraddr [lindex $tmsh::argv 1]\\n set asnumber [lindex $tmsh::argv 2]\\n set imishscript [open \"/var/tmp/imish_script.tmp\" \"w\"]\\n puts $imishscript \"enable\"\\n puts $imishscript \"configure terminal\"\\n puts $imishscript \"router bgp $asnumber\"\\n puts $imishscript \"neighbor $neighboraddr remote-as $asnumber\"\\n puts $imishscript \"neighbor $neighboraddr activate\"\\n puts $imishscript \"neighbor $neighboraddr soft-reconfiguration inbound\"\\n puts $imishscript \"neighbor $neighboraddr timers 10 30\"\\n puts $imishscript \"neighbor $neighboraddr timers connect 10\"\\n puts $imishscript \"end\"\\n puts $imishscript \"write mem\"\\n close $imishscript\\n set rc [ catch { exec imish -f /var/tmp/imish_script.tmp } result ]\\n puts $rc\\n puts $result\\n}\\n\\nproc script::help {} {\\n}\\n\\nproc script::tabc {} {\\n}\\n'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/cli/script',apiPayload)\nif '\"kind\":\"tm:cli:script:scriptstate\"' in apiCallResponse:\n print('Success!')\nelif 'already exists' in apiCallResponse:\n print('Script already exists! Skipping creation')\nelse:\n print('Error! ' + apiCallResponse)\n\n# Create the BGP configurationprint('Creating BGP AS ' + vpn['bgp-as'] + ' with neighbor ' + vpn['tunnel-self-ip-remote'])\napiPayload = {}\napiPayload['command'] = 'run'apiPayload['name'] = 'bgp_configuration'apiPayload['utilCmdArgs'] = vpn['tunnel-self-ip-remote'] + ' ' + vpn['bgp-as']\napiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/cli/script',apiPayload)\nprint('Script executed but BGP configuration should be manually confirmed!')\n\n# Save the configurationprint('Saving the configuration')\napiPayload = {}\napiPayload['command'] = 'save'apiCallResponse = icontrol_post(host,username,password,'/mgmt/tm/sys/config',apiPayload)\nif '\"kind\":\"tm:sys:config:savestate\",\"command\":\"save\"' in apiCallResponse:\n print('Success!')\nelse:\n print('Error! ' + apiCallResponse)\n quit()
Tested this on version:
12.1","kudosSumWeight":0,"postTime":"2019-08-13T06:06:22.000-07:00","images":{"__typename":"AssociatedImageConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"attachments":{"__typename":"AttachmentConnection","pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null},"edges":[]},"tags":{"__typename":"TagConnection","pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null},"edges":[]},"timeToRead":13,"rawTeaser":"","introduction":"","currentRevision":{"__ref":"Revision:revision:283490_1"},"latestVersion":{"__typename":"FriendlyVersion","major":"1","minor":"0"},"metrics":{"__typename":"MessageMetrics","views":483},"read":false,"visibilityScope":"PUBLIC","canonicalUrl":null,"seoTitle":null,"seoDescription":null,"placeholder":false,"originalMessageForPlaceholder":null,"contributors":{"__typename":"UserConnection","edges":[]},"nonCoAuthorContributors":{"__typename":"UserConnection","edges":[]},"coAuthors":{"__typename":"UserConnection","edges":[{"__typename":"UserEdge","node":{"__ref":"User:user:190002"}}]},"tkbMessagePolicies":{"__typename":"TkbMessagePolicies","canDoAuthoringActionsOnTkb":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.tkb.policy_can_do_authoring_action.accessDenied","key":"error.lithium.policies.tkb.policy_can_do_authoring_action.accessDenied","args":[]}}},"archivalData":null,"replies":{"__typename":"MessageConnection","edges":[],"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"customFields":[],"revisions({\"constraints\":{\"isPublished\":{\"eq\":true}}})":{"__typename":"RevisionConnection","totalCount":1}},"Conversation:conversation:283490":{"__typename":"Conversation","id":"conversation:283490","solved":false,"topic":{"__ref":"TkbTopicMessage:message:283490"},"lastPostingActivityTime":"2019-08-13T06:06:22.000-07:00","lastPostTime":"2019-08-13T06:06:22.000-07:00","unreadReplyCount":0,"isSubscribed":false},"ModerationData:moderation_data:283490":{"__typename":"ModerationData","id":"moderation_data:283490","status":"APPROVED","rejectReason":null,"isReportedAbuse":false,"rejectUser":null,"rejectTime":null,"rejectActorType":null},"Revision:revision:283490_1":{"__typename":"Revision","id":"revision:283490_1","lastEditTime":"2019-08-13T06:06:22.000-07:00"},"CachedAsset:theme:customTheme1-1749226372729":{"__typename":"CachedAsset","id":"theme:customTheme1-1749226372729","value":{"id":"customTheme1","animation":{"fast":"150ms","normal":"250ms","slow":"500ms","slowest":"750ms","function":"cubic-bezier(0.07, 0.91, 0.51, 1)","__typename":"AnimationThemeSettings"},"avatar":{"borderRadius":"50%","collections":["custom"],"__typename":"AvatarThemeSettings"},"basics":{"browserIcon":{"imageAssetName":"android-chrome-512x512-1748534255255.png","imageLastModified":"1748534256856","__typename":"ThemeAsset"},"customerLogo":{"imageAssetName":"devcentral-type-lockup-1748534239063.svg","imageLastModified":"1748534240836","__typename":"ThemeAsset"},"maximumWidthOfPageContent":"fluid","oneColumnNarrowWidth":"800px","gridGutterWidthMd":"30px","gridGutterWidthXs":"10px","pageWidthStyle":"WIDTH_OF_PAGE_CONTENT","__typename":"BasicsThemeSettings"},"buttons":{"borderRadiusSm":"5px","borderRadius":"5px","borderRadiusLg":"5px","paddingY":"5px","paddingYLg":"7px","paddingYHero":"var(--lia-bs-btn-padding-y-lg)","paddingX":"12px","paddingXLg":"14px","paddingXHero":"42px","fontStyle":"NORMAL","fontWeight":"500","textTransform":"NONE","disabledOpacity":0.5,"primaryTextColor":"var(--lia-bs-white)","primaryTextHoverColor":"var(--lia-bs-white)","primaryTextActiveColor":"var(--lia-bs-white)","primaryBgColor":"var(--lia-bs-primary)","primaryBgHoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.85))","primaryBgActiveColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.7))","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","primaryBorderActive":"1px solid transparent","primaryBorderFocus":"1px solid var(--lia-bs-white)","primaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","secondaryTextColor":"var(--lia-bs-gray-900)","secondaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","secondaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","secondaryBgColor":"var(--lia-bs-gray-400)","secondaryBgHoverColor":"hsl(var(--lia-bs-gray-400-h), var(--lia-bs-gray-400-s), calc(var(--lia-bs-gray-400-l) * 0.96))","secondaryBgActiveColor":"hsl(var(--lia-bs-gray-400-h), var(--lia-bs-gray-400-s), calc(var(--lia-bs-gray-400-l) * 0.92))","secondaryBorder":"1px solid transparent","secondaryBorderHover":"1px solid transparent","secondaryBorderActive":"1px solid transparent","secondaryBorderFocus":"1px solid transparent","secondaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","tertiaryTextColor":"var(--lia-bs-gray-900)","tertiaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","tertiaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","tertiaryBgColor":"transparent","tertiaryBgHoverColor":"transparent","tertiaryBgActiveColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.04)","tertiaryBorder":"1px solid transparent","tertiaryBorderHover":"1px solid hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","tertiaryBorderActive":"1px solid transparent","tertiaryBorderFocus":"1px solid transparent","tertiaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","destructiveTextColor":"var(--lia-bs-danger)","destructiveTextHoverColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.95))","destructiveTextActiveColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.9))","destructiveBgColor":"var(--lia-bs-gray-300)","destructiveBgHoverColor":"hsl(var(--lia-bs-gray-300-h), var(--lia-bs-gray-300-s), calc(var(--lia-bs-gray-300-l) * 0.96))","destructiveBgActiveColor":"hsl(var(--lia-bs-gray-300-h), var(--lia-bs-gray-300-s), calc(var(--lia-bs-gray-300-l) * 0.92))","destructiveBorder":"1px solid transparent","destructiveBorderHover":"1px solid transparent","destructiveBorderActive":"1px solid transparent","destructiveBorderFocus":"1px solid transparent","destructiveBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","__typename":"ButtonsThemeSettings"},"border":{"color":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","mainContent":"NONE","sideContent":"NONE","radiusSm":"3px","radius":"5px","radiusLg":"9px","radius50":"100vw","__typename":"BorderThemeSettings"},"boxShadow":{"xs":"0 0 0 1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.08), 0 3px 0 -1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.08)","sm":"0 2px 4px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.06)","md":"0 5px 15px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.15)","lg":"0 10px 30px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.15)","__typename":"BoxShadowThemeSettings"},"cards":{"bgColor":"var(--lia-panel-bg-color)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":"var(--lia-box-shadow-xs)","__typename":"CardsThemeSettings"},"chip":{"maxWidth":"300px","height":"30px","__typename":"ChipThemeSettings"},"coreTypes":{"defaultMessageLinkColor":"var(--lia-bs-primary)","defaultMessageLinkDecoration":"none","defaultMessageLinkFontStyle":"NORMAL","defaultMessageLinkFontWeight":"500","defaultMessageFontStyle":"NORMAL","defaultMessageFontWeight":"400","defaultMessageFontFamily":"var(--lia-bs-font-family-base)","forumColor":"#0C5C8D","forumFontFamily":"var(--lia-bs-font-family-base)","forumFontWeight":"var(--lia-default-message-font-weight)","forumLineHeight":"var(--lia-bs-line-height-base)","forumFontStyle":"var(--lia-default-message-font-style)","forumMessageLinkColor":"var(--lia-default-message-link-color)","forumMessageLinkDecoration":"var(--lia-default-message-link-decoration)","forumMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","forumMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","forumSolvedColor":"#62C026","blogColor":"#730015","blogFontFamily":"var(--lia-bs-font-family-base)","blogFontWeight":"var(--lia-default-message-font-weight)","blogLineHeight":"1.75","blogFontStyle":"var(--lia-default-message-font-style)","blogMessageLinkColor":"var(--lia-default-message-link-color)","blogMessageLinkDecoration":"var(--lia-default-message-link-decoration)","blogMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","blogMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","tkbColor":"#C20025","tkbFontFamily":"var(--lia-bs-font-family-base)","tkbFontWeight":"var(--lia-default-message-font-weight)","tkbLineHeight":"1.75","tkbFontStyle":"var(--lia-default-message-font-style)","tkbMessageLinkColor":"var(--lia-default-message-link-color)","tkbMessageLinkDecoration":"var(--lia-default-message-link-decoration)","tkbMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","tkbMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaColor":"#4099E2","qandaFontFamily":"var(--lia-bs-font-family-base)","qandaFontWeight":"var(--lia-default-message-font-weight)","qandaLineHeight":"var(--lia-bs-line-height-base)","qandaFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkColor":"var(--lia-default-message-link-color)","qandaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","qandaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaSolvedColor":"#3FA023","ideaColor":"#F3704B","ideaFontFamily":"var(--lia-bs-font-family-base)","ideaFontWeight":"var(--lia-default-message-font-weight)","ideaLineHeight":"var(--lia-bs-line-height-base)","ideaFontStyle":"var(--lia-default-message-font-style)","ideaMessageLinkColor":"var(--lia-default-message-link-color)","ideaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","ideaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","ideaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","contestColor":"#FCC845","contestFontFamily":"var(--lia-bs-font-family-base)","contestFontWeight":"var(--lia-default-message-font-weight)","contestLineHeight":"var(--lia-bs-line-height-base)","contestFontStyle":"var(--lia-default-message-link-font-style)","contestMessageLinkColor":"var(--lia-default-message-link-color)","contestMessageLinkDecoration":"var(--lia-default-message-link-decoration)","contestMessageLinkFontStyle":"ITALIC","contestMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","occasionColor":"#EE4B5B","occasionFontFamily":"var(--lia-bs-font-family-base)","occasionFontWeight":"var(--lia-default-message-font-weight)","occasionLineHeight":"var(--lia-bs-line-height-base)","occasionFontStyle":"var(--lia-default-message-font-style)","occasionMessageLinkColor":"var(--lia-default-message-link-color)","occasionMessageLinkDecoration":"var(--lia-default-message-link-decoration)","occasionMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","occasionMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","grouphubColor":"#491B62","categoryColor":"#949494","communityColor":"#FFFFFF","productColor":"#949494","__typename":"CoreTypesThemeSettings"},"colors":{"black":"#000000","white":"#FFFFFF","gray100":"#F7F7F7","gray200":"#F7F7F7","gray300":"#E8E8E8","gray400":"#D9D9D9","gray500":"#CCCCCC","gray600":"#949494","gray700":"#707070","gray800":"#545454","gray900":"#333333","dark":"#545454","light":"#F7F7F7","primary":"#0C5C8D","secondary":"#333333","bodyText":"#222222","bodyBg":"#F5F5F5","info":"#1D9CD3","success":"#62C026","warning":"#FFD651","danger":"#C20025","alertSystem":"#FF6600","textMuted":"#707070","highlight":"#FFFCAD","outline":"var(--lia-bs-primary)","custom":["#C20025","#081B85","#009639","#B3C6D7","#7CC0EB","#F29A36","#B2D7EB","#66AFD7","#007ABC","#343434"],"__typename":"ColorsThemeSettings"},"divider":{"size":"3px","marginLeft":"4px","marginRight":"4px","borderRadius":"50%","bgColor":"var(--lia-bs-gray-600)","bgColorActive":"var(--lia-bs-gray-600)","__typename":"DividerThemeSettings"},"dropdown":{"fontSize":"var(--lia-bs-font-size-sm)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius-sm)","dividerBg":"var(--lia-bs-gray-300)","itemPaddingY":"5px","itemPaddingX":"20px","headerColor":"var(--lia-bs-gray-700)","__typename":"DropdownThemeSettings"},"email":{"link":{"color":"#0069D4","hoverColor":"#0061c2","decoration":"none","hoverDecoration":"underline","__typename":"EmailLinkSettings"},"border":{"color":"#e4e4e4","__typename":"EmailBorderSettings"},"buttons":{"borderRadiusLg":"5px","paddingXLg":"16px","paddingYLg":"7px","fontWeight":"700","primaryTextColor":"#ffffff","primaryTextHoverColor":"#ffffff","primaryBgColor":"#0069D4","primaryBgHoverColor":"#005cb8","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","__typename":"EmailButtonsSettings"},"panel":{"borderRadius":"5px","borderColor":"#e4e4e4","__typename":"EmailPanelSettings"},"__typename":"EmailThemeSettings"},"emoji":{"skinToneDefault":"#ffcd43","skinToneLight":"#fae3c5","skinToneMediumLight":"#e2cfa5","skinToneMedium":"#daa478","skinToneMediumDark":"#a78058","skinToneDark":"#5e4d43","__typename":"EmojiThemeSettings"},"heading":{"color":"var(--lia-bs-body-color)","fontFamily":"Neusa Next Pro Wide Bold","fontStyle":"NORMAL","fontWeight":"700","h1FontSize":"30px","h2FontSize":"25px","h3FontSize":"20px","h4FontSize":"18px","h5FontSize":"16px","h6FontSize":"16px","lineHeight":"1.1","subHeaderFontSize":"11px","subHeaderFontWeight":"500","h1LetterSpacing":"normal","h2LetterSpacing":"normal","h3LetterSpacing":"normal","h4LetterSpacing":"normal","h5LetterSpacing":"normal","h6LetterSpacing":"normal","subHeaderLetterSpacing":"2px","h1FontWeight":"var(--lia-bs-headings-font-weight)","h2FontWeight":"var(--lia-bs-headings-font-weight)","h3FontWeight":"var(--lia-bs-headings-font-weight)","h4FontWeight":"var(--lia-bs-headings-font-weight)","h5FontWeight":"var(--lia-bs-headings-font-weight)","h6FontWeight":"var(--lia-bs-headings-font-weight)","__typename":"HeadingThemeSettings"},"icons":{"size10":"10px","size12":"12px","size14":"14px","size16":"16px","size20":"20px","size24":"24px","size30":"30px","size40":"40px","size50":"50px","size60":"60px","size80":"80px","size120":"120px","size160":"160px","__typename":"IconsThemeSettings"},"imagePreview":{"bgColor":"var(--lia-bs-gray-900)","titleColor":"var(--lia-bs-white)","controlColor":"var(--lia-bs-white)","controlBgColor":"var(--lia-bs-gray-800)","__typename":"ImagePreviewThemeSettings"},"input":{"borderColor":"var(--lia-bs-gray-600)","disabledColor":"var(--lia-bs-gray-600)","focusBorderColor":"var(--lia-bs-primary)","labelMarginBottom":"10px","btnFontSize":"var(--lia-bs-font-size-sm)","focusBoxShadow":"0 0 0 3px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","checkLabelMarginBottom":"2px","checkboxBorderRadius":"3px","borderRadiusSm":"var(--lia-bs-border-radius-sm)","borderRadius":"var(--lia-bs-border-radius)","borderRadiusLg":"var(--lia-bs-border-radius-lg)","formTextMarginTop":"4px","textAreaBorderRadius":"var(--lia-bs-border-radius)","activeFillColor":"var(--lia-bs-primary)","__typename":"InputThemeSettings"},"loading":{"dotDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.2)","dotLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.5)","barDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.06)","barLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.4)","__typename":"LoadingThemeSettings"},"link":{"color":"var(--lia-bs-primary)","hoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) - 10%))","decoration":"none","hoverDecoration":"underline","__typename":"LinkThemeSettings"},"listGroup":{"itemPaddingY":"15px","itemPaddingX":"15px","borderColor":"var(--lia-bs-gray-300)","__typename":"ListGroupThemeSettings"},"modal":{"contentTextColor":"var(--lia-bs-body-color)","contentBg":"var(--lia-bs-white)","backgroundBg":"var(--lia-bs-black)","smSize":"440px","mdSize":"760px","lgSize":"1080px","backdropOpacity":0.3,"contentBoxShadowXs":"var(--lia-bs-box-shadow-sm)","contentBoxShadow":"var(--lia-bs-box-shadow)","headerFontWeight":"700","__typename":"ModalThemeSettings"},"navbar":{"position":"FIXED","background":{"attachment":null,"clip":null,"color":"var(--lia-bs-white)","imageAssetName":null,"imageLastModified":"0","origin":null,"position":"CENTER_CENTER","repeat":"NO_REPEAT","size":"COVER","__typename":"BackgroundProps"},"backgroundOpacity":0.8,"paddingTop":"15px","paddingBottom":"15px","borderBottom":"1px solid var(--lia-bs-border-color)","boxShadow":"var(--lia-bs-box-shadow-sm)","brandMarginRight":"30px","brandMarginRightSm":"10px","brandLogoHeight":"30px","linkGap":"10px","linkJustifyContent":"flex-start","linkPaddingY":"5px","linkPaddingX":"10px","linkDropdownPaddingY":"9px","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkColor":"var(--lia-bs-body-color)","linkHoverColor":"var(--lia-bs-primary)","linkFontSize":"var(--lia-bs-font-size-sm)","linkFontStyle":"NORMAL","linkFontWeight":"400","linkTextTransform":"NONE","linkLetterSpacing":"normal","linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkBgColor":"transparent","linkBgHoverColor":"transparent","linkBorder":"none","linkBorderHover":"none","linkBoxShadow":"none","linkBoxShadowHover":"none","linkTextBorderBottom":"none","linkTextBorderBottomHover":"none","dropdownPaddingTop":"10px","dropdownPaddingBottom":"15px","dropdownPaddingX":"10px","dropdownMenuOffset":"2px","dropdownDividerMarginTop":"10px","dropdownDividerMarginBottom":"10px","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","controllerIconColor":"var(--lia-bs-body-color)","controllerIconHoverColor":"var(--lia-bs-body-color)","controllerTextColor":"var(--lia-nav-controller-icon-color)","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","controllerHighlightColor":"hsla(30, 100%, 50%)","controllerHighlightTextColor":"var(--lia-yiq-light)","controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerColor":"var(--lia-nav-controller-icon-color)","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","hamburgerBgColor":"transparent","hamburgerBgHoverColor":"transparent","hamburgerBorder":"none","hamburgerBorderHover":"none","collapseMenuMarginLeft":"20px","collapseMenuDividerBg":"var(--lia-nav-link-color)","collapseMenuDividerOpacity":0.16,"__typename":"NavbarThemeSettings"},"pager":{"textColor":"var(--lia-bs-link-color)","textFontWeight":"var(--lia-font-weight-md)","textFontSize":"var(--lia-bs-font-size-sm)","__typename":"PagerThemeSettings"},"panel":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-bs-border-radius)","borderColor":"var(--lia-bs-border-color)","boxShadow":"none","__typename":"PanelThemeSettings"},"popover":{"arrowHeight":"8px","arrowWidth":"16px","maxWidth":"300px","minWidth":"100px","headerBg":"var(--lia-bs-white)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius)","boxShadow":"0 0.5rem 1rem hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.15)","__typename":"PopoverThemeSettings"},"prism":{"color":"#000000","bgColor":"#f5f2f0","fontFamily":"var(--font-family-monospace)","fontSize":"var(--lia-bs-font-size-base)","fontWeightBold":"var(--lia-bs-font-weight-bold)","fontStyleItalic":"italic","tabSize":2,"highlightColor":"#b3d4fc","commentColor":"#62707e","punctuationColor":"#6f6f6f","namespaceOpacity":"0.7","propColor":"#990055","selectorColor":"#517a00","operatorColor":"#906736","operatorBgColor":"hsla(0, 0%, 100%, 0.5)","keywordColor":"#0076a9","functionColor":"#d3284b","variableColor":"#c14700","__typename":"PrismThemeSettings"},"rte":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":" var(--lia-panel-box-shadow)","customColor1":"#bfedd2","customColor2":"#fbeeb8","customColor3":"#f8cac6","customColor4":"#eccafa","customColor5":"#c2e0f4","customColor6":"#2dc26b","customColor7":"#f1c40f","customColor8":"#e03e2d","customColor9":"#b96ad9","customColor10":"#3598db","customColor11":"#169179","customColor12":"#e67e23","customColor13":"#ba372a","customColor14":"#843fa1","customColor15":"#236fa1","customColor16":"#ecf0f1","customColor17":"#ced4d9","customColor18":"#95a5a6","customColor19":"#7e8c8d","customColor20":"#34495e","customColor21":"#000000","customColor22":"#ffffff","defaultMessageHeaderMarginTop":"14px","defaultMessageHeaderMarginBottom":"10px","defaultMessageItemMarginTop":"0","defaultMessageItemMarginBottom":"10px","diffAddedColor":"hsla(170, 53%, 51%, 0.4)","diffChangedColor":"hsla(43, 97%, 63%, 0.4)","diffNoneColor":"hsla(0, 0%, 80%, 0.4)","diffRemovedColor":"hsla(9, 74%, 47%, 0.4)","specialMessageHeaderMarginTop":"14px","specialMessageHeaderMarginBottom":"10px","specialMessageItemMarginTop":"0","specialMessageItemMarginBottom":"10px","tableBgColor":"transparent","tableBorderColor":"var(--lia-bs-gray-700)","tableBorderStyle":"solid","tableCellPaddingX":"5px","tableCellPaddingY":"5px","tableTextColor":"var(--lia-bs-body-color)","tableVerticalAlign":"middle","__typename":"RteThemeSettings"},"tags":{"bgColor":"var(--lia-bs-gray-200)","bgHoverColor":"var(--lia-bs-gray-400)","borderRadius":"var(--lia-bs-border-radius-sm)","color":"var(--lia-bs-body-color)","hoverColor":"var(--lia-bs-body-color)","fontWeight":"var(--lia-font-weight-md)","fontSize":"var(--lia-font-size-xxs)","textTransform":"UPPERCASE","letterSpacing":"0.5px","__typename":"TagsThemeSettings"},"toasts":{"borderRadius":"var(--lia-bs-border-radius)","paddingX":"12px","__typename":"ToastsThemeSettings"},"typography":{"fontFamilyBase":"Proxima Nova A Medium","fontStyleBase":"NORMAL","fontWeightBase":"500","fontWeightLight":"300","fontWeightNormal":"400","fontWeightMd":"500","fontWeightBold":"700","letterSpacingSm":"normal","letterSpacingXs":"normal","lineHeightBase":"1.2","fontSizeBase":"15px","fontSizeXxs":"11px","fontSizeXs":"12px","fontSizeSm":"13px","fontSizeLg":"20px","fontSizeXl":"24px","smallFontSize":"14px","customFonts":[{"source":"SERVER","name":"Proxima Nova A Medium","styles":[{"style":"NORMAL","weight":"500","__typename":"FontStyleData"}],"assetNames":["ProximaNovaAMedium-normal-500.woff2"],"__typename":"CustomFont"},{"source":"SERVER","name":"Neusa Next Pro Wide Bold","styles":[{"style":"NORMAL","weight":"700","__typename":"FontStyleData"}],"assetNames":["NeusaNextProWideBold-normal-700.woff2"],"__typename":"CustomFont"}],"__typename":"TypographyThemeSettings"},"unstyledListItem":{"marginBottomSm":"5px","marginBottomMd":"10px","marginBottomLg":"15px","marginBottomXl":"20px","marginBottomXxl":"25px","__typename":"UnstyledListItemThemeSettings"},"yiq":{"light":"#ffffff","dark":"#000000","__typename":"YiqThemeSettings"},"colorLightness":{"primaryDark":0.36,"primaryLight":0.74,"primaryLighter":0.89,"primaryLightest":0.95,"infoDark":0.39,"infoLight":0.72,"infoLighter":0.85,"infoLightest":0.93,"successDark":0.24,"successLight":0.62,"successLighter":0.8,"successLightest":0.91,"warningDark":0.39,"warningLight":0.68,"warningLighter":0.84,"warningLightest":0.93,"dangerDark":0.41,"dangerLight":0.72,"dangerLighter":0.89,"dangerLightest":0.95,"__typename":"ColorLightnessThemeSettings"},"localOverride":false,"__typename":"Theme"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-1748938994216","value":{"title":"Loading..."},"localOverride":false},"CachedAsset:quilt:f5.prod:pages/kbs/TkbMessagePage:board:codeshare-1748938993176":{"__typename":"CachedAsset","id":"quilt:f5.prod:pages/kbs/TkbMessagePage:board:codeshare-1748938993176","value":{"id":"TkbMessagePage","container":{"id":"Common","headerProps":{"backgroundImageProps":null,"backgroundColor":null,"addComponents":null,"removeComponents":["community.widget.bannerWidget"],"componentOrder":null,"__typename":"QuiltContainerSectionProps"},"headerComponentProps":{"community.widget.breadcrumbWidget":{"disableLastCrumbForDesktop":false}},"footerProps":null,"footerComponentProps":null,"items":[{"id":"message-list","layout":"MAIN_SIDE","bgColor":"transparent","showTitle":true,"showDescription":true,"textPosition":"CENTER","textColor":"var(--lia-bs-body-color)","sectionEditLevel":null,"bgImage":null,"disableSpacing":null,"edgeToEdgeDisplay":null,"fullHeight":null,"showBorder":null,"__typename":"MainSideQuiltSection","columnMap":{"main":[{"id":"tkbs.widget.tkbArticleWidget","className":"lia-tkb-container","props":{"contributorListType":"panel","showHelpfulness":false,"showTimestamp":true,"showGuideNavigationSection":true,"showVersion":true,"lazyLoad":false,"editLevel":"CONFIGURE"},"__typename":"QuiltComponent"}],"side":[{"id":"featuredWidgets.widget.featuredContentWidget","className":null,"props":{"instanceId":"featuredWidgets.widget.featuredContentWidget-1702666556326","layoutProps":{"layout":"card","layoutOptions":{"useRepliesCount":false,"useAuthorRank":false,"useTimeToRead":true,"useKudosCount":false,"useViewCount":true,"usePreviewMedia":true,"useBody":false,"useCenteredCardContent":false,"useTags":true,"useTimestamp":false,"useBoardLink":true,"useAuthorLink":false,"useSolvedBadge":true}},"titleSrOnly":false,"showPager":true,"pageSize":3,"lazyLoad":true},"__typename":"QuiltComponent"},{"id":"messages.widget.relatedContentWidget","className":null,"props":{"hideIfEmpty":true,"enablePagination":true,"useTitle":true,"listVariant":{"type":"listGroup"},"pageSize":3,"style":"list","pagerVariant":{"type":"loadMore"},"viewVariant":{"type":"inline","props":{"useRepliesCount":true,"useMedia":true,"useAuthorRank":false,"useNode":true,"useTimeToRead":true,"useSpoilerFreeBody":true,"useKudosCount":true,"useNodeLink":true,"useViewCount":true,"usePreviewMedia":false,"useBody":false,"timeStampType":"postTime","useTags":true,"clampSubjectLines":2,"useBoardIcon":false,"useMessageTimeLink":true,"clampBodyLines":3,"useTextBody":true,"useSolvedBadge":true,"useAvatar":true,"useAuthorLogin":true,"useUnreadCount":true}},"lazyLoad":true,"panelType":"divider"},"__typename":"QuiltComponent"}],"__typename":"MainSideSectionColumns"}}],"__typename":"QuiltContainer"},"__typename":"Quilt","localOverride":false},"localOverride":false},"CachedAsset:text:en_US-components/common/EmailVerification-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/common/EmailVerification-1748938994216","value":{"email.verification.title":"Email Verification Required","email.verification.message.update.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. To change your email, visit My Settings.","email.verification.message.resend.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. Resend email."},"localOverride":false},"CachedAsset:text:en_US-pages/kbs/TkbMessagePage-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-pages/kbs/TkbMessagePage-1748938994216","value":{"title":"{contextMessageSubject} | {communityTitle}","errorMissing":"This article cannot be found","name":"TKB Message Page","section.message-list.title":"","archivedMessageTitle":"This Content Has Been Archived","section.erPqcf.title":"","section.erPqcf.description":"","section.message-list.description":""},"localOverride":false},"CachedAsset:text:en_US-components/common/ActionFeedback-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/common/ActionFeedback-1748938994216","value":{"joinedGroupHub.title":"Welcome","joinedGroupHub.message":"You are now a member of this group and are subscribed to updates.","groupHubInviteNotFound.title":"Invitation Not Found","groupHubInviteNotFound.message":"Sorry, we could not find your invitation to the group. The owner may have canceled the invite.","groupHubNotFound.title":"Group Not Found","groupHubNotFound.message":"The grouphub you tried to join does not exist. It may have been deleted.","existingGroupHubMember.title":"Already Joined","existingGroupHubMember.message":"You are already a member of this group.","accountLocked.title":"Account Locked","accountLocked.message":"Your account has been locked due to multiple failed attempts. Try again in {lockoutTime} minutes.","editedGroupHub.title":"Changes Saved","editedGroupHub.message":"Your group has been updated.","leftGroupHub.title":"Goodbye","leftGroupHub.message":"You are no longer a member of this group and will not receive future updates.","deletedGroupHub.title":"Deleted","deletedGroupHub.message":"The group has been deleted.","groupHubCreated.title":"Group Created","groupHubCreated.message":"{groupHubName} is ready to use","accountClosed.title":"Account Closed","accountClosed.message":"The account has been closed and you will now be redirected to the homepage","resetTokenExpired.title":"Reset Password Link has Expired","resetTokenExpired.message":"Try resetting your password again","invalidUrl.title":"Invalid URL","invalidUrl.message":"The URL you're using is not recognized. Verify your URL and try again.","accountClosedForUser.title":"Account Closed","accountClosedForUser.message":"{userName}'s account is closed","inviteTokenInvalid.title":"Invitation Invalid","inviteTokenInvalid.message":"Your invitation to the community has been canceled or expired.","inviteTokenError.title":"Invitation Verification Failed","inviteTokenError.message":"The url you are utilizing is not recognized. Verify your URL and try again","pageNotFound.title":"Access Denied","pageNotFound.message":"You do not have access to this area of the community or it doesn't exist","eventAttending.title":"Responded as Attending","eventAttending.message":"You'll be notified when there's new activity and reminded as the event approaches","eventInterested.title":"Responded as Interested","eventInterested.message":"You'll be notified when there's new activity and reminded as the event approaches","eventNotFound.title":"Event Not Found","eventNotFound.message":"The event you tried to respond to does not exist.","redirectToRelatedPage.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.message":"The content you are trying to access is archived","redirectToRelatedPage.message":"The content you are trying to access is archived","relatedUrl.archivalLink.flyoutMessage":"The content you are trying to access is archived View Archived Content"},"localOverride":false},"CachedAsset:quiltWrapper:f5.prod:Common:1748938993646":{"__typename":"CachedAsset","id":"quiltWrapper:f5.prod:Common:1748938993646","value":{"id":"Common","header":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"#66AFD7","items":[{"id":"custom.widget.GainsightShared","props":{"widgetVisibility":"signedInOnly","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.Beta_MetaNav","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"community.widget.navbarWidget","props":{"showUserName":false,"showRegisterLink":true,"useIconLanguagePicker":true,"useLabelLanguagePicker":true,"style":{"boxShadow":"var(--lia-bs-box-shadow-sm)","linkFontWeight":"700","controllerHighlightColor":"#F29A36","dropdownDividerMarginBottom":"10px","hamburgerBorderHover":"none","linkFontSize":"15px","linkBoxShadowHover":"none","backgroundOpacity":1,"controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerBgColor":"transparent","linkTextBorderBottom":"none","hamburgerColor":"var(--lia-nav-controller-icon-color)","brandLogoHeight":"48px","linkLetterSpacing":"normal","linkBgHoverColor":"transparent","collapseMenuDividerOpacity":0.16,"paddingBottom":"10px","dropdownPaddingBottom":"15px","dropdownMenuOffset":"2px","hamburgerBgHoverColor":"transparent","borderBottom":"unset","hamburgerBorder":"none","dropdownPaddingX":"10px","brandMarginRightSm":"10px","linkBoxShadow":"none","linkJustifyContent":"center","linkColor":"#343434","collapseMenuDividerBg":"var(--lia-nav-link-color)","dropdownPaddingTop":"10px","controllerHighlightTextColor":"var(--lia-yiq-dark)","controllerTextColor":"var(--lia-nav-controller-icon-color)","background":{"imageAssetName":"","color":"#B2D7EB","size":"COVER","repeat":"NO_REPEAT","position":"CENTER_CENTER","imageLastModified":""},"linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkHoverColor":"#343434","position":"FIXED","linkBorder":"none","linkTextBorderBottomHover":"2px solid #343434","brandMarginRight":"30px","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","linkBorderHover":"none","collapseMenuMarginLeft":"20px","linkFontStyle":"NORMAL","linkPaddingX":"10px","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","paddingTop":"10px","linkPaddingY":"5px","linkTextTransform":"NONE","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkBgColor":"transparent","linkDropdownPaddingY":"9px","controllerIconColor":"#343434","dropdownDividerMarginTop":"10px","linkGap":"10px","controllerIconHoverColor":"#343434"},"links":{"sideLinks":[],"logoLinks":[],"mainLinks":[{"children":[{"linkType":"INTERNAL","id":"migrated-link-1","params":{"boardId":"TechnicalForum","categoryId":"Forums"},"routeName":"ForumBoardPage"},{"linkType":"INTERNAL","id":"migrated-link-2","params":{"boardId":"WaterCooler","categoryId":"Forums"},"routeName":"ForumBoardPage"}],"linkType":"INTERNAL","id":"migrated-link-0","params":{"categoryId":"Forums"},"routeName":"CategoryPage"},{"children":[{"linkType":"INTERNAL","id":"migrated-link-4","params":{"boardId":"codeshare","categoryId":"CrowdSRC"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"migrated-link-5","params":{"boardId":"communityarticles","categoryId":"CrowdSRC"},"routeName":"TkbBoardPage"}],"linkType":"INTERNAL","id":"migrated-link-3","params":{"categoryId":"CrowdSRC"},"routeName":"CategoryPage"},{"children":[{"linkType":"INTERNAL","id":"migrated-link-7","params":{"boardId":"TechnicalArticles","categoryId":"Articles"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"article-series","params":{"boardId":"article-series","categoryId":"Articles"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"security-insights","params":{"boardId":"security-insights","categoryId":"Articles"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"migrated-link-8","params":{"boardId":"DevCentralNews","categoryId":"Articles"},"routeName":"TkbBoardPage"}],"linkType":"INTERNAL","id":"migrated-link-6","params":{"categoryId":"Articles"},"routeName":"CategoryPage"},{"children":[{"linkType":"INTERNAL","id":"migrated-link-10","params":{"categoryId":"CommunityGroups"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"migrated-link-11","params":{"categoryId":"F5-Groups"},"routeName":"CategoryPage"}],"linkType":"INTERNAL","id":"migrated-link-9","params":{"categoryId":"GroupsCategory"},"routeName":"CategoryPage"},{"children":[],"linkType":"INTERNAL","id":"migrated-link-12","params":{"boardId":"Events","categoryId":"top"},"routeName":"EventBoardPage"},{"children":[],"linkType":"INTERNAL","id":"migrated-link-13","params":{"boardId":"Suggestions","categoryId":"top"},"routeName":"IdeaBoardPage"},{"children":[],"linkType":"EXTERNAL","id":"Common-external-link","url":"https://community.f5.com/c/how-do-i","target":"SELF"}]},"className":"QuiltComponent_lia-component-edit-mode__lQ9Z6","showSearchIcon":false,"languagePickerStyle":"iconAndLabel"},"__typename":"QuiltComponent"},{"id":"community.widget.bannerWidget","props":{"backgroundColor":"transparent","visualEffects":{"showBottomBorder":false},"backgroundImageProps":{"backgroundSize":"COVER","backgroundPosition":"CENTER_CENTER","backgroundRepeat":"NO_REPEAT"},"fontColor":"#222222"},"__typename":"QuiltComponent"},{"id":"community.widget.breadcrumbWidget","props":{"backgroundColor":"#007ABC","linkHighlightColor":"#FFFFFF","visualEffects":{"showBottomBorder":false},"backgroundOpacity":100,"linkTextColor":"#FFFFFF"},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"footer":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"var(--lia-bs-body-color)","items":[{"id":"custom.widget.Beta_Footer","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.Tag_Manager_Helper","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.Consent_Blackbar","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"__typename":"QuiltWrapper","localOverride":false},"localOverride":false},"CachedAsset:component:custom.widget.GainsightShared-en-us-1748939019167":{"__typename":"CachedAsset","id":"component:custom.widget.GainsightShared-en-us-1748939019167","value":{"component":{"id":"custom.widget.GainsightShared","template":{"id":"GainsightShared","markupLanguage":"HTML","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"Shared functions for Gainsight integration","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.GainsightShared","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"TEXTHTML","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"Shared functions for Gainsight integration","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Beta_MetaNav-en-us-1748939019167":{"__typename":"CachedAsset","id":"component:custom.widget.Beta_MetaNav-en-us-1748939019167","value":{"component":{"id":"custom.widget.Beta_MetaNav","template":{"id":"Beta_MetaNav","markupLanguage":"HANDLEBARS","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"MetaNav menu at the top of every page.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Beta_MetaNav","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"MetaNav menu at the top of every page.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Beta_Footer-en-us-1748939019167":{"__typename":"CachedAsset","id":"component:custom.widget.Beta_Footer-en-us-1748939019167","value":{"component":{"id":"custom.widget.Beta_Footer","template":{"id":"Beta_Footer","markupLanguage":"HANDLEBARS","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"DevCentral´s custom footer.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Beta_Footer","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"DevCentral´s custom footer.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Tag_Manager_Helper-en-us-1748939019167":{"__typename":"CachedAsset","id":"component:custom.widget.Tag_Manager_Helper-en-us-1748939019167","value":{"component":{"id":"custom.widget.Tag_Manager_Helper","template":{"id":"Tag_Manager_Helper","markupLanguage":"HANDLEBARS","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"Helper widget to inject Tag Manager scripts into head element","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Tag_Manager_Helper","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"Helper widget to inject Tag Manager scripts into head element","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Consent_Blackbar-en-us-1748939019167":{"__typename":"CachedAsset","id":"component:custom.widget.Consent_Blackbar-en-us-1748939019167","value":{"component":{"id":"custom.widget.Consent_Blackbar","template":{"id":"Consent_Blackbar","markupLanguage":"HTML","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Consent_Blackbar","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"TEXTHTML","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:text:en_US-components/community/Breadcrumb-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/community/Breadcrumb-1748938994216","value":{"navLabel":"Breadcrumbs","dropdown":"Additional parent page navigation"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBanner-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBanner-1748938994216","value":{"messageMarkedAsSpam":"This post has been marked as spam","messageMarkedAsSpam@board:TKB":"This article has been marked as spam","messageMarkedAsSpam@board:BLOG":"This post has been marked as spam","messageMarkedAsSpam@board:FORUM":"This discussion has been marked as spam","messageMarkedAsSpam@board:OCCASION":"This event has been marked as spam","messageMarkedAsSpam@board:IDEA":"This idea has been marked as spam","manageSpam":"Manage Spam","messageMarkedAsAbuse":"This post has been marked as abuse","messageMarkedAsAbuse@board:TKB":"This article has been marked as abuse","messageMarkedAsAbuse@board:BLOG":"This post has been marked as abuse","messageMarkedAsAbuse@board:FORUM":"This discussion has been marked as abuse","messageMarkedAsAbuse@board:OCCASION":"This event has been marked as abuse","messageMarkedAsAbuse@board:IDEA":"This idea has been marked as abuse","preModCommentAuthorText":"This comment will be published as soon as it is approved","preModCommentModeratorText":"This comment is awaiting moderation","messageMarkedAsOther":"This post has been rejected due to other reasons","messageMarkedAsOther@board:TKB":"This article has been rejected due to other reasons","messageMarkedAsOther@board:BLOG":"This post has been rejected due to other reasons","messageMarkedAsOther@board:FORUM":"This discussion has been rejected due to other reasons","messageMarkedAsOther@board:OCCASION":"This event has been rejected due to other reasons","messageMarkedAsOther@board:IDEA":"This idea has been rejected due to other reasons","messageArchived":"This post was archived on {date}","relatedUrl":"View Related Content","relatedContentText":"Showing related content","archivedContentLink":"View Archived Content"},"localOverride":false},"CachedAsset:text:en_US-components/tkbs/TkbArticleWidget-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/tkbs/TkbArticleWidget-1748938994216","value":{},"localOverride":false},"Category:category:Forums":{"__typename":"Category","id":"category:Forums","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Forum:board:TechnicalForum":{"__typename":"Forum","id":"board:TechnicalForum","forumPolicies":{"__typename":"ForumPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Forum:board:WaterCooler":{"__typename":"Forum","id":"board:WaterCooler","forumPolicies":{"__typename":"ForumPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Articles":{"__typename":"Category","id":"category:Articles","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:TechnicalArticles":{"__typename":"Tkb","id":"board:TechnicalArticles","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:DevCentralNews":{"__typename":"Tkb","id":"board:DevCentralNews","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:GroupsCategory":{"__typename":"Category","id":"category:GroupsCategory","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:F5-Groups":{"__typename":"Category","id":"category:F5-Groups","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:CommunityGroups":{"__typename":"Category","id":"category:CommunityGroups","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Occasion:board:Events":{"__typename":"Occasion","id":"board:Events","boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"occasionPolicies":{"__typename":"OccasionPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Idea:board:Suggestions":{"__typename":"Idea","id":"board:Suggestions","boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"ideaPolicies":{"__typename":"IdeaPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:communityarticles":{"__typename":"Tkb","id":"board:communityarticles","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:security-insights":{"__typename":"Tkb","id":"board:security-insights","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:article-series":{"__typename":"Tkb","id":"board:article-series","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"CachedAsset:text:en_US-components/community/Navbar-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/community/Navbar-1748938994216","value":{"community":"Community Home","inbox":"Inbox","manageContent":"Manage Content","tos":"Terms of Service","forgotPassword":"Forgot Password","themeEditor":"Theme Editor","edit":"Edit Navigation Bar","skipContent":"Skip to content","migrated-link-9":"Groups","migrated-link-7":"Technical Articles","migrated-link-8":"DevCentral News","migrated-link-1":"Technical Forum","migrated-link-10":"Community Groups","migrated-link-2":"Water Cooler","migrated-link-11":"F5 Groups","Common-external-link":"How Do I...?","migrated-link-0":"Forums","article-series":"Article Series","migrated-link-5":"Community Articles","migrated-link-6":"Articles","security-insights":"Security Insights","migrated-link-3":"CrowdSRC","migrated-link-4":"CodeShare","migrated-link-12":"Events","migrated-link-13":"Suggestions"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarHamburgerDropdown-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarHamburgerDropdown-1748938994216","value":{"hamburgerLabel":"Side Menu"},"localOverride":false},"CachedAsset:text:en_US-components/community/BrandLogo-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/community/BrandLogo-1748938994216","value":{"logoAlt":"Khoros","themeLogoAlt":"Brand Logo"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarTextLinks-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarTextLinks-1748938994216","value":{"more":"More"},"localOverride":false},"CachedAsset:text:en_US-components/authentication/AuthenticationLink-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/authentication/AuthenticationLink-1748938994216","value":{"title.login":"Sign In","title.registration":"Register","title.forgotPassword":"Forgot Password","title.multiAuthLogin":"Sign In"},"localOverride":false},"CachedAsset:text:en_US-components/nodes/NodeLink-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/nodes/NodeLink-1748938994216","value":{"place":"Place {name}"},"localOverride":false},"QueryVariables:TopicReplyList:message:283490:1":{"__typename":"QueryVariables","id":"TopicReplyList:message:283490:1","value":{"id":"message:283490","first":10,"sorts":{"postTime":{"direction":"ASC"}},"repliesFirst":3,"repliesFirstDepthThree":1,"repliesSorts":{"postTime":{"direction":"ASC"}},"useAvatar":true,"useAuthorLogin":true,"useAuthorRank":true,"useBody":true,"useKudosCount":true,"useTimeToRead":false,"useMedia":false,"useReadOnlyIcon":false,"useRepliesCount":true,"useSearchSnippet":false,"useAcceptedSolutionButton":false,"useSolvedBadge":false,"useAttachments":false,"attachmentsFirst":5,"useTags":true,"useNodeAncestors":false,"useUserHoverCard":false,"useNodeHoverCard":false,"useModerationStatus":true,"usePreviewSubjectModal":false,"useMessageStatus":true}},"ROOT_MUTATION":{"__typename":"Mutation"},"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/QueryHandler-1748938994216","value":{"title":"Query Handler"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarDropdownToggle-1748938994216","value":{"ariaLabelClosed":"Press the down arrow to open the menu"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageView/MessageViewStandard-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageView/MessageViewStandard-1748938994216","value":{"anonymous":"Anonymous","author":"{messageAuthorLogin}","authorBy":"{messageAuthorLogin}","board":"{messageBoardTitle}","replyToUser":" to {parentAuthor}","showMoreReplies":"Show More","replyText":"Reply","repliesText":"Replies","markedAsSolved":"Marked as Solution","messageStatus":"Status: ","statusChanged":"Status changed: {previousStatus} to {currentStatus}","statusAdded":"Status added: {status}","statusRemoved":"Status removed: {status}","labelExpand":"expand replies","labelCollapse":"collapse replies","unhelpfulReason.reason1":"Content is outdated","unhelpfulReason.reason2":"Article is missing information","unhelpfulReason.reason3":"Content is for a different Product","unhelpfulReason.reason4":"Doesn't match what I was searching for"},"localOverride":false},"CachedAsset:text:en_US-components/messages/ThreadedReplyList-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/ThreadedReplyList-1748938994216","value":{"title":"{count, plural, one{# Reply} other{# Replies}}","title@board:BLOG":"{count, plural, one{# Comment} other{# Comments}}","title@board:TKB":"{count, plural, one{# Comment} other{# Comments}}","title@board:IDEA":"{count, plural, one{# Comment} other{# Comments}}","title@board:OCCASION":"{count, plural, one{# Comment} other{# Comments}}","noRepliesTitle":"No Replies","noRepliesTitle@board:BLOG":"No Comments","noRepliesTitle@board:TKB":"No Comments","noRepliesTitle@board:IDEA":"No Comments","noRepliesTitle@board:OCCASION":"No Comments","noRepliesDescription":"Be the first to reply","noRepliesDescription@board:BLOG":"Be the first to comment","noRepliesDescription@board:TKB":"Be the first to comment","noRepliesDescription@board:IDEA":"Be the first to comment","noRepliesDescription@board:OCCASION":"Be the first to comment","messageReadOnlyAlert:BLOG":"Comments have been turned off for this post","messageReadOnlyAlert:TKB":"Comments have been turned off for this article","messageReadOnlyAlert:IDEA":"Comments have been turned off for this idea","messageReadOnlyAlert:FORUM":"Replies have been turned off for this discussion","messageReadOnlyAlert:OCCASION":"Comments have been turned off for this event"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageReplyCallToAction-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageReplyCallToAction-1748938994216","value":{"leaveReply":"Leave a reply...","leaveReply@board:BLOG@message:root":"Leave a comment...","leaveReply@board:TKB@message:root":"Leave a comment...","leaveReply@board:IDEA@message:root":"Leave a comment...","leaveReply@board:OCCASION@message:root":"Leave a comment...","repliesTurnedOff.FORUM":"Replies are turned off for this topic","repliesTurnedOff.BLOG":"Comments are turned off for this topic","repliesTurnedOff.TKB":"Comments are turned off for this topic","repliesTurnedOff.IDEA":"Comments are turned off for this topic","repliesTurnedOff.OCCASION":"Comments are turned off for this topic","infoText":"Stop poking me!"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageSubject-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageSubject-1748938994216","value":{"noSubject":"(no subject)"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBody-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBody-1748938994216","value":{"showMessageBody":"Show More","mentionsErrorTitle":"{mentionsType, select, board {Board} user {User} message {Message} other {}} No Longer Available","mentionsErrorMessage":"The {mentionsType} you are trying to view has been removed from the community.","videoProcessing":"Video is being processed. Please try again in a few minutes.","bannerTitle":"Video provider requires cookies to play the video. Accept to continue or {url} it directly on the provider's site.","buttonTitle":"Accept","urlText":"watch"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageCustomFields-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageCustomFields-1748938994216","value":{"CustomField.default.label":"Value of {name}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageRevision-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageRevision-1748938994216","value":{"lastUpdatedDatePublished":"{publishCount, plural, one{Published} other{Updated}} {date}","lastUpdatedDateDraft":"Created {date}","version":"Version {major}.{minor}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageReplyButton-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageReplyButton-1748938994216","value":{"repliesCount":"{count}","title":"Reply","title@board:BLOG@message:root":"Comment","title@board:TKB@message:root":"Comment","title@board:IDEA@message:root":"Comment","title@board:OCCASION@message:root":"Comment"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageAuthorBio-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageAuthorBio-1748938994216","value":{"sendMessage":"Send Message","actionMessage":"Follow this blog board to get notified when there's new activity","coAuthor":"CO-PUBLISHER","contributor":"CONTRIBUTOR","userProfile":"View Profile","iconlink":"Go to {name} {type}"},"localOverride":false},"CachedAsset:text:en_US-components/guides/GuideBottomNavigation-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/guides/GuideBottomNavigation-1748938994216","value":{"nav.label":"Previous/Next Page","nav.previous":"Previous","nav.next":"Next"},"localOverride":false},"CachedAsset:text:en_US-components/customComponent/CustomComponent-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/customComponent/CustomComponent-1748938994216","value":{"errorMessage":"Error rendering component id: {customComponentId}","bannerTitle":"Video provider requires cookies to play the video. Accept to continue or {url} it directly on the provider's site.","buttonTitle":"Accept","urlText":"watch"},"localOverride":false},"CachedAsset:text:en_US-components/users/UserLink-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-components/users/UserLink-1748938994216","value":{"authorName":"View Profile: {author}","anonymous":"Anonymous"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserRank-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserRank-1748938994216","value":{"rankName":"{rankName}","userRank":"Author rank {rankName}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserAvatar-1748938994216","value":{"altText":"{login}'s avatar","altTextGeneric":"User's avatar"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/ranks/UserRankLabel-1748938994216":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/ranks/UserRankLabel-1748938994216","value":{"altTitle":"Icon for {rankName} rank"},"localOverride":false}}}},"page":"/kbs/TkbMessagePage/TkbMessagePage","query":{"boardId":"codeshare","messageSubject":"python-script-to-install-license-and-configuration-for-ipsec-tunneling-on-a-ve-i","messageId":"283490"},"buildId":"L6311oNj_FtHpWug1zHiY","runtimeConfig":{"buildInformationVisible":false,"logLevelApp":"info","logLevelMetrics":"info","surveysEnabled":true,"openTelemetry":{"clientEnabled":false,"configName":"f5","serviceVersion":"25.4.0","universe":"prod","collector":"http://localhost:4318","logLevel":"error","routeChangeAllowedTime":"5000","headers":"","enableDiagnostic":"false","maxAttributeValueLength":"4095"},"apolloDevToolsEnabled":false,"quiltLazyLoadThreshold":"3"},"isFallback":false,"isExperimentalCompile":false,"dynamicIds":["components_customComponent_CustomComponent","components_community_Navbar_NavbarWidget","components_community_Breadcrumb_BreadcrumbWidget","components_tkbs_TkbArticleWidget","components_messages_MessageView_MessageViewStandard","components_messages_ThreadedReplyList","components_customComponent_CustomComponentContent_TemplateContent","components_customComponent_CustomComponentContent_HtmlContent","components_customComponent_CustomComponentContent_CustomComponentScripts"],"appGip":true,"scriptLoader":[]}