From 9acc3d1262fb9a6361c3d0a5c12cd8d4769ecfd1 Mon Sep 17 00:00:00 2001 From: DumpySquare Date: Thu, 24 Oct 2024 17:11:08 -0700 Subject: [PATCH 1/4] nov 2024 work --- CHANGELOG.md | 12 ++ diagnostics.json | 30 +-- f5_flipper_test.tgz | Bin 10671 -> 11039 bytes fixes_enhancements_oct24.conf | 209 ++++++++++++++++++++ package.json | 17 +- src/digLbVserver.ts | 272 +++++++++++++++++++++++++-- src/extension.ts | 40 +++- src/fastWebView.ts | 6 +- src/fastWebViewFull.ts | 124 +----------- src/models.ts | 5 +- src/ns2FastParams.ts | 28 +-- src/nsCfgViewProvider.ts | 8 +- src/regex.ts | 9 +- templates/as3/HTTP.yaml | 24 +-- tests/011_tgz_unpacker.unit.tests.ts | 37 ++-- tests/024_service.unit.tests.ts | 26 ++- tests/archiveBuilder.ts | 8 +- tests/artifacts/apps/apple.ns.conf | 29 ++- tests/artifacts/f5_flipper_test.tgz | Bin 10671 -> 11039 bytes tsconfig.json | 2 +- 20 files changed, 671 insertions(+), 215 deletions(-) create mode 100644 fixes_enhancements_oct24.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index 544a404..4d81fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,18 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ### Fixed +--- + +## [1.11.0] - (10-24-2024) + +### Fixed + +- [RFE] Add another iteration for monitors and ssl settings #39 + - monitors, ssl and pool members can be applied at the "service" level + - Most of the time they are applied at the "service group" +- [RFE] Processing vservers using IPv6 addresses #43 +- [RFE] ns json output to main work flow #45 + --- ## [1.10.1] - (05-19-2024) diff --git a/diagnostics.json b/diagnostics.json index 92ad403..d7e1fd8 100644 --- a/diagnostics.json +++ b/diagnostics.json @@ -29,45 +29,45 @@ }, { "code": "a2ea", - "severity": "Error", - "title": "App with match across services", - "message": "XC cannot match across services, like ftp, control on tcp/21, data on tcp/22", - "regex": "match-across-(pools|services|virtuals) enabled" + "severity": "Warning", + "title": "Content Policy detected", + "message": "NS Content Policies inspect and manipulate traffic based on application layer options like headers/URL. Convert to F5 iRule/Traffic Policies", + "regex": " -policyName " }, { "code": "6a1e", "severity": "Warning", - "title": "Static NAT not supported", + "title": "XC-Static NAT not supported", "message": "XC can nat, but uses a range of sources -- refine the regex ", "regex": "snat" }, { "code": "2245", "severity": "Warning", - "title": "snat pool object detected", + "title": "XC-snat pool object detected", "message": "NATs are supported, but not statics", "regex": "ltm snatpool" }, { "code": "3a67", "severity": "Error", - "title": "Supporting APM profile detected", - "message": "While not a direct APM profile, these profiles on the VS indicate that an APM profile is applied", - "regex": "(/Common/websso { }|/Common/rba { })" + "title": "", + "message": "", + "regex": "" }, { "code": "4987", "severity": "Error", - "title": "no classmatch allowed in iRule", - "message": "not really XC but will cause problems", - "regex": "classmatch" + "title": "", + "message": "", + "regex": "" }, { "code": "5a5a", "severity": "Error", - "title": "iRule stream profile detected", - "message": "F5 TMOS stream profile functionality not supported", - "regex": "stream::" + "title": "", + "message": "", + "regex": "" }, { "code": "0780", diff --git a/f5_flipper_test.tgz b/f5_flipper_test.tgz index 9af3b6fac43934d0760d49cece0f051a1e146d8c..7279f893a437b550b71cb3b7656f7848dd79d513 100644 GIT binary patch literal 11039 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 Z;&0El=iBq``S#G~{{z>zebfLF0RXKA)5rh- literal 10671 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 V-=1&Jx98vB`9IGN8jJu60RWS*1Ec@| diff --git a/fixes_enhancements_oct24.conf b/fixes_enhancements_oct24.conf new file mode 100644 index 0000000..daafb3f --- /dev/null +++ b/fixes_enhancements_oct24.conf @@ -0,0 +1,209 @@ + + + + + +### student-qat.uwo.ca--443 ########## - Hover for more details - ########## + +# not capturing the action from the service policy + + +add lb vserver student-qat.uwo.ca--443 SSL 172.31.25.65 443 -persistenceType COOKIEINSERT -timeout 1201 -lbMethod ROUNDROBIN -redirectURL "http://www.uwo.ca/western/PeopleSoft/HEOutage/studentStaticQAT.html" -cltTimeout 1201 -appflowLog DISABLED -lbprofilename "SameSite=None Override" -devno 62029824 + +bind ssl vserver student-qat.uwo.ca--443 -cipherName UWO-default +bind ssl vserver student-qat.uwo.ca--443 -certkeyName student-qat_wisg_uwo_ca_v1 +add ssl certKey student-qat_wisg_uwo_ca_v1 -cert student-qat_wisg_uwo_ca_v1.crt -key student-qat_wisg_uwo_ca_v1.key f94b16649a6ec398a4fb8ba18e5b78f65f16b7b4deb7a2ec7a1076f6195270f37cd533656c9d672e7cfdaa375788ec34 -encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2022_09_28_20_29_18 -expiryMonitor DISABLED +bind ssl vserver student-qat.uwo.ca--443 -certkeyName Sectigo_intermediate -CA -ocspCheck Optional +add ssl certKey Sectigo_intermediate -cert SectigoRSAOrganizationValidationSecureServerCA-int.der -inform DER -expiryMonitor DISABLED +bind ssl vserver student-qat.uwo.ca--443 -certkeyName Sectigo-Intermediate-2-usr-trust -CA -ocspCheck Optional +add ssl certKey Sectigo-Intermediate-2-usr-trust -cert "USERTrust RSA Certification Authority.pem" -expiryMonitor DISABLED +bind ssl vserver student-qat.uwo.ca--443 -certkeyName Sectigo-Root-AAA-cert -CA -ocspCheck Optional +add ssl certKey Sectigo-Root-AAA-cert -cert "AAA Certificate Services.pem" -expiryMonitor DISABLED +bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_256 +bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_384 +bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_224 +bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_521 + +bind lb vserver student-qat.uwo.ca--443 student-qat--weber02--7035 +add service student-qat--weber02--7035 weber02.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -state DISABLED -appflowLog DISABLED -devno 38666240 +add server weber02.its.uwo.pri 172.29.52.50 -state DISABLED -comment NOC-9852 -devno 16867 + +bind lb vserver student-qat.uwo.ca--443 student-qat--weber03--7035 +add service student-qat--weber03--7035 weber03.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -state DISABLED -appflowLog DISABLED -devno 38371328 +add server weber03.its.uwo.pri 172.29.52.51 -state DISABLED -comment NOC-9852 -devno 16868 + +bind lb vserver student-qat.uwo.ca--443 student-qat--nweber04--7035 +add service student-qat--nweber04--7035 nweber04.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -appflowLog DISABLED -devno 899219456 +add server nweber04.its.uwo.pri 172.31.33.172 -comment NI-43936 -devno 22202 + +bind lb vserver student-qat.uwo.ca--443 student-qat--nweber05--7035 +add service student-qat--nweber05--7035 nweber05.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -appflowLog DISABLED -devno 899186688 +add server nweber05.its.uwo.pri 172.31.33.173 -comment NI-43936 -devno 22203 + + +bind lb vserver student-qat.uwo.ca--443 -policyName rw_add_samesite_cookie_info -priority 100 -gotoPriorityExpression END -type RESPONSE + +add rewrite policy rw_add_samesite_cookie_info "http.RES.HEADER(\"Set-Cookie\").EXISTS" rw_ac_samesite_insert + + + + +-------------------------------------------------- + +### finance.uwo.ca--49000 ########## - Hover for more details - ########## + +# good example of multiple services. not following the -backupVServer + +add lb vserver finance.uwo.ca--49000 TCP 129.100.0.40 49000 -persistenceType NONE -cltTimeout 301 -backupVServer finance.uwo.ca--49000-failover01 -appflowLog DISABLED -devno 59146240 +bind lb vserver finance.uwo.ca--49000-failover01 finance--austin02--49000 +add service finance--austin02--49000 austin02.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 36503552 +add server austin02.its.uwo.pri 172.31.30.45 -comment NOC-8872 -devno 16854 +bind lb vserver finance.uwo.ca--49000 finance--austin01--49000 +add service finance--austin01--49000 austin01.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 36372480 +add server austin01.its.uwo.pri 172.31.30.44 -comment NOC-8872 -devno 16853 +bind lb vserver finance.uwo.ca--49000-failover02 finance--austin03--49000 +add service finance--austin03--49000 austin03.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 37158912 +add server austin03.its.uwo.pri 172.31.30.46 -comment NOC-8872 -devno 16855 +bind lb vserver finance.uwo.ca--49000-failover03 finance--austin04--49000 +add service finance--austin04--49000 austin04.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 37289984 +add server austin04.its.uwo.pri 172.31.30.47 -comment NOC-8872 -devno 16856 + + + +--------------------------------------------------------- +### recruit-dev.wisg.uwo.ca--443 ########## - Hover for more details - ########## + +# not following -policyName reference + +add lb vserver recruit-dev.wisg.uwo.ca--443 SSL 172.31.25.121 443 -persistenceType COOKIEINSERT -timeout 0 -cltTimeout 1201 -comment NOC-17839 -redirectFromPort 80 -httpsRedirectUrl "https://recruit-dev.wisg.uwo.ca" -devno 76087296 +bind lb vserver recruit-dev.wisg.uwo.ca--443 recruit-dev--7057 +add serviceGroup recruit-dev--7057 HTTP -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -comment NOC-17839 -devno 55607296 +bind serviceGroup recruit-dev--7057 watson03.its.uwo.pri 7057 -devno 120225792 +add server watson03.its.uwo.pri 172.29.52.39 -comment NOC-8761 -devno 16846 +bind serviceGroup recruit-dev--7057 watson02.its.uwo.pri 7057 -devno 120193024 +add server watson02.its.uwo.pri 172.29.52.38 -comment NOC-8761 -devno 16845 +bind serviceGroup recruit-dev--7057 -monitorName recruit-dev-index-check -devno 120258560 +add lb monitor recruit-dev-index-check HTTP -respCode 200 -httpRequest "HEAD /index.html" -LRTM ENABLED -interval 20 -resptimeout 5 -devno 21436 +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -cipherName UWO-default +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName wc_recruit-dev_wisg_uwo_ca_v1 +add ssl certKey wc_recruit-dev_wisg_uwo_ca_v1 -cert "/nsconfig/ssl/wc_recruit-dev_wisg_uwo_ca_v1_cert.pem" -key "/nsconfig/ssl/wc_recruit-dev_wisg_uwo_ca_v1_key.pem" 36d3f278db3556c7c578fe9e1a7d391997849f3838d584d6133a200481816c414df22e41fda677b4c79baedf90ed7ed0 -encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2022_09_28_20_29_18 -expiryMonitor DISABLED +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName Sectigo_intermediate -CA -ocspCheck Optional +add ssl certKey Sectigo_intermediate -cert SectigoRSAOrganizationValidationSecureServerCA-int.der -inform DER -expiryMonitor DISABLED +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName Sectigo-Intermediate-2-usr-trust -CA -ocspCheck Optional +add ssl certKey Sectigo-Intermediate-2-usr-trust -cert "USERTrust RSA Certification Authority.pem" -expiryMonitor DISABLED +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName Sectigo-Root-AAA-cert -CA -ocspCheck Optional +add ssl certKey Sectigo-Root-AAA-cert -cert "AAA Certificate Services.pem" -expiryMonitor DISABLED +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_256 +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_384 +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_224 +bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_521 +bind lb vserver recruit-dev.wisg.uwo.ca--443 -policyName res_invite.recruit-dev -priority 100 -gotoPriorityExpression END -type REQUEST + + + + +### westernworldwide.uwo.ca--443 ########## - Hover for more details - ########## + +# rule for -redirect reference + +add lb vserver westernworldwide.uwo.ca--443 SSL 129.100.0.69 443 -persistenceType COOKIEINSERT -timeout 1201 -redirectURL "https://www.uwo.ca/western/PeopleSoft/WIOutage/WIStaticNormal.html" -cltTimeout 1201 -devno 57442304 + +bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis01.its.uwo.pri--7170 +add service westernworldwide--willis01.its.uwo.pri--7170 willis01.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 52101120 +add server willis01.its.uwo.pri 172.29.52.72 -comment NOC-15774 -devno 16911 +bind ssl vserver westernworldwide.uwo.ca--443 -cipherName UWO-default +bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName westernworldwide_uwo_ca_v1 +add ssl certKey westernworldwide_uwo_ca_v1 -cert westernworldwide_uwo_ca_v1.crt -key westernworldwide_uwo_ca_v1.key 55f72a447442a909a275097253767785f2c99acb3fd983f45b49d6c823253821f383ee2126eb52d58825ade97f7640b3 -encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2022_09_28_20_29_18 -expiryMonitor DISABLED +bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName Sectigo_intermediate -CA -ocspCheck Optional +add ssl certKey Sectigo_intermediate -cert SectigoRSAOrganizationValidationSecureServerCA-int.der -inform DER -expiryMonitor DISABLED +bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName Sectigo-Intermediate-2-usr-trust -CA -ocspCheck Optional +add ssl certKey Sectigo-Intermediate-2-usr-trust -cert "USERTrust RSA Certification Authority.pem" -expiryMonitor DISABLED +bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName Sectigo-Root-AAA-cert -CA -ocspCheck Optional +add ssl certKey Sectigo-Root-AAA-cert -cert "AAA Certificate Services.pem" -expiryMonitor DISABLED +bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_256 +bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_384 +bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_224 +bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_521 +bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis02.its.uwo.pri--7170 +add service westernworldwide--willis02.its.uwo.pri--7170 willis02.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 52133888 +add server willis02.its.uwo.pri 172.29.52.73 -comment NOC-15774 -devno 16912 +bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis03.its.uwo.pri--7170 +add service westernworldwide--willis03.its.uwo.pri--7170 willis03.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 54198272 +add server willis03.its.uwo.pri 172.29.52.74 -comment NOC-15774 -devno 16907 +bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis04.its.uwo.pri--7170 +add service westernworldwide--willis04.its.uwo.pri--7170 willis04.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 54231040 +add server willis04.its.uwo.pri 172.29.52.75 -comment NOC-15774 -devno 16908 + + { + "code": "4818", + "severity": "Warning", + "title": "vServer redirect detected", + "message": "this is typically handed on f5 with an irule to detect number of pool member avaialable and redirect as needed", + "regex": "-redirectURL" + }, + + + + + + +-------------------------------------------- + +### groot-cs-redirect ########## - Hover for more details - ########## + +# json output not working. bug in map somewhere + +add cs vserver groot-cs-redirect HTTP 192.168.10.65 80 -cltTimeout 180 -persistenceType NONE -devno 54722560 +bind cs vserver groot-cs-redirect -policyName groot-i-cs-policy -priority 100 -devno 112005 +bind cs vserver groot-cs-redirect -policyName groot-am-cs-policy -priority 110 -devno 112005 +bind cs vserver groot-cs-redirect -policyName groot-yes-cs-policy -priority 120 -devno 112005 +bind cs vserver groot-cs-redirect -policyName groot-groot-cs-policy -priority 130 -devno 112005 +add cs policy groot-i-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"grooti\")" -action groot-i-cs-action +add cs action groot-i-cs-action -targetLBVserver groot-i-lb-vsvr +add cs policy groot-am-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"grootam\")" -action groot-am-cs-action +add cs action groot-am-cs-action -targetLBVserver groot-am-lb-vsvr +add cs policy groot-yes-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"grootyes\")" -action groot-yes-cs-action +add cs action groot-yes-cs-action -targetLBVserver groot-yes-lb-vsvr +add cs policy groot-groot-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"groot\")" -action groot-groot-cs-action +add cs action groot-groot-cs-action -targetLBVserver groot-groot-lb-vsvr +add lb vserver groot-i-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50823168 +bind lb vserver groot-i-lb-vsvr groot-i-svc-grp +add serviceGroup groot-i-svc-grp SSL -maxClient 1000 -maxReq 0 -cip ENABLED X-Client-IP -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47382528 +bind serviceGroup groot-i-svc-grp sprout134A-grooti 443 -devno 71041024 +add server sprout134A-grooti 192.168.160.138 -devno 108843 +bind serviceGroup groot-i-svc-grp sprout134B-grooti 443 -devno 71073792 +add server sprout134B-grooti 192.168.160.140 -devno 108844 +bind serviceGroup groot-i-svc-grp -monitorName ping -devno 71106560 +bind ssl vserver groot-i-lb-vsvr -certkeyName star.groot.cer +add ssl certKey star.groot.cer -cert www.star.groot_2022.pfx -key www.star.groot_2022.pfx -inform PFX -passcrypt XXXX -encrypted -encryptmethod ENCMTHD_3 +bind ssl vserver groot-i-lb-vsvr -eccCurveName P_256 +bind ssl vserver groot-i-lb-vsvr -eccCurveName P_384 +bind ssl vserver groot-i-lb-vsvr -eccCurveName P_224 +bind ssl vserver groot-i-lb-vsvr -eccCurveName P_521 +add lb vserver groot-am-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50855936 +bind lb vserver groot-am-lb-vsvr groot-am-svc-grp +add serviceGroup groot-am-svc-grp SSL -maxClient 1000 -maxReq 0 -cip ENABLED X-Client-IP -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47415296 +bind serviceGroup groot-am-svc-grp sprout134A_grootam 443 -devno 71139328 +add server sprout134A_grootam 192.168.160.118 -devno 108845 +bind serviceGroup groot-am-svc-grp sprout134B_grootam 443 -devno 71172096 +add server sprout134B_grootam 192.168.160.119 -devno 108846 +bind ssl vserver groot-am-lb-vsvr -certkeyName star.groot.cer +bind ssl vserver groot-am-lb-vsvr -eccCurveName P_256 +bind ssl vserver groot-am-lb-vsvr -eccCurveName P_384 +bind ssl vserver groot-am-lb-vsvr -eccCurveName P_224 +bind ssl vserver groot-am-lb-vsvr -eccCurveName P_521 +add lb vserver groot-yes-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50921472 +bind lb vserver groot-yes-lb-vsvr groot-yes-svc-grp +add serviceGroup groot-yes-svc-grp SSL -maxClient 1000 -maxReq 0 -cip ENABLED X-Client-IP -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47448064 +bind ssl vserver groot-yes-lb-vsvr -certkeyName star.groot.cer +bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_256 +bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_384 +bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_224 +bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_521 +add lb vserver groot-groot-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50888704 +bind lb vserver groot-groot-lb-vsvr groot-svc-grp +bind ssl vserver groot-groot-lb-vsvr -certkeyName star.groot.cer +bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_256 +bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_384 +bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_224 +bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_521 \ No newline at end of file diff --git a/package.json b/package.json index 7231cb5..864a835 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "F5 Flipper", "description": "Breaking down Citrix NetScaler ADC configs", "publisher": "F5DevCentral", - "version": "1.10.1", + "version": "1.11.0", "keywords": [ "F5", "F5Networks", @@ -121,11 +121,16 @@ "enablement": "view == nsTemplatesView" }, { - "command": "f5-flipper.viewJson", - "title": "JSON Output", + "command": "f5-flipper.viewNsLines", + "title": "View NS App Lines", "category": "F5-Flipper", - "icon": "$(json)", - "enablement": "view == nsConfigView" + "icon": "$(output)" + }, + { + "command": "f5-flipper.viewNsJson", + "title": "View NS App Lines", + "category": "F5-Flipper", + "icon": "$(json)" }, { "command": "f5-flipper.afton", @@ -171,7 +176,7 @@ "group": "inline" }, { - "command": "f5-flipper.viewJson", + "command": "f5-flipper.viewNsLines", "when": "view == nsConfigView && viewItem =~ /(nsApp|nsGSLB)/", "group": "inline" }, diff --git a/src/digLbVserver.ts b/src/digLbVserver.ts index d8baa62..0166a0a 100644 --- a/src/digLbVserver.ts +++ b/src/digLbVserver.ts @@ -58,18 +58,29 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { // app.name = app.name.replace(/"/g, '') - if (app.name === '"1 APPLE_443_HTTPS"') { + // if (app.name === '"1 APPLE_443_HTTPS"') { + + // debugger; + // const x = RegExp(/\w+|"[\w\s]*"/); + // const y = x.test(app.name) + // const v = app.name.split(/\w+|"[\w\s]*"/gm) + // const z = y; + // } + + // function nameFilter() + - // debugger; - const x = RegExp(/\w+|"[\w\s]*"/); - const y = x.test(app.name) - const v = app.name.split(/\w+|"[\w\s]*"/gm) - const z = y; - } // start with 'bind lb vserver' // todo: update this filter to accomodate names with spaces, see above; - const bindLbVservers = coa.bind?.lb?.vserver?.filter(el => el.startsWith(app.name)); + const bindLbVservers = coa.bind?.lb?.vserver?.filter(el => { + const name = app.name; + // pull out the app name from the binding + const bindlbname = el.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === bindlbname; + return y; + }); for await (const x of bindLbVservers) { const parent = 'bind lb vserver'; const originalString = parent + ' ' + x; @@ -81,22 +92,31 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } - // todo: need to see if this references a "add service" or "add serviceGroup" + // this references a "add service" or "add serviceGroup" if (!app.bindings) app.bindings = {}; if (rxMatch.groups?.service) { const serviceName = rxMatch.groups?.service; + // dig "add service with supporting bind service lines" + // dig service details -> do we have a service with this name? // there should only be one "add service" with this name since we are looking in this specific "bind lb vserver" - const serviceD = coa.add?.service?.filter(s => s.startsWith(serviceName))[0]; - if (serviceD) { - // this should only ever find one + const serviceD = coa.add?.service?.filter(s => { + const name = serviceName; + const addServices = coa.add.service; + // pull out the app name from the binding + const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === sname; + return y; + }); + for await (const x of serviceD) { const parent = 'add service'; const originalString = parent + ' ' + serviceD; app.lines.push(originalString); - const rxMatch = serviceD.match(rx.parents[parent]) + const rxMatch = x.match(rx.parents[parent]) const opts = parseNsOptions(rxMatch.groups?.opts, rx); if (!rxMatch) { /* istanbul ignore next */ @@ -116,6 +136,15 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { opts } + // todo: is this where we should dig the service binding options for -monitor references? + + // dig "bind service ..." + await digBindService(serviceName, app, coa, rx) + + + // dig "bind ssl service ..." + await digBindSslService(serviceName, app, coa, rx) + // also get server reference under 'add server ' if (rxMatch.groups.server) { @@ -158,6 +187,101 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { } +/** + * dig details for 'bind service ...' + * @param serviceName + * @param app + * @param obj + * @param rx + * @returns + */ +export async function digBindService(serviceName: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { + + + // get all the services with matching name + const bindServices = obj.bind?.service?.filter(s => { + const bindServicesList = obj.bind.service; + + const sName = s.split(' ')[0] === serviceName + + return sName; + }) + + // loop through services and dig additional details + for await (const x of bindServices) { + const parent = 'bind service'; + const originalString = parent + ' ' + x; + const rxMatch = x.match(rx.parents[parent]) + + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + } + + app.lines.push(originalString); + + if (rxMatch.groups.serv) { + + + const memberRef = rxMatch.groups.serv.split(' '); + + // dig server from serviceGroup server reference + await digServer(memberRef[0], app, obj, rx) + .then(i => { + + const serverDetails = { + name: memberRef[0], + port: memberRef[1] + } + // if (!serviceGroup.servers) serviceGroup.servers = [] + + // serviceGroup.servers.push(Object.assign(serverDetails, i)) + }) + + + } else if (rxMatch.groups.monitor) { + + const monitorName = rxMatch.groups.monitor.split(' ').pop(); + + // add the object param/array if not already there + // if (!serviceGroup.monitors) serviceGroup.monitors = []; + + //todo: get a list of the default monitor names and add them to the config somehow + + // create the serviceGroup monitor object with the name + const monitorObj = { + name: monitorName + }; + + // get monitor config line + obj.add?.lb?.monitor?.filter(m => m.split(' ')[0] === monitorName) + .forEach(x => { + const parent = 'add lb monitor'; + const originalString = parent + ' ' + x; + app.lines.push(originalString) + const rxMatch = x.match(rx.parents[parent]) + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + } + const opts = parseNsOptions(rxMatch.groups.opts, rx); + + // add any monitor object options + deepmergeInto(monitorObj, opts) + }) + + // push the full monitor object to the serviceGroup + // serviceGroup.monitors.push(monitorObj); + + } else if (rxMatch.groups.opts) { + deepmergeInto( + // serviceGroup, + parseNsOptions(rxMatch.groups.opts, rx) + ) + } + } +} + /** * @@ -188,6 +312,65 @@ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: } } + + +/** + * + * @param serviceName service name from "bind lb vserver" + * @param app + * @param obj + * @param rx + */ +export async function digService(serviceName: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { + + + // this should be a single service name + // retrieve the single service "add service ..." + // for each add service, dig all the "add server ..." referenced by the "add service ..." + // get all the supporting "bind service ..." + + // we have the service name from the "bind lb vserver" + // filter out the services (single) we need + // + + const serviceD = obj.add?.service?.filter(s => { + const name = serviceName; + // pull out the app name from the binding + const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === sname; + return y; + })[0]; + + if (serviceD) { + const parent = 'add service'; + const originalString = parent + ' ' + serviceD; + app.lines.push(originalString); + const rxMatch = serviceD.match(rx.parents[parent]) + const opts = parseNsOptions(rxMatch.groups?.opts, rx); + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + } + + // if we have service details create array to put them + if (!app.bindings.service) { + app.bindings.service = []; + } + + let serviceDetails = { + name: serviceName, + protocol: rxMatch.groups.protocol, + port: rxMatch.groups.port, + server: rxMatch.groups.server, + opts + } + } +} + + + + /** * * @param name serviceGroup name from 'bind lb vserver' @@ -338,7 +521,12 @@ export async function digServer(serverName: string, app: AdcApp, obj: AdcConfObj } - +/** + * digs for matches of "bind ssl vserver ..." + * @param app + * @param obj + * @param rx + */ export function digSslBinding(app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { const sslBindObj = [] @@ -382,6 +570,62 @@ export function digSslBinding(app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { } } + if (sslBindObj.length > 0) { + app.bindings.certs = sslBindObj; + } +} + + + +/** + * digs for matches of "bind ssl vserver ..." + * @param app + * @param obj + * @param rx + */ +export function digBindSslService(serviceName: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { + + const sslBindObj = [] + + const appName = app.name; + + // todo: update filter to accomodate spaces in name + const appBindSslServices = obj.bind?.ssl?.service?.filter(s => s.startsWith(serviceName)) + + // check ssl bindings + if (appBindSslServices?.length > 0) { + for (const x of appBindSslServices) { + + + const parent = 'bind ssl service'; + const originalString = parent + ' ' + x; + app.lines.push(originalString); + const rxMatch = x.match(rx.parents[parent]); + + const opts = parseNsOptions(rxMatch.groups.opts, rx) + // only parse certkeyName details, pass on all the ciphers and eccCurveNames + if (opts['-certkeyName']) { + const certKeyName = opts['-certkeyName'] + + for (const el of obj.add?.ssl?.certKey) { + + if (el.startsWith(certKeyName)) { + const parent = 'add ssl certKey'; + const originalString = parent + ' ' + el; + app.lines.push(originalString); + const rxMatch = el.match(rx.parents[parent]); + const opts2 = parseNsOptions(rxMatch.groups.opts, rx) + opts2.profileName = certKeyName; + sslBindObj.push(opts2) + } + } + } + + // not parsing any "set ssl vserver" details at this time + // probably not going to so the migration can be more of a refactor and utilize updated ssl settings, which are most important + } + } + if (sslBindObj.length > 0) { app.bindings.certs = sslBindObj; } diff --git a/src/extension.ts b/src/extension.ts index 8a87bad..10ca23e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,6 +22,7 @@ import { Hovers } from './hovers'; import { NsCodeLensProvider } from './codeLens'; import { FastCore } from './fastCore'; import { NsTemplateProvider } from './templateViewProvider'; +import { isArray } from 'f5-conx-core'; ext.logger = logger; @@ -131,8 +132,17 @@ export async function activateInternal(context: ExtensionContext) { // this is an unsaved file in vscode // one spot to setup direct text to engine + const editors = window.activeTextEditor;; + const editorText = editors.document.getText(); + // save the text the a local temp file + + // then return the filePath of the temp file + filePath = '' + + window.showErrorMessage('f5-flipper.cfgExplore -> save file first: feature in development'); + return logger.error('f5-flipper.cfgExplore -> save file first: feature in development'); } else if (item?.path) { @@ -207,9 +217,9 @@ export async function activateInternal(context: ExtensionContext) { })); - context.subscriptions.push(commands.registerCommand('f5-flipper.viewJson', async (x) => { - ext.telemetry.capture({ command: 'f5-flipper.viewJson' }); - const appName = x.label; + context.subscriptions.push(commands.registerCommand('f5-flipper.viewNsJson', async (x) => { + ext.telemetry.capture({ command: 'f5-flipper.viewNsJson' }); + const appName = x.label || x.name; const app = ext.nsCfgProvider.explosion.config.apps.find(a => a.name === appName) @@ -251,9 +261,29 @@ export async function activateInternal(context: ExtensionContext) { })); - context.subscriptions.push(commands.registerCommand('f5-flipper.render', async (text) => { + context.subscriptions.push(commands.registerCommand('f5-flipper.viewNsLines', async (x) => { + ext.telemetry.capture({ command: 'f5-flipper.viewNsLines' }); + + if(typeof x === "string") { - ext.nsCfgProvider.render(text, 'lines'); + // single ns app as text string > from view app item + ext.nsCfgProvider.render(x, 'lines'); + } else if ( isArray(x) ) { + + // "Sources header item" should be an array of the ns configs + // render function will collapse arrays + ext.nsCfgProvider.render(x, 'lines'); + + } else { + + // got an app view item reference or app ns def + // lookup the app and return the details as needed + const appName = x.label || x.name; + + const app = ext.nsCfgProvider.explosion.config.apps.find(a => a.name === appName) + + ext.nsCfgProvider.render(x, 'lines'); + } })); diff --git a/src/fastWebView.ts b/src/fastWebView.ts index cb59806..694fe8b 100644 --- a/src/fastWebView.ts +++ b/src/fastWebView.ts @@ -18,8 +18,8 @@ import { ext } from './extensionVariables'; import { logger } from './logger'; import fast from '@f5devcentral/f5-fast-core'; import path from 'path'; -import { AdcApp, NsFastTempParams } from './models'; -import { mungeNS2FAST } from './ns2FastParams'; +import { AdcApp } from './models'; + const fast = require('@f5devcentral/f5-fast-core'); @@ -138,7 +138,7 @@ export class FastWebView { // mutate ns app params into a better format for FAST templates const temp = app.fastTempParams; - // merge with FAST template default params + // merge with FAST template default params (overwriting default) const fastParams = Object.assign(defaultParams, temp) logger.debug(`ns app ${app.name} FAST Template params: `, fastParams); diff --git a/src/fastWebViewFull.ts b/src/fastWebViewFull.ts index 0e490f0..0234fbb 100644 --- a/src/fastWebViewFull.ts +++ b/src/fastWebViewFull.ts @@ -19,6 +19,7 @@ import { logger } from './logger'; import fast from '@f5devcentral/f5-fast-core'; import path from 'path'; import { AdcApp, NsFastTempParams } from './models'; +import { mungeNS2FAST } from './ns2FastParams'; const fast = require('@f5devcentral/f5-fast-core'); @@ -98,125 +99,6 @@ export class FastWebView { } - /** - * mutate ns app json to a form easier for FAST/mustache to work with - * @param nsApp NS app as json - */ - mungeNS2FAST(nsApp: AdcApp) { - - if (nsApp.fastTempParams) { - - // if we already have the munged params, send those since they could have been modified by the user - return nsApp.fastTempParams - - } else { - - // map the ns app params to the fast template params - const nsFastJson: NsFastTempParams = { - tenant_name: nsApp.name, - app_name: nsApp.name, - type: nsApp.type, - protocol: nsApp.protocol, - virtual_address: nsApp.ipAddress, - virtual_port: nsApp.port, - pool_members: [] - }; - - if (nsApp?.opts?.['-persistenceType']) { - const persistType = nsApp.opts['-persistenceType'] as string; - nsFastJson.persistence = { [persistType]: persistType } - } - - if (nsApp?.opts?.['-lbMethod']) { - const lbMethod = nsApp.opts['-lbMethod'] as string; - nsFastJson.lbMethod = { [lbMethod]: lbMethod } - } - - if (nsApp?.opts?.['-cltTimeout']) { - const cltTimeout = nsApp.opts['-cltTimeout'] as string; - nsFastJson.cltTimeout = { [cltTimeout]: cltTimeout } - } - - if (nsApp?.opts?.['-timeout']) { - const timeout = nsApp.opts['-timeout'] as string; - nsFastJson.timeout = { [timeout]: timeout } - } - - if (nsApp?.opts?.['-redirectURL']) { - const redirectURL = nsApp.opts['-redirectURL'] as string; - nsFastJson.redirectURL = { redirectURL } - } - - if (nsApp?.opts?.['-backupVServer']) { - const backupVServer = nsApp.opts['-backupVServer'] as string; - nsFastJson.backupVServer = { [backupVServer]: backupVServer } - } - - if (nsApp?.opts?.['-tcpProfileName']) { - const tcpProfileName = nsApp.opts['-tcpProfileName'] as string; - nsFastJson.tcpProfileName = { [tcpProfileName]: tcpProfileName } - } - - - // capture all the service bindings (similar to f5 nodes) - if (nsApp.bindings?.service) { - - // loop through service bindings to populate pool members - for (const service of nsApp.bindings.service) { - // @ts-expect-error - nsFastJson.pool_members.push(service); - } - } - - // capture all the serviceGroup bindings (more like f5 pool + members) - if (nsApp.bindings?.serviceGroup && nsApp.bindings.serviceGroup.length > 0) { - - if (nsApp.bindings.serviceGroup[0]?.servers) - - // todo: extend this to loop through service group for all servers - for (const servers of nsApp.bindings.serviceGroup[0].servers) { - // @ts-expect-error - nsFastJson.pool_members.push(servers); - }; - } - - if (nsFastJson.pool_members) { - - // remap pool member details to make it easier for FAST to key off details - nsFastJson.pool_members = nsFastJson.pool_members.map(poolMember => { - - // create new object - const tempMemberObj: any = {}; - - if (poolMember.hostname) { - const hsTemp = poolMember.hostname; - tempMemberObj.hostname = { - hostname: hsTemp - } - } - - if (poolMember.address) { - tempMemberObj.address = { address: poolMember.address } - } - - if (poolMember.name) tempMemberObj.name = { name: poolMember.name } - if (poolMember.port) tempMemberObj.port = { port: poolMember.port } - - // overwrite the new member details - // we do this to leave behind all the other "-opts" that aren't strickly necessary for FAST templates - // if they are needed, they should be added to get mapped here or else they show in the HTML output - return tempMemberObj; - }) - } - - // if no pool members, remove empty array - if (nsFastJson.pool_members.length === 0) delete nsFastJson.pool_members; - - // return the new params - return nsFastJson; - - } - } public async autoRenderHTML(app: AdcApp) { @@ -245,7 +127,7 @@ export class FastWebView { // const nsAppParams = JSON.parse(doc.getText()); // mutate ns app params into a better format for FAST templates - const temp = this.mungeNS2FAST(app); + const temp = mungeNS2FAST(app); // merge with FAST template default params const fastParams = Object.assign(defaultParams, temp) @@ -349,7 +231,7 @@ export class FastWebView { // const nsAppParams = JSON.parse(doc.getText()); // mutate ns app params into a better format for FAST templates - const temp = this.mungeNS2FAST(app); + const temp = mungeNS2FAST(app); // merge with FAST template default params const fastParams = Object.assign(defaultParams, temp) diff --git a/src/models.ts b/src/models.ts index 15a8022..1dacea7 100644 --- a/src/models.ts +++ b/src/models.ts @@ -45,7 +45,7 @@ export type NsFastTempParams = { virtual_port: string; persistence?: { [key: string]: string }; lbMethod?: { [key: string]: string }; - cltTimeout?: { [key: string]: string }; + idleTimeout?: { [key: string]: string } | string; timeout?: { [key: string]: string }; redirectURL?: { [key: string]: string }; backupVServer?: { [key: string]: string }; @@ -252,6 +252,7 @@ export type AdcRegExTree = { 'bind serviceGroup': RegExp; 'bind lb vserver': RegExp; 'bind cs vserver': RegExp; + 'bind ssl service': RegExp; 'bind ssl vserver': RegExp; 'bind gslb vserver': RegExp; } @@ -344,10 +345,12 @@ export type AdcConfObj = { gslb?: { vserver?: string[]; } + service?: string[] serviceGroup?: string[]; audit?: string; tunnel?: string; ssl?: { + service?: string[]; vserver?: string[]; }; }; diff --git a/src/ns2FastParams.ts b/src/ns2FastParams.ts index 4e940b3..610efbb 100644 --- a/src/ns2FastParams.ts +++ b/src/ns2FastParams.ts @@ -8,13 +8,11 @@ import { } from "./models"; - -export function - /** - * mutate ns app json to a form easier for FAST/mustache to work with - * @param nsApp NS app as json - */ - mungeNS2FAST(nsApp: AdcApp) { +/** + * mutate ns app json to a form easier for FAST/mustache to work with + * @param nsApp NS app as json + */ +export function mungeNS2FAST(nsApp: AdcApp) { if (nsApp.fastTempParams) { @@ -31,7 +29,8 @@ export function protocol: nsApp.protocol, virtual_address: nsApp.ipAddress, virtual_port: nsApp.port === '*' ? '0' : nsApp.port, - pool_members: [] + pool_members: [], + monitors: [] }; if (nsApp?.opts?.['-persistenceType']) { @@ -46,7 +45,7 @@ export function if (nsApp?.opts?.['-cltTimeout']) { const cltTimeout = nsApp.opts['-cltTimeout'] as string; - nsFastJson.cltTimeout = { [cltTimeout]: cltTimeout } + nsFastJson.idleTimeout = cltTimeout; } if (nsApp?.opts?.['-timeout']) { @@ -138,9 +137,6 @@ export function }) } - // if no pool members, remove empty array - if (nsFastJson.pool_members.length === 0) delete nsFastJson.pool_members; - // remap health monitors for fast templates nsFastJson.monitors = nsFastJson?.monitors.map(monitor => { @@ -148,6 +144,14 @@ export function return monitor; }) + + // if no pool members, remove empty array + if (nsFastJson.pool_members.length === 0) delete nsFastJson.pool_members; + + // if no monitors, remove empty array + if (nsFastJson.monitors.length === 0) delete nsFastJson.monitors; + + // return the new params return nsFastJson; diff --git a/src/nsCfgViewProvider.ts b/src/nsCfgViewProvider.ts index 38dcf37..8708ca3 100644 --- a/src/nsCfgViewProvider.ts +++ b/src/nsCfgViewProvider.ts @@ -268,7 +268,7 @@ export class NsCfgProvider implements TreeDataProvider { desc, 'nsApp', icon, TreeItemCollapsibleState.None, { - command: 'f5-flipper.render', + command: 'f5-flipper.viewNsJson', title: '', arguments: [app] } @@ -319,7 +319,7 @@ export class NsCfgProvider implements TreeDataProvider { '', 'nsGSLB', icon, TreeItemCollapsibleState.None, { - command: 'f5-flipper.render', + command: 'f5-flipper.viewNsJson', title: '', arguments: [app] } @@ -400,7 +400,7 @@ export class NsCfgProvider implements TreeDataProvider { `lines: ${source.content.split('\n').length.toString()}`, 'nsFile', '', TreeItemCollapsibleState.None, { - command: 'f5-flipper.render', + command: 'f5-flipper.viewNsLines', title: '', arguments: [source.content] } @@ -512,7 +512,7 @@ export class NsCfgProvider implements TreeDataProvider { this.explosion.config.sources.length.toString(), '', '', TreeItemCollapsibleState.Collapsed, { - command: 'f5-flipper.cfgExplore-show', + command: 'f5-flipper.viewNsLines', title: '', arguments: [allSources] } diff --git a/src/regex.ts b/src/regex.ts index 3839b43..c3256f8 100644 --- a/src/regex.ts +++ b/src/regex.ts @@ -63,13 +63,13 @@ export class RegExTree { 'add ns rpcNode': /(?\S+) (?[\S ]+)/, 'add route': /(?\S+)/, 'add dns nameServer': /(?\S+)/, - 'add lb vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\d.]+) (?(\d+|\*)) (?[\S ]+)/, + 'add lb vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\d\w.:]+) (?(\d+|\*)) (?[\S ]+)/, 'add lb monitor': /(?\S+) (?\S+) (?[\S ]+)/, 'add ssl certKey': /(?\S+) (?[\S ]+)/, 'add server': /(?\S+) (?\S+) ?(?[\S ]+)?/, 'add service': /(?\S+) (?\S+) (?\S+) (?(\d+|\*)) (?[\S ]+)/, 'add serviceGroup': /(?\S+) (?\S+) (?[\S ]+)/, - 'add cs vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\d.]+) (?(\d+|\*)) (?[\S ]+)/, + 'add cs vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\d\w.:]+) (?(\d+|\*)) (?[\S ]+)/, 'add cs action': /(?\S+) (?[\S ]+)/, 'add cs policy': /(?\S+) (?[\S ]+)/, 'add gslb vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\S ]+)/, @@ -86,10 +86,11 @@ export class RegExTree { 'set ns param': /(?[\S ]+)/, 'set ns hostName': /(?[\S ]+)/, 'set gslb vserver': /(?\S+) (?[\S ]+)/, - 'bind service': /(?[\S ]+)/, + 'bind service': /(?("[\S ]+"|[\S]+)) ((?\S+ (\d+|\*))|(?-monitorName \S+)|(?[\S ]+))/, 'bind serviceGroup': /(?("[\S ]+"|[\S]+)) ((?\S+ (\d+|\*))|(?-monitorName \S+)|(?[\S ]+))/, - 'bind lb vserver': /(?("[\S ]+"|[\S]+)) ((?-[\S ]+)|(?[\S]+))/, + 'bind lb vserver': /(?("[\S ]+"|[\S]+)) ((?-[\S ]+)|(?("[\S ]+"|[\S]+)))/, 'bind cs vserver': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, + 'bind ssl service': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, 'bind ssl vserver': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, 'bind gslb vserver': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, } diff --git a/templates/as3/HTTP.yaml b/templates/as3/HTTP.yaml index d358f02..66d9662 100644 --- a/templates/as3/HTTP.yaml +++ b/templates/as3/HTTP.yaml @@ -21,9 +21,9 @@ template: | "translateServerPort": true, "class": "Service_HTTP", {{#persistence}} - {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}],{{/SOURCEIP}} - {{#COOKIEINSERT}}"persistenceMethods": [{"bigip": "/Common/cookie"}],{{/COOKIEINSERT}} - {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} + {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}]{{/SOURCEIP}} + {{#COOKIEINSERT}}"persistenceMethods": [{"bigip": "/Common/cookie"}]{{/COOKIEINSERT}} + {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}]{{/SSLSESSION}} {{#NONE}}"persistenceMethods" : [],{{/NONE}} {{/persistence}} "virtualAddresses": [ @@ -52,15 +52,15 @@ template: | ], "class": "Pool", {{#lbMethod}} - {{#ROUNDROBIN}} - "loadBalancingMode":"round-robin", - {{/ROUNDROBIN}} - {{#LEASTCONNECTION}} - "loadBalancingMode":"least-connections-member", - {{/LEASTCONNECTION}} - {{/lbMethod}} - {{^lbMethod}} - "loadBalancingMode":"least-connections-member", + {{#ROUNDROBIN}} + "loadBalancingMode":"round-robin", + {{/ROUNDROBIN}} + {{#LEASTCONNECTION}} + "loadBalancingMode":"least-connections-member", + {{/LEASTCONNECTION}} + {{#NONE}} + "loadBalancingMode":"least-connections-member", + {{/NONE}} {{/lbMethod}} "monitors": [ "http" diff --git a/tests/011_tgz_unpacker.unit.tests.ts b/tests/011_tgz_unpacker.unit.tests.ts index 626f099..ae064fd 100644 --- a/tests/011_tgz_unpacker.unit.tests.ts +++ b/tests/011_tgz_unpacker.unit.tests.ts @@ -108,6 +108,7 @@ describe('tgz unpacker tests', function () { const resp = adc.loadParseAsync(badFile) + // assert.deepStrictEqual(resp, "x") assert.rejects(resp, 'should reject the promise since the file is not supported/bad') }) @@ -118,9 +119,14 @@ describe('tgz unpacker tests', function () { const badFile = path.join(__dirname, 'artifacts', 'bad1.tgz') - const resp = adc.loadParseAsync(badFile) + const resp = await adc.loadParseAsync(badFile) + .catch(e => { + err = e; + return "x"; + }) - assert.rejects(resp, 'should reject the promise since the file is not supported/bad') + assert.deepStrictEqual(resp, undefined) + // assert.rejects(resp, 'should reject the promise since the file is not supported/bad') }) @@ -154,33 +160,38 @@ describe('tgz unpacker tests', function () { const badFile = path.join(__dirname, 'artifacts', 'noApps.ns.conf') - const resp = adc.loadParseAsync(badFile) + const resp = await adc.loadParseAsync(badFile) .then(async x => { exp = await adc.explode(); const z = x; }) .catch(e => { err = e; - return e; + return "x"; }) - assert.rejects(resp, 'should reject the promise since the file is not found') + assert.deepStrictEqual(resp, "x") + // assert.rejects(resp, 'should reject the promise since the file is not found') }) it(`rejects .conf file with no ns config and no ns version`, async () => { - adc = new ADC(); - const badFile = path.join(__dirname, 'artifacts', 'noAppsNoVersion.ns.conf') + // I'm pretty sure this is no longer valid since it will assume a version if no version is detected - const resp = adc.loadParseAsync(badFile) - .catch(e => { - err = e; - return e; - }) + // adc = new ADC(); - assert.rejects(resp, 'should reject the promise since the file is not found') + // const badFile = path.join(__dirname, 'artifacts', 'noAppsNoVersion.ns.conf') + + // const resp = await adc.loadParseAsync(badFile) + // .catch(e => { + // err = e; + // return "x"; + // }) + + // assert.deepStrictEqual(resp, "x") + // assert.rejects(resp, 'should reject the promise since the file is not found') }) diff --git a/tests/024_service.unit.tests.ts b/tests/024_service.unit.tests.ts index 1345cd0..d78149b 100644 --- a/tests/024_service.unit.tests.ts +++ b/tests/024_service.unit.tests.ts @@ -30,7 +30,7 @@ describe('service abstraction tests', function () { before(async function () { // log test file name - makes it easer for troubleshooting console.log(' file:', __filename) - testFile = await archiveMake() as string; + testFile = await archiveMake('apple.ns.conf') as string; // clear the events arrays parsedFileEvents.length = 0 parsedObjEvents.length = 0 @@ -72,6 +72,30 @@ describe('service abstraction tests', function () { }) + it(`basic service reference non ssl with monitor`, async () => { + + // this app should have three different service bindings and 15 total line of config + + // get application we are looking for + const app = expld.config.apps?.find(x => x.name === "\"2 APPLE_80_HTTP\"") + + assert.deepStrictEqual(app!.bindings!.service!.length, 1, "should have three service bindings") + assert.deepStrictEqual(app!.lines!.length, 6, "should have 16 total lines of ns config") + + }) + + it(`basic service reference ssl`, async () => { + + // this app should have three different service bindings and 15 total line of config + + // get application we are looking for + const app = expld.config.apps?.find(x => x.name === "\"3 APPLE_443_HTTPS\"") + + assert.deepStrictEqual(app!.bindings!.service!.length, 1, "should have one service bindings") + assert.deepStrictEqual(app!.lines!.length, 9, "should have 16 total lines of ns config") + + }) + it(`basic service reference with hostname/fqdn and address`, async () => { diff --git a/tests/archiveBuilder.ts b/tests/archiveBuilder.ts index b7b8681..937b066 100644 --- a/tests/archiveBuilder.ts +++ b/tests/archiveBuilder.ts @@ -22,7 +22,7 @@ import { execSync } from 'child_process'; * @type * @returns */ -export async function archiveMake(): Promise { +export async function archiveMake(file: string = '*'): Promise { const filesInArchive: any[] = []; const baseArchiveName = 'f5_flipper_test'; @@ -32,9 +32,13 @@ export async function archiveMake(): Promise { const baseArchiveDir = path.join(__dirname, 'artifacts', 'apps'); + if(file !== "*") { + return path.join(baseArchiveDir, file) + } + // start building the list of filePaths to include in the archive // config dir should always be in the archive - let filesPaths: string[] = globSync('*', { cwd: baseArchiveDir }) + let filesPaths: string[] = globSync(file, { cwd: baseArchiveDir }) diff --git a/tests/artifacts/apps/apple.ns.conf b/tests/artifacts/apps/apple.ns.conf index d830ab3..093fbc4 100644 --- a/tests/artifacts/apps/apple.ns.conf +++ b/tests/artifacts/apps/apple.ns.conf @@ -17,4 +17,31 @@ add service GALA02_HTTPS_82_SVC SERVERCORE2 SSL 82 -gslb NONE -maxClient 0 -maxR add server SERVERCORE2 10.240.21.170 -comment "created with apple in mind" -devno 171689 bind lb vserver "1 APPLE_443_HTTPS" GALA01_HTTPS_82_SVC add service GALA01_HTTPS_82_SVC SERVERCORE1 SSL 82 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED client-ip -usip NO -useproxyport YES -sp OFF -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP YES -devno 363462656 -add server SERVERCORE1 sevcore1.jonny.dev -devno 171388 \ No newline at end of file +add server SERVERCORE1 sevcore1.jonny.dev -devno 171388 + + + +### "2 APPLE_443_HTTPS" ########## +# Features: 80/http/ services directly on vserver/space in name/monitor on service +add lb vserver "2 APPLE_80_HTTP" SSL 10.240.20.11 80 -persistenceType COOKIEINSERT -timeout 0 -lbMethod ROUNDROBIN -cltTimeout 180 -comment "service with monitor directly attached" -devno 88932352 +bind lb vserver "2 APPLE_80_HTTP" FUJI03_HTTP_SVC_8080 +add service FUJI03_HTTP_SVC_8080 10.240.21.176 HTTP 8080 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED X-Forwarded-For -usip NO -useproxyport YES -sp ON -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP YES -devno 127238144 +add server 10.240.21.176 10.240.21.176 -devno 66095 +bind service FUJI03_HTTP_SVC_8080 -monitorName fugi03_http_monitor -devno 536477696 +add lb monitor fugi03_http_monitor HTTP -respCode 200 -httpRequest "GET /index.html" -LRTM DISABLED + + +### "2 APPLE_443_HTTPS" ########## +# Features: 443/https/ssl/ services directly on vserver/space in name/monitor on service/ipv6 +add lb vserver "3 APPLE_443_HTTPS" SSL 2001:db8:3333:4444:5555:6666:7777:8888 443 -persistenceType COOKIEINSERT -timeout 0 -lbMethod ROUNDROBIN -cltTimeout 180 -comment "service with monitor directly attached" -devno 88932352 +bind lb vserver "3 APPLE_443_HTTPS" FUJI04_HTTPS_SVC_443 +add service FUJI04_HTTPS_SVC_443 fuji04_server1 SSL 443 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED X-Forwarded-For -usip NO -useproxyport YES -sp OFF -cltTimeout 360 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -comment CH22001039470 -devno 204341248 +add server fuji04_server1 2001:db8:0000:0123:4567:89ab:0000:cdef -devno 66898 +bind service FUJI04_HTTPS_SVC_443 -monitorName https-ecv -devno 466419712 +bind ssl service FUJI04_HTTPS_SVC_443 -eccCurveName P_256 +bind ssl service FUJI04_HTTPS_SVC_443 -eccCurveName P_384 +bind ssl service FUJI04_HTTPS_SVC_443 -eccCurveName P_224 +bind ssl service FUJI04_HTTPS_SVC_443 -eccCurveName P_521 + + + diff --git a/tests/artifacts/f5_flipper_test.tgz b/tests/artifacts/f5_flipper_test.tgz index 9af3b6fac43934d0760d49cece0f051a1e146d8c..7279f893a437b550b71cb3b7656f7848dd79d513 100644 GIT binary patch literal 11039 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 Z;&0El=iBq``S#G~{{z>zebfLF0RXKA)5rh- literal 10671 zcmV;gDNxoQiwFP!000001MPkLbKAC-aDVn+fpx#xX>Lmz0H5;A?v5-mv2JBa73DOU z$$VOhglskxsgjf*clyWg0Hi4JDN3@Fwl}B-(+${O;8r2%##= z;5!JBra=B*`bEM>0pB56RwYeFP{Q9qB&iaGe?5_Q&nAkk@H+@&;+#Z>DYI1yf)|tT zga^qg#{~K8kB&GhpKY_QQ3*M3iKoB7L5~E8+2N+`MEFv-R zqfCs!r0X~#i6VeHSVXEW>Z*uj2n3(RD=Yd8u%d|D|G_Q#BUyOC-P#JR6%ZDi<&uP^ z`!8~H1*9|LTMLg+s3o!DO-R76LomDoJrkJb5KN6rLZ zhp~xB2>d7vf*2@HXDP|L3_Fl$DprwdchC}QwrzE+j-x3K>NrlvQY~oXrK}`zQ9(;(FxHs=*WwXrVxFxaF)R+bGQyhO^S;#?}~C6+H?R?hc96pySG0e1WpdDcSo0^9P~SJ#h8xU_5n-~|GL zwTT0t+=2eUWVQpsy>%1#ZXASQ{Pryn;`=bz+}*D?F@Qi=SibYzY_$ydYM5vJAr@ST8L5^Hv1P z@XT@?Y9)8Af+f-6TVTxZJRc)v_=BKdG@W9Yfpns?D}_EP0b|9H$;?ge`O{~K|AWS`(TAG zZAH;mqNMiwt2MAZD_nu@Wb*b`;oWr7148Vs2!Qnv!mq}a@K19*Y6&_sKFsPsd&66E zW=w^vZtv&Gusbq@>nq`AV48!`yA(%PWmtSxgAe$Z`y#rH>T!b zJfZ^KbZ5P07UP>qw>J~s4!h>HU`(guso4a7-4(97eZe$(<59mm{V15T?#$SOF&a~4 zr?V@gJKIOy{5a|fZ@Yux?Q|d6L-f4vPWwWCVE!!hr-S!K3r#2E=}hPjhvS}LeCQdp zqBCGgZG@$wH^Q19&Efc+&>N3NMsG&t?@z~*Nrsd4NU1+{1Ajuhh6o5N65j_7=o%(g zR3LmN&+jd7LjaQHg)KRHr$eNUy0CSq%hpnZNV0Ux>PXtcRxQVY3eq|YX(3}rv9KjU zO(kSuEo|9xkgiI$q(B=YMe4{MO|_g3Q8c8hmZfwS#O~Nwx1qAYwqz^1r6FaZ>+;f3 zoQ1X|7}*kn8Y0Vut}QWC5Rx2c;b5mD5w35@{!Dc-M!lQab$>2Vbv^?j+ANpu6YJRL z5YG{uBYiGObF474*sC?)QIe;agudnVEc>2-kA}&dPL%i)Bw|q#y4pPQD7rcqwH5iTQA7mW{PtWThjH&WKu)TQZu1K(n z0iqH|58?K=q(UI{e(o~NS#NU1{^;FIz-Y|0u`COCj+9OPc-NA@D!$gC~ z12zfcpUE@uBO$S;K;O7a*S2B;*pJ;ek^Q7~m3#M@JeR03bEI7n!&wG_Xm4@p4~oU& zh6P^-A`(Sz;77CjkVN-^=K!S8z?w1e7T_T*b86{m9xRt$U^(-Lh=p#so(Th4mVmG( zVdO?J@omBkFdC1HvS~x&xFNCQw_&iF&;p<@8INiD%dIeT+?agk?qUKGw+uW?cr z9-^c!~Eq9LlJ z+S0Xb>b}70*^bgbh1d%3NIblHUso)G9jsQg37o$T!bdA~A`p__HzbN95C>rO>@8Ap z#Z3Nn;JNm5`j5~v1sMq8#-m}{Y&H?6#xLUa*qn{JH->2Z^24Wds;FoOt54@|&Vf)8 zGiwl>4OiAvx7)pqNEiWS>!ZW9J)+@#aj3CQgLgn|gr$Xq6e1x1W13n%`TFVn zjo2HHX5GQa{NdAi6s$-ThpvA|3nj-qE#4w?`YvNIFz4^P!$H3Um6YXrz4U@dkdE(y z1+76A>stfYj|UFGk|N7`N0C&9un4Gsk7+;~0fD($*2)vBBn`|C{FwN0;?4WU+wSdf z#s?u4%j-3=QgOJmYEYhR;A(>4bssNUYBzayQN1 zs}SZ$v*=(&Rzr8?#-Nr(-x$ui|1?{3NXoi{nA5VM>I z%eRTs3w+0=u$ISBF4zCTQn;~V`<~W*r6>p*`9VwtX?T);w^zeKZ=X0f-4FBbyS-B1+?uob z)L^2wFxq4~{_ybtcqRuGgR=?)0zwoAAsP7jPNuOgytl&VY8z$klY;CTL}RjNZ+z!G8moDD@xpXy$A>n9Pk)fBvf}n#OESM-lUzao_l1@NP7o z8ojP*yrIta(>a|}TE~Ln+kumW0Eah%Nod`zEKc>ZXbH$+jx?j6bViNz83>807#vof z?#j9&jjVb}cPh}Jn_0Woz~-zwoteK5X4gM_I%kA0a&hlrlI8oA1_4bH3k`isB_uwm zXlM|@^eIa5i+DR7HdtgEcFqT*zVYFQPv?}<()Gxk&M5e7JV_-`*=BlJzqP^y}Z`W@?>6AuYXz(mT8CXI^N6jx6`%D za}*A0C)|+3TCY7iY2gO`#gp)x^=g+ z>GD|Y`hBvrHeMV7fjn5=#)?S0Ja3ghw|&~A`+s*fx93Mw#!P!(>hEhiGVTcAI+;S($5& zgN zyrSk9=zZjytgh&~R7>a9H0bQr zxYwnt_B3q09nR9UnW^#1JRLIAiRy>o^WGTm3&>>dlc{L#SC@$&J~Rtg-vN9{A@^9Y zzT#%9PLJF&Qo2;lFV`|%npUa#EP)bqZrQj!B;Tm27S&q!V!PUZYnaJok>f|ekEX;1 zBCtf7o0u;8N1{(+Ak1b%mKL*Jq37Wj2g2J2!aE1TlLO(G2g2VS2!DSdyn7)0ZwJDE zJ`mnJ5Pr1>K5+z<=?i=n&!q+MRlAWumS#|DlRG)B6-kp9Ws&`7n-}1?+q41KA%0Bx z`6P~P%cB`F!HO<=THXrS!7525+AQeQ(}INH?rD&vkP2%%TuvDz{VTytlZYl(eBXUP z0a(Sb)5*PlPkbi`T?a_ItYp$!jzi5_Q3$Bh5j9blL>X5XNQzp~K55DT#=59=L<~_Q zc17z;7z0qIIB- zM03Mr5#dfFZiVNhGRsJWqTGmC;S;IAk|a{Dji?pP4MQvJ3;wOF^0f#8q_mrV($xz;pM z!~+U#vC%qCyKV{>JhGk~(CT*=uI*$L*kkEHxRMb~cY9SXp5sRXHO{aS>a>btOM75Hf$CPii@W&>&Zahjx{f%A993|zRrL!QL@c;%e~VK|-L z?2^}6mr|eSxy?M?hJO1X(K=+o_XeQ3acr0Lao(VfHVd>y>mtk;i4UhQ1X? z?wZSIRiKKJ5Ozv75#bV2qsWuEpRpIA!cYFUllxx5`g6FpBaX2IVdsl5X8pWyd5JuM zWjcNVmP}Q~QG!taf?Dp(`$bjFmFjEiTRWaQkREG#*2Zz!t}8FNdzXwj`S{W_W=T^2 zq&w~2nDcJ`{d_VW=I7(mhbQRQ(;FQ`F5dV)@wS6uKbj{m=6+zW)~WYs)w*fk^=Fbu zQJLLcMXb8L9!09y0*@k6Y*QylQfMW|i&L!k2T-c7$+o+`CyCcLdi`so6%NOEmvP?) zlh}4|6Fv}zI+FfUAucyK5_q zOLVg@7mS``#n$<;h>aqAxEbc~#eZBpu>k1+1o0oAKK*zw*3-%p8b!OCPI~o)Ps8e_ z!rzZAJd(xf6n`1u0WFOZt-6$N>?M2CUs_i^Wn$J%I z{eJ~M0q5^-W&%_$AruUACUjRMbZzTmL>}iKgYfe?!+UTY5(IQ~kme@yvQI7F309`H zT6?a4HzA=-{Mfo<0W>5(LU?v|Mz_M>McyK}|2( zc;7zw+5HBXWX<-$OP2^=N;1F@DQ68E;h{)J6LQ5Ccv%-=)Bbk4L5yycdS?!=DmJ=z z*Xy@l@Yr&z(sS4AXVD2n$D%{`Ggdp9X}50Pj-QZ7--t_vfu|E_$v};6f8MNz zS2xL)m+AO+)Sr&8=m0PdJQC8yX*xPg_kRq7yL_nFiq%P6SijYdjT5Xa*Wd1KuZE#> z8Ok9<(?BGnD)_{)1qyi*mFRIxB`dLQ$dl)?`^oB2vcqn(@Laax2#Sg-$|ByrV|#2E zOl~V=?^48l#_q$T57CKXE_p?T5QEM-AM)gFQi$kh{ol7Gb z`Wxp}=88GdZmUb_D=V<9csP4e6$+(*ps0%-RYZ9sCGQHT1!GXpge!2QXw&+CQ#e*+`l%_d-t?)orFJC}VBC9YR$fJ*wMQllxd=3lTfRG`i+2mfGT>j-iq8V51LS{|9)h=XX449QeAuUa#pYjX{KUQw>IZ_}B5jRBqifEt zx}*NDgFZc7=|!DFoeZzTzT7Tr`Sb0Y0yX-H_EegDL@gE0L)605#jUN0w(~c6Q|x|L zchS0tXx4z-`3jwMS?ryY%zjpjjqrhTzhq3rm0RXKGCP(aw*F@)@}J4WI6)X$URCv@ zlK`!oa&~?VJ%4az(UV!6Y(n(BrKhQv2o}!|S5Q(yS^vv4s=rZs@NRMJ74*e^^n(WSf0=>ZZ37h3- zCx0C6QubBaTEpy|CHJD*FwFNHVwsY={8L({I6GLXF$c^K@|7!Bm0FZeo!x2EwG zXD0aBugb|UmJKF6M8eW?y@0~8PB}}jDj$f}8;^e;7=w{%Ow$8JgXFN+VpV8inbzW^ z=eN;NA3>VDS#?xfYsr!>=e>EDVO~a|DDrYtX`}wrSUV9eIZAM6>&jyUU)I<~tL#&! zxF4i#8AkE?fmm43pe}E_=@W^eQ-~!r?3bW%oKi)@pJJeDTF2>@*2GiHlOypm8u4ju z_C2vb&!6UgG>;z~h=mi8y&n~Q&XwNpru-u`RY>>^iZ>#GqOkFejOl+Urxh;awnTe$=ID<$K9fJqq1+ z9gFg5{56nsu38qZ7-+_*x^Q5NQGe+`9-{f;fqHxAD_y=ADvn&KDyI@sd0$fvyB52> zYB9MzUs*4HXL;7sv*-qM`nf%^Hrv2j;#O`3Ysx-l#n8gbXp)EK7eE*gHE zoNUW37VlTS)~CF0yd+@be)JmXnmy?y=-ikq|CR1dFK)yyL7co9|EtAQf5#{K91`-% zSHL#M{|G7?V6iGxHCdLV?;w(8tbL3BPkr+7|IWVr!-wNwc=Z#Q_9s8(JU}TFqBRE1 zAKdkP+MiH(w&0rn7trf=>SCZ|uOGMgolk-+_qBOe>CaTSJ1QJ~U*WK;0hz1^xNYX8 z8~t`o>Smqi<|po$h{+F#b{5oXf$$DUukzD1Iu~gC<5*M#{&9A8_SNe0x94@9J9^I2 z*Yy8L!x$Fle`HxwzWM)CpZ4=VFrEKVbWy@B=6_5p1~=9-04jj&B4BfDml$^qRTE>+{WW)wPnD<1Pxz$hHzbM`&3;@Gn9vl%y+ zM5WoO0u63PCHdmgZ`E@tNhu+Ot4^idN7q_a6Go>JekM^hWqc~-?0cEE)S7nI5|1^t zaun^B=01*iTazD0y}j8LTIczTQC*+i%^nB4S>VDHbcN;X=AXIAGlu2nrk^=N4UF=| z(3X?6s$i9nYT#9}TU&M&%raUH{0g<8Ew^V9RWMA6rR)k5`6XMIr}4KS-h#E}N&>tE z>nK>OMiK?P)KN+Pzll&z-#RiBXiMbmdq6Ym`Qe~IJBOcu>(MNrpebOyGiK_k!$I!zu2TgmGpJkIEc$F02S563PPz~KaP#n6CG?UI>< z^}{5LE{a9NuR`pQ)5V9!$47DZVU8ij;(Ga%EJoCRbbWek8<@QPkX%R3t_BAmK79Dl z=Fae}aWU!G(o@u;7~78_>X8~o5e=N`#}V~NbR^NDcC}Zk0joi)CSLVwHDER9RjBvv zRcl6U&~6XMdIg&i8+6Py&Gs5Lp*85&$gN(xCN#D{S)Af7R_5*R=)tB3gV(@wwIqAd zjFC5c@~atxS7rBRi$nciNa%EaX{{hPw_MOC*>k&_f=3WGq!TAV0u3NuejRn&)5)5o zV+HGVvhPg$rxadB3htT97aTKdghm+x~h}u_RM$Hqn|ML4BBOzzN+(b2XmHJGr_?;12FOc;q>hBE*1GB`UDZ(1Fzl$c zl=7`)OH8p7VtG18uye7__<8HFf(3Vdi@N!+7FjF>|ra9>X50Y3nHGP`-P#-52jP z;pXf2p&B=1@6&k??>$;KWAD?O^IL~1-GI7J<0h_q)NMeey3(<5sjuGlbo4SU;lHiq z$YK(KAG~-4{JJ4WNzctoNR_Bpm8dezp~}@S=l@AiR+Iccq(Ka2ndX0B4Smc1IQ2>MKadKgoPGI+o=ud13@?Iw z&CGCyndqoW3Y2#NL8HWrg)Trz{LT8j5gqaXbZ*HR$!yuVH5rsAQ%v{F$V#%JR*o>L z$truOA*@#p?y9M&HM(LQqRP=`H5t8tEo)NcsJ9whs}lmrO635&noOsL4APN~Xw0cw>bxNpQvF&K=LJy?sWwG@gC>I7I z9hW83D#%M6p1e}R=~Uog4oAcKS(~EFO{OFsBwZ85veK@d7 zzgide?Rn*A5fcBE(|?=eKLu(q+5Z70tkM}=7XP7d`@c_q3j04Y`|?jd|CjD!)mr5L z{z@FS{~IF!^-G8@A-%1L_Bao89|?vcQs|a4?NWzl?EWh|@GjX29cRh@@Jm1w7aZ!9 z5CTZUq5?%Mi7Em*)ZtmS;6VSq1aY__$qh{p?plcMt&p%)cR`1TN2|re-ZIHF(A;Wq za0)S!B?U{mT+Wj$^WqZ%3tzXXX2oo&0@yaUFrikFS1HM=+-W1qZtHg8W14#-=seBz zB=KFIT#fiirX1TqJQ1^;p#So&a(O<>542ND3?Yh4dPf)90u#qfr`MI41c~;;Tzx9($l7?C|~q= zCpL1a8p$vOHk4t1<=4{?LQt0!oSRmv^BgG-RBKc;D5`yD!^)YhTX<)fGO=#(a1ZQ?xo!J9RxOIK8NJ8{s=>;-*#Lay4WD^e?A z_Ih=0$?hw;^-?-bl^)z#34__ln6<>>viJ($p@Jl&vV^{3%3dvGn!UIC1~F2xgb?ar zlmL7#;j@DUT1nd`v8e@KWoA3c(-O2bGEhLH+}k^XXq!4gswe3W722E!!Z zcHK1mT81f3YO~*M4AapSjMSqHqcj*sDHsO*)eM6m(mHy6!Z*{7kH&Tj=~z}Es)WZJ z{BGKm!sAMKiGx=#2cKW`=BgbR9dh(Ls&;sg-#8w8jb0~5{8dMUD%4fEd?-efJpW#` z{qV~2>Z7RK7z$x!?;z@I6vXW86+g(Wh5iI+P?8>&cE;^9yqylkNq2TV?~eNOUv7=* zM}Eg)uRBAZ&fom;2WW}VAHO>YelnhZI)4M&BbYbc;jm{6hwK=(3PpI}i~jYm7i_m= zNY{2|8#!gKg@sW&E}uZ&(nhTALxBZFEgg(Xz^o7gpb(y zk7P;iAgJNe_>JSj4z9axzL<0c*DLv@_K9UNAbdLL(psW_M#O%f}I zN_;aqOn{HZLEu-nHxuK)P^5d+V=9&=xOo4$f08i=%}p-)5A1Bw%IQSzTRMSwe&I*S zdMZ2TngcjbW)MpNjO#x)K86?~iB2h=1A#oP-SGJ)J!~_7<71uBZRQvo;49}Ao9I`~ zDK@}o2f1>Co{A7>&)EY_Ie6Wv*AGLO2F2`hO66mxnV=Fj@huGp`fG}DJXj0 z7i?l9zXTgef~sCRs-z<1JCTh_GdU}cFRL1nw05kyVZ~us_$VytC@lFXETs;scq~Yj z38To9_;ktqd1wt3GIJhJ0iR4kI+=oeG6kg-g?9Bh*Y)?dJ%77eeFQE(0arQ!S3Ut( zIUYB&^8wLH6JAr)(#WqNdTGL|JjtS}E*z*|9o|0uYWeNauMTgoe)vfJYVh{zSI2Lk zel>V|^^=a&uLf_ges%oz=~sidS3mhk{c7;`>Q~2apMEuXd-YR})UO6_uYPs>_UTuH z$3{u7y`GU@&Oqv=f@FRh@{WeFrr?S@Z)z0S7FzP^P&9ND8q|+N z>!fJ;>r8VB>?$EGndH@rtZbGq1fa{3)tMiq)Q_gKV=UVyQPd9}{oFP_x*<%$5LW4m z^17nGfv&o)Nos|z+p4m24u5%Fu_9v)SLxcx>)QG2>nfoR-EW&e7oe(_$QJ7*RjPn4 z^cftqT!t?7AUklSQvqJ=PdMuE?CcAq4&=MqPG#ai6nVSEfqcIkgl^!kLnkqk#Pm=k zfY`k%JOJhafqp@h#sbVwSc^b2Wot9bM3s z!_9hog;ZVA&BVMKa8p_e*0L>=O`2pcgv5e;OD=rl$|k*?fE`ozr%tu0Zp;Yk8kVz& zwT*pngMDzpKKSooA4TnC8@RcMzGR>l^M)^k!_B?$Un|$CiU8?i)%JBqkrj298IxN`r`3`x|wn|7UjkD&C=_4OGob~O$!b<@4QFW#|lyK=0{ z>^%8$SCt8x*++aR+v*o64GjOgghMf zan|hB&NtWe?p$fyLgOp}j+6^db;rEt# zyTHB{Ucu#HyBw!xJHt@2dFR#V-;$seD^Qd=mmL{mF4Ug&CdtN~%EJ#>xE+69+}Yj& zW((|7<^HWU{;lFO6-h5#|>GZqc(KqS4x85eY zFCB~5QIRIMHW#ip7cM~lUCo7|N)JJ9X)e9qTzc7D`rpl52qJ{($^T_Po-!{NvtorI0mBHIr$EXYu zzdABvn7BJQL6WNAi$Ng=FV~&{x$m*oYmdD00ib)%f0uE3{ySEnhM`K& z|CHgk^WRT?c$@Nn^A8<6oFmYkOoql>mZdqJ<2KKaIRgG|b_BdEOU(DKHeT$mz3h&- z2qbg;-ZKdNZR5F!)^x{?>$AH;N<)+?;nI-`mZ3U1S{4os@gw)~KYkiwZexmC~sk8XUQ z7@Ag%mFqj1v5|(fs{W_S*2TFU%;vb-;&Raoy^%F5ce)l;3SEvM(N2su5kVWo%1*l# z$Etp{4Pw?=?r9P4y2EZ(>2+Lc6cyirX-wZ6)82S$q(icU2V`HWZKMY&sFKv7x2y4u zfmecS5gI(WMW}kGy{XY2Q=>XlqrZx&WfiM=zr!mLkq0{n2@-z`eE&Ij|AB@i{o8r> V-=1&Jx98vB`9IGN8jJu60RWS*1Ec@| diff --git a/tsconfig.json b/tsconfig.json index 8c339c1..add7760 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2016", + "target": "ES2020", "module": "commonjs", "moduleResolution": "node", "removeComments": false, From fa1d775fee2c873daae05ba5522a0c9d9dac75f6 Mon Sep 17 00:00:00 2001 From: DumpySquare Date: Thu, 5 Dec 2024 21:31:09 +0530 Subject: [PATCH 2/4] final work --- fixes_enhancements_oct24.conf | 209 ---------------------------------- package.json | 2 +- src/digLbVserver.ts | 3 + src/extension.ts | 2 +- src/models.ts | 1 + src/ns2FastParams.ts | 6 +- src/nsCfgViewProvider.ts | 90 +++++++-------- src/regex.ts | 1 + templates/as3/FTP.yaml | 100 ++++++++++++++++ templates/as3/HTTP.yaml | 52 ++++++--- templates/as3/RDP.yaml | 31 +++-- templates/as3/SSL.yaml | 28 ++++- templates/as3/SSL_BRIDGE.yaml | 30 +++-- templates/as3/TCP.yaml | 32 ++++-- templates/as3/TFTP.yaml | 98 ++++++++++++++++ templates/as3/UDP.yaml | 36 ++++-- 16 files changed, 399 insertions(+), 322 deletions(-) delete mode 100644 fixes_enhancements_oct24.conf create mode 100644 templates/as3/FTP.yaml create mode 100644 templates/as3/TFTP.yaml diff --git a/fixes_enhancements_oct24.conf b/fixes_enhancements_oct24.conf deleted file mode 100644 index daafb3f..0000000 --- a/fixes_enhancements_oct24.conf +++ /dev/null @@ -1,209 +0,0 @@ - - - - - -### student-qat.uwo.ca--443 ########## - Hover for more details - ########## - -# not capturing the action from the service policy - - -add lb vserver student-qat.uwo.ca--443 SSL 172.31.25.65 443 -persistenceType COOKIEINSERT -timeout 1201 -lbMethod ROUNDROBIN -redirectURL "http://www.uwo.ca/western/PeopleSoft/HEOutage/studentStaticQAT.html" -cltTimeout 1201 -appflowLog DISABLED -lbprofilename "SameSite=None Override" -devno 62029824 - -bind ssl vserver student-qat.uwo.ca--443 -cipherName UWO-default -bind ssl vserver student-qat.uwo.ca--443 -certkeyName student-qat_wisg_uwo_ca_v1 -add ssl certKey student-qat_wisg_uwo_ca_v1 -cert student-qat_wisg_uwo_ca_v1.crt -key student-qat_wisg_uwo_ca_v1.key f94b16649a6ec398a4fb8ba18e5b78f65f16b7b4deb7a2ec7a1076f6195270f37cd533656c9d672e7cfdaa375788ec34 -encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2022_09_28_20_29_18 -expiryMonitor DISABLED -bind ssl vserver student-qat.uwo.ca--443 -certkeyName Sectigo_intermediate -CA -ocspCheck Optional -add ssl certKey Sectigo_intermediate -cert SectigoRSAOrganizationValidationSecureServerCA-int.der -inform DER -expiryMonitor DISABLED -bind ssl vserver student-qat.uwo.ca--443 -certkeyName Sectigo-Intermediate-2-usr-trust -CA -ocspCheck Optional -add ssl certKey Sectigo-Intermediate-2-usr-trust -cert "USERTrust RSA Certification Authority.pem" -expiryMonitor DISABLED -bind ssl vserver student-qat.uwo.ca--443 -certkeyName Sectigo-Root-AAA-cert -CA -ocspCheck Optional -add ssl certKey Sectigo-Root-AAA-cert -cert "AAA Certificate Services.pem" -expiryMonitor DISABLED -bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_256 -bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_384 -bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_224 -bind ssl vserver student-qat.uwo.ca--443 -eccCurveName P_521 - -bind lb vserver student-qat.uwo.ca--443 student-qat--weber02--7035 -add service student-qat--weber02--7035 weber02.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -state DISABLED -appflowLog DISABLED -devno 38666240 -add server weber02.its.uwo.pri 172.29.52.50 -state DISABLED -comment NOC-9852 -devno 16867 - -bind lb vserver student-qat.uwo.ca--443 student-qat--weber03--7035 -add service student-qat--weber03--7035 weber03.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -state DISABLED -appflowLog DISABLED -devno 38371328 -add server weber03.its.uwo.pri 172.29.52.51 -state DISABLED -comment NOC-9852 -devno 16868 - -bind lb vserver student-qat.uwo.ca--443 student-qat--nweber04--7035 -add service student-qat--nweber04--7035 nweber04.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -appflowLog DISABLED -devno 899219456 -add server nweber04.its.uwo.pri 172.31.33.172 -comment NI-43936 -devno 22202 - -bind lb vserver student-qat.uwo.ca--443 student-qat--nweber05--7035 -add service student-qat--nweber05--7035 nweber05.its.uwo.pri HTTP 7035 -gslb NONE -maxClient 0 -maxReq 0 -cip ENABLED WL-Proxy-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -appflowLog DISABLED -devno 899186688 -add server nweber05.its.uwo.pri 172.31.33.173 -comment NI-43936 -devno 22203 - - -bind lb vserver student-qat.uwo.ca--443 -policyName rw_add_samesite_cookie_info -priority 100 -gotoPriorityExpression END -type RESPONSE - -add rewrite policy rw_add_samesite_cookie_info "http.RES.HEADER(\"Set-Cookie\").EXISTS" rw_ac_samesite_insert - - - - --------------------------------------------------- - -### finance.uwo.ca--49000 ########## - Hover for more details - ########## - -# good example of multiple services. not following the -backupVServer - -add lb vserver finance.uwo.ca--49000 TCP 129.100.0.40 49000 -persistenceType NONE -cltTimeout 301 -backupVServer finance.uwo.ca--49000-failover01 -appflowLog DISABLED -devno 59146240 -bind lb vserver finance.uwo.ca--49000-failover01 finance--austin02--49000 -add service finance--austin02--49000 austin02.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 36503552 -add server austin02.its.uwo.pri 172.31.30.45 -comment NOC-8872 -devno 16854 -bind lb vserver finance.uwo.ca--49000 finance--austin01--49000 -add service finance--austin01--49000 austin01.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 36372480 -add server austin01.its.uwo.pri 172.31.30.44 -comment NOC-8872 -devno 16853 -bind lb vserver finance.uwo.ca--49000-failover02 finance--austin03--49000 -add service finance--austin03--49000 austin03.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 37158912 -add server austin03.its.uwo.pri 172.31.30.46 -comment NOC-8872 -devno 16855 -bind lb vserver finance.uwo.ca--49000-failover03 finance--austin04--49000 -add service finance--austin04--49000 austin04.its.uwo.pri TCP 49000 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 301 -svrTimeout 301 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -appflowLog DISABLED -devno 37289984 -add server austin04.its.uwo.pri 172.31.30.47 -comment NOC-8872 -devno 16856 - - - ---------------------------------------------------------- -### recruit-dev.wisg.uwo.ca--443 ########## - Hover for more details - ########## - -# not following -policyName reference - -add lb vserver recruit-dev.wisg.uwo.ca--443 SSL 172.31.25.121 443 -persistenceType COOKIEINSERT -timeout 0 -cltTimeout 1201 -comment NOC-17839 -redirectFromPort 80 -httpsRedirectUrl "https://recruit-dev.wisg.uwo.ca" -devno 76087296 -bind lb vserver recruit-dev.wisg.uwo.ca--443 recruit-dev--7057 -add serviceGroup recruit-dev--7057 HTTP -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -comment NOC-17839 -devno 55607296 -bind serviceGroup recruit-dev--7057 watson03.its.uwo.pri 7057 -devno 120225792 -add server watson03.its.uwo.pri 172.29.52.39 -comment NOC-8761 -devno 16846 -bind serviceGroup recruit-dev--7057 watson02.its.uwo.pri 7057 -devno 120193024 -add server watson02.its.uwo.pri 172.29.52.38 -comment NOC-8761 -devno 16845 -bind serviceGroup recruit-dev--7057 -monitorName recruit-dev-index-check -devno 120258560 -add lb monitor recruit-dev-index-check HTTP -respCode 200 -httpRequest "HEAD /index.html" -LRTM ENABLED -interval 20 -resptimeout 5 -devno 21436 -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -cipherName UWO-default -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName wc_recruit-dev_wisg_uwo_ca_v1 -add ssl certKey wc_recruit-dev_wisg_uwo_ca_v1 -cert "/nsconfig/ssl/wc_recruit-dev_wisg_uwo_ca_v1_cert.pem" -key "/nsconfig/ssl/wc_recruit-dev_wisg_uwo_ca_v1_key.pem" 36d3f278db3556c7c578fe9e1a7d391997849f3838d584d6133a200481816c414df22e41fda677b4c79baedf90ed7ed0 -encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2022_09_28_20_29_18 -expiryMonitor DISABLED -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName Sectigo_intermediate -CA -ocspCheck Optional -add ssl certKey Sectigo_intermediate -cert SectigoRSAOrganizationValidationSecureServerCA-int.der -inform DER -expiryMonitor DISABLED -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName Sectigo-Intermediate-2-usr-trust -CA -ocspCheck Optional -add ssl certKey Sectigo-Intermediate-2-usr-trust -cert "USERTrust RSA Certification Authority.pem" -expiryMonitor DISABLED -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -certkeyName Sectigo-Root-AAA-cert -CA -ocspCheck Optional -add ssl certKey Sectigo-Root-AAA-cert -cert "AAA Certificate Services.pem" -expiryMonitor DISABLED -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_256 -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_384 -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_224 -bind ssl vserver recruit-dev.wisg.uwo.ca--443 -eccCurveName P_521 -bind lb vserver recruit-dev.wisg.uwo.ca--443 -policyName res_invite.recruit-dev -priority 100 -gotoPriorityExpression END -type REQUEST - - - - -### westernworldwide.uwo.ca--443 ########## - Hover for more details - ########## - -# rule for -redirect reference - -add lb vserver westernworldwide.uwo.ca--443 SSL 129.100.0.69 443 -persistenceType COOKIEINSERT -timeout 1201 -redirectURL "https://www.uwo.ca/western/PeopleSoft/WIOutage/WIStaticNormal.html" -cltTimeout 1201 -devno 57442304 - -bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis01.its.uwo.pri--7170 -add service westernworldwide--willis01.its.uwo.pri--7170 willis01.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 52101120 -add server willis01.its.uwo.pri 172.29.52.72 -comment NOC-15774 -devno 16911 -bind ssl vserver westernworldwide.uwo.ca--443 -cipherName UWO-default -bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName westernworldwide_uwo_ca_v1 -add ssl certKey westernworldwide_uwo_ca_v1 -cert westernworldwide_uwo_ca_v1.crt -key westernworldwide_uwo_ca_v1.key 55f72a447442a909a275097253767785f2c99acb3fd983f45b49d6c823253821f383ee2126eb52d58825ade97f7640b3 -encrypted -encryptmethod ENCMTHD_3 -kek -suffix 2022_09_28_20_29_18 -expiryMonitor DISABLED -bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName Sectigo_intermediate -CA -ocspCheck Optional -add ssl certKey Sectigo_intermediate -cert SectigoRSAOrganizationValidationSecureServerCA-int.der -inform DER -expiryMonitor DISABLED -bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName Sectigo-Intermediate-2-usr-trust -CA -ocspCheck Optional -add ssl certKey Sectigo-Intermediate-2-usr-trust -cert "USERTrust RSA Certification Authority.pem" -expiryMonitor DISABLED -bind ssl vserver westernworldwide.uwo.ca--443 -certkeyName Sectigo-Root-AAA-cert -CA -ocspCheck Optional -add ssl certKey Sectigo-Root-AAA-cert -cert "AAA Certificate Services.pem" -expiryMonitor DISABLED -bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_256 -bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_384 -bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_224 -bind ssl vserver westernworldwide.uwo.ca--443 -eccCurveName P_521 -bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis02.its.uwo.pri--7170 -add service westernworldwide--willis02.its.uwo.pri--7170 willis02.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 52133888 -add server willis02.its.uwo.pri 172.29.52.73 -comment NOC-15774 -devno 16912 -bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis03.its.uwo.pri--7170 -add service westernworldwide--willis03.its.uwo.pri--7170 willis03.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 54198272 -add server willis03.its.uwo.pri 172.29.52.74 -comment NOC-15774 -devno 16907 -bind lb vserver westernworldwide.uwo.ca--443 westernworldwide--willis04.its.uwo.pri--7170 -add service westernworldwide--willis04.its.uwo.pri--7170 willis04.its.uwo.pri HTTP 7170 -gslb NONE -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -sp OFF -cltTimeout 1201 -svrTimeout 1201 -CKA NO -TCPB NO -CMP NO -downStateFlush DISABLED -devno 54231040 -add server willis04.its.uwo.pri 172.29.52.75 -comment NOC-15774 -devno 16908 - - { - "code": "4818", - "severity": "Warning", - "title": "vServer redirect detected", - "message": "this is typically handed on f5 with an irule to detect number of pool member avaialable and redirect as needed", - "regex": "-redirectURL" - }, - - - - - - --------------------------------------------- - -### groot-cs-redirect ########## - Hover for more details - ########## - -# json output not working. bug in map somewhere - -add cs vserver groot-cs-redirect HTTP 192.168.10.65 80 -cltTimeout 180 -persistenceType NONE -devno 54722560 -bind cs vserver groot-cs-redirect -policyName groot-i-cs-policy -priority 100 -devno 112005 -bind cs vserver groot-cs-redirect -policyName groot-am-cs-policy -priority 110 -devno 112005 -bind cs vserver groot-cs-redirect -policyName groot-yes-cs-policy -priority 120 -devno 112005 -bind cs vserver groot-cs-redirect -policyName groot-groot-cs-policy -priority 130 -devno 112005 -add cs policy groot-i-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"grooti\")" -action groot-i-cs-action -add cs action groot-i-cs-action -targetLBVserver groot-i-lb-vsvr -add cs policy groot-am-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"grootam\")" -action groot-am-cs-action -add cs action groot-am-cs-action -targetLBVserver groot-am-lb-vsvr -add cs policy groot-yes-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"grootyes\")" -action groot-yes-cs-action -add cs action groot-yes-cs-action -targetLBVserver groot-yes-lb-vsvr -add cs policy groot-groot-cs-policy -rule "HTTP.REQ.HOSTNAME.CONTAINS(\"groot\")" -action groot-groot-cs-action -add cs action groot-groot-cs-action -targetLBVserver groot-groot-lb-vsvr -add lb vserver groot-i-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50823168 -bind lb vserver groot-i-lb-vsvr groot-i-svc-grp -add serviceGroup groot-i-svc-grp SSL -maxClient 1000 -maxReq 0 -cip ENABLED X-Client-IP -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47382528 -bind serviceGroup groot-i-svc-grp sprout134A-grooti 443 -devno 71041024 -add server sprout134A-grooti 192.168.160.138 -devno 108843 -bind serviceGroup groot-i-svc-grp sprout134B-grooti 443 -devno 71073792 -add server sprout134B-grooti 192.168.160.140 -devno 108844 -bind serviceGroup groot-i-svc-grp -monitorName ping -devno 71106560 -bind ssl vserver groot-i-lb-vsvr -certkeyName star.groot.cer -add ssl certKey star.groot.cer -cert www.star.groot_2022.pfx -key www.star.groot_2022.pfx -inform PFX -passcrypt XXXX -encrypted -encryptmethod ENCMTHD_3 -bind ssl vserver groot-i-lb-vsvr -eccCurveName P_256 -bind ssl vserver groot-i-lb-vsvr -eccCurveName P_384 -bind ssl vserver groot-i-lb-vsvr -eccCurveName P_224 -bind ssl vserver groot-i-lb-vsvr -eccCurveName P_521 -add lb vserver groot-am-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50855936 -bind lb vserver groot-am-lb-vsvr groot-am-svc-grp -add serviceGroup groot-am-svc-grp SSL -maxClient 1000 -maxReq 0 -cip ENABLED X-Client-IP -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47415296 -bind serviceGroup groot-am-svc-grp sprout134A_grootam 443 -devno 71139328 -add server sprout134A_grootam 192.168.160.118 -devno 108845 -bind serviceGroup groot-am-svc-grp sprout134B_grootam 443 -devno 71172096 -add server sprout134B_grootam 192.168.160.119 -devno 108846 -bind ssl vserver groot-am-lb-vsvr -certkeyName star.groot.cer -bind ssl vserver groot-am-lb-vsvr -eccCurveName P_256 -bind ssl vserver groot-am-lb-vsvr -eccCurveName P_384 -bind ssl vserver groot-am-lb-vsvr -eccCurveName P_224 -bind ssl vserver groot-am-lb-vsvr -eccCurveName P_521 -add lb vserver groot-yes-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50921472 -bind lb vserver groot-yes-lb-vsvr groot-yes-svc-grp -add serviceGroup groot-yes-svc-grp SSL -maxClient 1000 -maxReq 0 -cip ENABLED X-Client-IP -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47448064 -bind ssl vserver groot-yes-lb-vsvr -certkeyName star.groot.cer -bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_256 -bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_384 -bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_224 -bind ssl vserver groot-yes-lb-vsvr -eccCurveName P_521 -add lb vserver groot-groot-lb-vsvr SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -devno 50888704 -bind lb vserver groot-groot-lb-vsvr groot-svc-grp -bind ssl vserver groot-groot-lb-vsvr -certkeyName star.groot.cer -bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_256 -bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_384 -bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_224 -bind ssl vserver groot-groot-lb-vsvr -eccCurveName P_521 \ No newline at end of file diff --git a/package.json b/package.json index 864a835..31ea432 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "F5 Flipper", "description": "Breaking down Citrix NetScaler ADC configs", "publisher": "F5DevCentral", - "version": "1.11.0", + "version": "1.11.1", "keywords": [ "F5", "F5Networks", diff --git a/src/digLbVserver.ts b/src/digLbVserver.ts index 0166a0a..20c0e10 100644 --- a/src/digLbVserver.ts +++ b/src/digLbVserver.ts @@ -44,6 +44,9 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { /* istanbul ignore next */ return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } + + // rxMatch.groups.name = rxMatch.groups.name.match(rx.trimQuotes)[1]; + const opts = parseNsOptions(rxMatch.groups?.opts, rx); const app: AdcApp = { diff --git a/src/extension.ts b/src/extension.ts index 10ca23e..0f9b217 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -282,7 +282,7 @@ export async function activateInternal(context: ExtensionContext) { const app = ext.nsCfgProvider.explosion.config.apps.find(a => a.name === appName) - ext.nsCfgProvider.render(x, 'lines'); + ext.nsCfgProvider.render(app, 'lines'); } })); diff --git a/src/models.ts b/src/models.ts index 1dacea7..52129e4 100644 --- a/src/models.ts +++ b/src/models.ts @@ -219,6 +219,7 @@ export type AdcRegExTree = { cfgOptions: RegExp; cfgOptionsQuotes: RegExp; verbs: RegExp; + trimQuotes: RegExp; parents: { 'add ns ip': RegExp; 'add ns ip6': RegExp; diff --git a/src/ns2FastParams.ts b/src/ns2FastParams.ts index 610efbb..ab64f83 100644 --- a/src/ns2FastParams.ts +++ b/src/ns2FastParams.ts @@ -4,7 +4,8 @@ import { AdcApp, - NsFastTempParams + NsFastTempParams, + AdcRegExTree } from "./models"; @@ -12,7 +13,8 @@ import { * mutate ns app json to a form easier for FAST/mustache to work with * @param nsApp NS app as json */ -export function mungeNS2FAST(nsApp: AdcApp) { +export function mungeNS2FAST(nsApp: AdcApp, rx: AdcRegExTree +) { if (nsApp.fastTempParams) { diff --git a/src/nsCfgViewProvider.ts b/src/nsCfgViewProvider.ts index 8708ca3..3a042d8 100644 --- a/src/nsCfgViewProvider.ts +++ b/src/nsCfgViewProvider.ts @@ -328,66 +328,66 @@ export class NsCfgProvider implements TreeDataProvider { sortTreeItems(treeItems); - } else if (element.label === 'FAST Templates') { + // } else if (element.label === 'FAST Templates') { - // list fast template files - // list all the files in the templates folder - const files = readdirSync(path.join(this.ctx.extensionPath, 'templates'), { withFileTypes: true }); + // // list fast template files + // // list all the files in the templates folder + // const files = readdirSync(path.join(this.ctx.extensionPath, 'templates'), { withFileTypes: true }); - files.forEach(file => { - // is file or folder? + // files.forEach(file => { + // // is file or folder? - const filePath = path.join(file.path, file.name); - const isFile = lstatSync(filePath).isFile(); + // const filePath = path.join(file.path, file.name); + // const isFile = lstatSync(filePath).isFile(); - if (isFile) { + // if (isFile) { - // create template object - // const filePath = path.join(this.ctx.extensionPath, 'templates', 'ns', file.name); - // treeItems.push(new NsCfgApp(file.name, ``, ``, 'nsFile', '', TreeItemCollapsibleState.None, { - // command: 'vscode.open', - // title: '', - // arguments: [Uri.file(filePath)] - // })); - } else { - // get number of templates in folder - const filesCount = readdirSync(path.join(this.ctx.extensionPath, 'templates', file.name)) - // create folder - treeItems.push(new NsCfgApp(file.name, ``, filesCount.length.toString(), 'nsFolder', '', TreeItemCollapsibleState.Collapsed)); - } + // // create template object + // // const filePath = path.join(this.ctx.extensionPath, 'templates', 'ns', file.name); + // // treeItems.push(new NsCfgApp(file.name, ``, ``, 'nsFile', '', TreeItemCollapsibleState.None, { + // // command: 'vscode.open', + // // title: '', + // // arguments: [Uri.file(filePath)] + // // })); + // } else { + // // get number of templates in folder + // const filesCount = readdirSync(path.join(this.ctx.extensionPath, 'templates', file.name)) + // // create folder + // treeItems.push(new NsCfgApp(file.name, ``, filesCount.length.toString(), 'nsFolder', '', TreeItemCollapsibleState.Collapsed)); + // } - }) + // }) - } else if (element.contextValue === 'nsFolder') { + // } else if (element.contextValue === 'nsFolder') { - // list templates in folder - const x = element; - const files = readdirSync(path.join(this.ctx.extensionPath, 'templates', element.label), { withFileTypes: true }); + // // list templates in folder + // const x = element; + // const files = readdirSync(path.join(this.ctx.extensionPath, 'templates', element.label), { withFileTypes: true }); - files.forEach(file => { - // is file or folder? + // files.forEach(file => { + // // is file or folder? - const filePath = path.join(file.path, file.name); - const isFile = lstatSync(filePath).isFile(); + // const filePath = path.join(file.path, file.name); + // const isFile = lstatSync(filePath).isFile(); - if (isFile) { + // if (isFile) { - // create template object - // const filePath = path.join(this.ctx.extensionPath, 'templates', 'ns', file.name); - treeItems.push(new NsCfgApp(file.name, ``, ``, 'fastTemplate', '', TreeItemCollapsibleState.None, { - command: 'vscode.open', - title: '', - arguments: [Uri.file(filePath)] - })); - } else { - // create folder - // treeItems.push(new NsCfgApp(file.name, ``, ``, 'nsFolder', '', TreeItemCollapsibleState.Collapsed)); - } + // // create template object + // // const filePath = path.join(this.ctx.extensionPath, 'templates', 'ns', file.name); + // treeItems.push(new NsCfgApp(file.name, ``, ``, 'fastTemplate', '', TreeItemCollapsibleState.None, { + // command: 'vscode.open', + // title: '', + // arguments: [Uri.file(filePath)] + // })); + // } else { + // // create folder + // // treeItems.push(new NsCfgApp(file.name, ``, ``, 'nsFolder', '', TreeItemCollapsibleState.Collapsed)); + // } - }) + // }) } else if (element.label === 'Sources') { @@ -533,7 +533,7 @@ export class NsCfgProvider implements TreeDataProvider { } // todo: possibly move all the fast template stuff to a separate view - treeItems.push(new NsCfgApp('FAST Templates', 'Conversion Templates', '', 'fastHeader', '', TreeItemCollapsibleState.Collapsed)); + // treeItems.push(new NsCfgApp('FAST Templates', 'Conversion Templates', '', 'fastHeader', '', TreeItemCollapsibleState.Collapsed)); // display ns config as json object treeItems.push(new NsCfgApp('JSON', 'Parent NS.Conf objects as JSON', '', '', '', TreeItemCollapsibleState.None, diff --git a/src/regex.ts b/src/regex.ts index c3256f8..5052c83 100644 --- a/src/regex.ts +++ b/src/regex.ts @@ -57,6 +57,7 @@ export class RegExTree { cfgOptions: this.cfgOptions, cfgOptionsQuotes: this.cfgOptionsQuotes, verbs: /^(add|set|bind|link|enable|disable) /, + trimQuotes: /^"(.*)"$/, parents: { 'add ns ip': /(?\S+) (?\S+) (?[\S ]+)/, 'add ns ip6': /(?\S+) (?[\S ]+)/, diff --git a/templates/as3/FTP.yaml b/templates/as3/FTP.yaml new file mode 100644 index 0000000..a5bfba1 --- /dev/null +++ b/templates/as3/FTP.yaml @@ -0,0 +1,100 @@ +title: Simple FTP Application +description: FTP Application - Includes Persistence and TCP Timers 24-OCT-2024 +contentType: application/json +definitions: + idleTimeout: + default: 300 +template: | + { + "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", + "class": "AS3", + "declaration": { + "class": "ADC", + "schemaVersion": "3.45.0", + "id": "urn:uuid:8cbab3f1-de39-4944-9459-cea0b3e7f69c", + "t_{{virtual_address}}": { + "class": "Tenant", + "{{app_name}}-app": { + "class": "Application", + "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, + "{{app_name}}": { + "layer4": "tcp", + "pool": "pool_{{app_name}}", + "translateServerAddress": true, + "translateServerPort": true, + "class": "Service_TCP", + {{#comment}} + "description": {{comment}}, + {{/comment}} + {{#persistence}} + {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}],{{/SOURCEIP}} + {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} + {{#NONE}}"persistenceMethods" : [],{{/NONE}} + {{/persistence}} + "profileTCP": { + "use": "{{app_name}}-tcp" + }, + "profileFTP": { + "use": "{{app_name}}-ftp" + }, + "virtualAddresses": [ + { "use": "sa_{{virtual_address}}" } + ], + "virtualPort": {{virtual_port}}, + "snat": "auto" + }, + "pool_{{app_name}}": { + "members": [ + {{#pool_members}} + { + {{#address}} + "hostname": {{name}}, + "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, + {{/address}} + {{#fqdn}} + "addressDiscovery": "fqdn", + "hostname": "{{hostname}}", + "servicePort": {{port}}, + {{/fqdn}} + "shareNodes": true + }, + {{/pool_members}} + ], + "monitors": [ + { + "bigip": "/Common/tcp" + } + ], + {{#lbMethod}} + {{#ROUNDROBIN}} + "loadBalancingMode":"round-robin", + {{/ROUNDROBIN}} + {{#LEASTCONNECTION}} + "loadBalancingMode":"least-connections-member", + {{/LEASTCONNECTION}} + {{/lbMethod}} + {{^lbMethod}} + "loadBalancingMode":"least-connections-member", + {{/lbMethod}} + "class": "Pool" + }, + "{{app_name}}-tcp": { + "idleTimeout": {{idleTimeout}}, + "class": "TCP_Profile" + }, + "{{app_name}}-ftp": { + "activeModeEnabled": true, + "port": 20, + "class": "FTP_Profile" + } + } + } + } + } \ No newline at end of file diff --git a/templates/as3/HTTP.yaml b/templates/as3/HTTP.yaml index 66d9662..0af820e 100644 --- a/templates/as3/HTTP.yaml +++ b/templates/as3/HTTP.yaml @@ -1,6 +1,9 @@ title: HTTP Application Template -description: This template aims to provide a jumping point for taking an NS app config and converting it to AS3. +description: 24-OCT-2024 contentType: application/json +definitions: + idleTimeout: + default: 300 template: | { "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", @@ -14,6 +17,12 @@ template: | "{{app_name}}-app": { "class": "Application", "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, "{{app_name}}": { "layer4": "tcp", "pool": "pool_{{app_name}}", @@ -21,13 +30,16 @@ template: | "translateServerPort": true, "class": "Service_HTTP", {{#persistence}} - {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}]{{/SOURCEIP}} - {{#COOKIEINSERT}}"persistenceMethods": [{"bigip": "/Common/cookie"}]{{/COOKIEINSERT}} - {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}]{{/SSLSESSION}} + {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}],{{/SOURCEIP}} + {{#COOKIEINSERT}}"persistenceMethods": [{"bigip": "/Common/cookie"}],{{/COOKIEINSERT}} + {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} {{#NONE}}"persistenceMethods" : [],{{/NONE}} {{/persistence}} + "profileTCP": { + "use": "{{app_name}}-tcp" + }, "virtualAddresses": [ - "{{virtual_address}}" + { "use": "sa_{{virtual_address}}" } ], "virtualPort": {{virtual_port}}, "snat": "auto" @@ -37,34 +49,38 @@ template: | {{#pool_members}} { {{#address}} + "hostname": {{name}}, "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, {{/address}} - {{#hostname}} + {{#fqdn}} "addressDiscovery": "fqdn", "hostname": "{{hostname}}", - {{/hostname}} - {{#port}} "servicePort": {{port}}, - {{/port}} + {{/fqdn}} "shareNodes": true }, {{/pool_members}} ], "class": "Pool", {{#lbMethod}} - {{#ROUNDROBIN}} - "loadBalancingMode":"round-robin", - {{/ROUNDROBIN}} - {{#LEASTCONNECTION}} - "loadBalancingMode":"least-connections-member", - {{/LEASTCONNECTION}} - {{#NONE}} - "loadBalancingMode":"least-connections-member", - {{/NONE}} + {{#ROUNDROBIN}} + "loadBalancingMode":"round-robin", + {{/ROUNDROBIN}} + {{#LEASTCONNECTION}} + "loadBalancingMode":"least-connections-member", + {{/LEASTCONNECTION}} + {{/lbMethod}} + {{^lbMethod}} + "loadBalancingMode":"least-connections-member", {{/lbMethod}} "monitors": [ "http" ] + }, + "{{app_name}}-tcp": { + "idleTimeout": {{idleTimeout}}, + "class": "TCP_Profile" } } } diff --git a/templates/as3/RDP.yaml b/templates/as3/RDP.yaml index 87a0998..ed48cff 100644 --- a/templates/as3/RDP.yaml +++ b/templates/as3/RDP.yaml @@ -1,7 +1,10 @@ title: RDP Application Template -description: RDP Application Template - Includes Long TCP Timeout and Persistence Timeout +description: RDP Application Template - Includes Long TCP Timeout and Persistence 24-OCT-2024 contentType: application/json +definitions: + idleTimeout: + default: 300 template: | { "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", @@ -15,6 +18,12 @@ template: | "{{app_name}}-app": { "class": "Application", "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, "{{app_name}}": { "layer4": "tcp", "pool": "pool_{{app_name}}", @@ -22,10 +31,10 @@ template: | "translateServerPort": true, "class": "Service_TCP", "profileTCP": { - "use": "{{app_name}}-tcp_idle_9000" + "use": "{{app_name}}-tcp" }, "virtualAddresses": [ - "{{virtual_address}}" + { "use": "sa_{{virtual_address}}" } ], "virtualPort": {{virtual_port}}, "snat": "auto" @@ -35,15 +44,15 @@ template: | {{#pool_members}} { {{#address}} + "hostname": {{name}}, "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, {{/address}} - {{#hostname}} + {{#fqdn}} "addressDiscovery": "fqdn", "hostname": "{{hostname}}", - {{/hostname}} - {{#port}} "servicePort": {{port}}, - {{/port}} + {{/fqdn}} "shareNodes": true }, {{/pool_members}} @@ -66,10 +75,10 @@ template: | {{/lbMethod}} "class": "Pool" }, - "{{app_name}}-tcp_idle_9000": { - "idleTimeout": 9000, - "class": "TCP_Profile" - } + "{{app_name}}-tcp": { + "idleTimeout": {{idleTimeout}}, + "class": "TCP_Profile" + } } } } diff --git a/templates/as3/SSL.yaml b/templates/as3/SSL.yaml index 55b3362..e60f1b3 100644 --- a/templates/as3/SSL.yaml +++ b/templates/as3/SSL.yaml @@ -1,6 +1,9 @@ title: SSL HTTPS RE-ENCRYPT Application Template -description: SSL Decrypt and Reencrypt Application - Enables Switching based on Content +description: SSL Decrypt and Reencrypt Application - Enables Switching based on Content 24-OCT-2024 contentType: application/json +definitions: + idleTimeout: + default: 300 template: | { "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", @@ -16,6 +19,12 @@ template: | "{{app_name}}-app": { "class": "Application", "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, "certkey-{{app_name}}": { "class": "Certificate", "certificate": { @@ -50,8 +59,11 @@ template: | {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} {{#NONE}}"persistenceMethods" : [],{{/NONE}} {{/persistence}} + "profileTCP": { + "use": "{{app_name}}-tcp" + }, "virtualAddresses": [ - "{{virtual_address}}" + { "use": "sa_{{virtual_address}}" } ], "virtualPort": {{virtual_port}}, "snat": "auto", @@ -66,15 +78,15 @@ template: | {{#pool_members}} { {{#address}} + "hostname": {{name}}, "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, {{/address}} - {{#hostname}} + {{#fqdn}} "addressDiscovery": "fqdn", "hostname": "{{hostname}}", - {{/hostname}} - {{#port}} "servicePort": {{port}}, - {{/port}} + {{/fqdn}} "shareNodes": true }, {{/pool_members}} @@ -94,6 +106,10 @@ template: | "monitors": [ "https" ] + }, + "{{app_name}}-tcp": { + "idleTimeout": {{idleTimeout}}, + "class": "TCP_Profile" } } } diff --git a/templates/as3/SSL_BRIDGE.yaml b/templates/as3/SSL_BRIDGE.yaml index 0928584..faac1f6 100644 --- a/templates/as3/SSL_BRIDGE.yaml +++ b/templates/as3/SSL_BRIDGE.yaml @@ -1,6 +1,9 @@ -title: SSL Bridge Passthrough TCP Application -description: Layer 4 SSL Passthrough TCP Application - Includes Persistence +title: SSL Bridge Passthrough TCP Application Template +description: Layer 4 SSL Passthrough TCP Application - Includes Persistence 24-OCT-2024 contentType: application/json +definitions: + idleTimeout: + default: 300 template: | { "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", @@ -14,6 +17,12 @@ template: | "{{app_name}}-app": { "class": "Application", "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, "{{app_name}}": { "layer4": "tcp", "pool": "pool_{{app_name}}", @@ -28,8 +37,11 @@ template: | {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} {{#NONE}}"persistenceMethods" : [],{{/NONE}} {{/persistence}} + "profileTCP": { + "use": "{{app_name}}-tcp" + }, "virtualAddresses": [ - "{{virtual_address}}" + { "use": "sa_{{virtual_address}}" } ], "virtualPort": {{virtual_port}}, "snat": "auto" @@ -39,15 +51,15 @@ template: | {{#pool_members}} { {{#address}} + "hostname": {{name}}, "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, {{/address}} - {{#hostname}} + {{#fqdn}} "addressDiscovery": "fqdn", "hostname": "{{hostname}}", - {{/hostname}} - {{#port}} "servicePort": {{port}}, - {{/port}} + {{/fqdn}} "shareNodes": true }, {{/pool_members}} @@ -69,6 +81,10 @@ template: | "loadBalancingMode":"least-connections-member", {{/lbMethod}} "class": "Pool" + }, + "{{app_name}}-tcp": { + "idleTimeout": {{idleTimeout}}, + "class": "TCP_Profile" } } } diff --git a/templates/as3/TCP.yaml b/templates/as3/TCP.yaml index 32aaf75..88baac1 100644 --- a/templates/as3/TCP.yaml +++ b/templates/as3/TCP.yaml @@ -1,6 +1,9 @@ title: Simple TCP Application -description: Layer 4 Passthrough TCP Application - Includes Persistence +description: Layer 4 Passthrough TCP Application - Includes Persistence 22-OCT-2024 contentType: application/json +definitions: + idleTimeout: + default: 300 template: | { "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", @@ -14,22 +17,31 @@ template: | "{{app_name}}-app": { "class": "Application", "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, "{{app_name}}": { "layer4": "tcp", "pool": "pool_{{app_name}}", "translateServerAddress": true, "translateServerPort": true, "class": "Service_TCP", + {{#comment}} + "description": {{comment}}, + {{/comment}} {{#persistence}} {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}],{{/SOURCEIP}} {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} {{#NONE}}"persistenceMethods" : [],{{/NONE}} {{/persistence}} "profileTCP": { - "use": "{{app_name}}-tcp_idle_9000" + "use": "{{app_name}}-tcp" }, "virtualAddresses": [ - "{{virtual_address}}" + { "use": "sa_{{virtual_address}}" } ], "virtualPort": {{virtual_port}}, "snat": "auto" @@ -39,15 +51,15 @@ template: | {{#pool_members}} { {{#address}} + "hostname": {{name}}, "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, {{/address}} - {{#hostname}} + {{#fqdn}} "addressDiscovery": "fqdn", "hostname": "{{hostname}}", - {{/hostname}} - {{#port}} "servicePort": {{port}}, - {{/port}} + {{/fqdn}} "shareNodes": true }, {{/pool_members}} @@ -70,10 +82,10 @@ template: | {{/lbMethod}} "class": "Pool" }, - "{{app_name}}-tcp_idle_9000": { - "idleTimeout": 9000, + "{{app_name}}-tcp": { + "idleTimeout": 300, "class": "TCP_Profile" - } + } } } } diff --git a/templates/as3/TFTP.yaml b/templates/as3/TFTP.yaml new file mode 100644 index 0000000..072b5ce --- /dev/null +++ b/templates/as3/TFTP.yaml @@ -0,0 +1,98 @@ +title: Simple TFTP Application +description: TFTP Application - Includes Persistence and TCP Timers 24-OCT-2024 +contentType: application/json +definitions: + idleTimeout: + default: 300 +template: | + { + "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", + "class": "AS3", + "declaration": { + "class": "ADC", + "schemaVersion": "3.45.0", + "id": "urn:uuid:8cbab3f1-de39-4944-9459-cea0b3e7f69c", + "t_{{virtual_address}}": { + "class": "Tenant", + "{{app_name}}-app": { + "class": "Application", + "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, + "{{app_name}}": { + "layer4": "udp", + "pool": "pool_{{app_name}}", + "translateServerAddress": true, + "translateServerPort": true, + "class": "Service_UDP", + {{#comment}} + "description": {{comment}}, + {{/comment}} + {{#persistence}} + {{#SOURCEIP}}"persistenceMethods": [{"bigip": "/Common/source_addr"}],{{/SOURCEIP}} + {{#SSLSESSION}}"persistenceMethods": [{"bigip": "/Common/ssl"}],{{/SSLSESSION}} + {{#NONE}}"persistenceMethods" : [],{{/NONE}} + {{/persistence}} + "profileUDP": { + "use": "{{app_name}}-udp" + }, + "profileTFTP": { + "use": "{{app_name}}-udp" + }, + "virtualAddresses": [ + { "use": "sa_{{virtual_address}}" } + ], + "virtualPort": {{virtual_port}}, + "snat": "auto" + }, + "pool_{{app_name}}": { + "members": [ + {{#pool_members}} + { + {{#address}} + "hostname": {{name}}, + "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, + {{/address}} + {{#fqdn}} + "addressDiscovery": "fqdn", + "hostname": "{{hostname}}", + "servicePort": {{port}}, + {{/fqdn}} + "shareNodes": true + }, + {{/pool_members}} + ], + "monitors": [ + { + "bigip": "/Common/tcp" + } + ], + {{#lbMethod}} + {{#ROUNDROBIN}} + "loadBalancingMode":"round-robin", + {{/ROUNDROBIN}} + {{#LEASTCONNECTION}} + "loadBalancingMode":"least-connections-member", + {{/LEASTCONNECTION}} + {{/lbMethod}} + {{^lbMethod}} + "loadBalancingMode":"least-connections-member", + {{/lbMethod}} + "class": "Pool" + }, + "{{app_name}}-udp": { + "idleTimeout": {{idleTimeout}}, + "class": "UDP_Profile" + }, + "{{app_name}}-tftp": { + "class": "TFTP_Profile" + } + } + } + } + } \ No newline at end of file diff --git a/templates/as3/UDP.yaml b/templates/as3/UDP.yaml index 13ae259..34334fa 100644 --- a/templates/as3/UDP.yaml +++ b/templates/as3/UDP.yaml @@ -1,6 +1,9 @@ -title: Simple UDP Application -description: Layer 4 Passthrough UDP Application - Includes Persistence +title: Simple UDP Application Template +description: Layer 4 Passthrough UDP Application - Includes Persistence 24-OCT-2024 contentType: application/json +definitions: + idleTimeout: + default: 300 template: | { "$schema": "https://raw.githubusercontent.com/F5Networks/f5-appsvcs-extension/master/schema/latest/as3-schema.json", @@ -16,6 +19,12 @@ template: | "{{app_name}}-app": { "class": "Application", "template": "shared", + "sa_{{virtual_address}}": { + "class": "Service_Address", + "icmpEcho": "enable", + "arpEnabled": true, + "virtualAddress": "{{virtual_address}}" + }, "{{app_name}}": { "layer4": "udp", "pool": "pool_{{app_name}}", @@ -23,29 +32,28 @@ template: | "translateServerPort": true, "class": "Service_UDP", "profileUDP": { - "bigip": "/Common/udp" + "use": "{{app_name}}-udp" }, "virtualAddresses": [ - "{{virtual_address}}" + { "use": "sa_{{virtual_address}}" } ], "virtualPort": {{virtual_port}}, "snat": "auto" }, "pool_{{app_name}}": { - "loadBalancingMode": "least-connections-member", "members": [ {{#pool_members}} { {{#address}} + "hostname": {{name}}, "serverAddresses": ["{{address}}"], + "servicePort": {{port}}, {{/address}} - {{#hostname}} + {{#fqdn}} "addressDiscovery": "fqdn", "hostname": "{{hostname}}", - {{/hostname}} - {{#port}} "servicePort": {{port}}, - {{/port}} + {{/fqdn}} "shareNodes": true }, {{/pool_members}} @@ -57,16 +65,20 @@ template: | ], {{#lbMethod}} {{#ROUNDROBIN}} - "loadBalancingMode":"round-robin", + "loadBalancingMode": "round-robin", {{/ROUNDROBIN}} {{#LEASTCONNECTION}} - "loadBalancingMode":"least-connections-member", + "loadBalancingMode": "least-connections-member", {{/LEASTCONNECTION}} {{/lbMethod}} {{^lbMethod}} - "loadBalancingMode":"least-connections-member", + "loadBalancingMode": "least-connections-member", {{/lbMethod}} "class": "Pool" + }, + "{{app_name}}-udp": { + "idleTimeout": {{idleTimeout}}, + "class": "UDP_Profile" } } } From f5376e2c620476b01ea6e6ee7becdeaef0f6078c Mon Sep 17 00:00:00 2001 From: DumpySquare Date: Fri, 6 Dec 2024 07:09:50 +0530 Subject: [PATCH 3/4] quote clean up --- src/ns2FastParams.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ns2FastParams.ts b/src/ns2FastParams.ts index ab64f83..596d399 100644 --- a/src/ns2FastParams.ts +++ b/src/ns2FastParams.ts @@ -13,8 +13,7 @@ import { * mutate ns app json to a form easier for FAST/mustache to work with * @param nsApp NS app as json */ -export function mungeNS2FAST(nsApp: AdcApp, rx: AdcRegExTree -) { +export function mungeNS2FAST(nsApp: AdcApp) { if (nsApp.fastTempParams) { From db1aa29e9569388bad52596e63dc53b0b83b586e Mon Sep 17 00:00:00 2001 From: DumpySquare Date: Wed, 11 Dec 2024 10:20:21 +0530 Subject: [PATCH 4/4] 12.11 work --- CHANGELOG.md | 6 +- f5_flipper_test.tgz | Bin 11039 -> 11042 bytes src/digLbVserver.ts | 309 ++++++++++++++++------------ src/models.ts | 12 +- src/parseAdcUtils.ts | 65 ++++-- src/regex.ts | 14 +- tests/007_parseNsOpts.unit.tests.ts | 108 ++++++++++ tests/024_service.unit.tests.ts | 34 +-- tests/031_sslCerts.unit.tests.ts | 2 +- tests/artifacts/apps/namaste.conf | 40 ++++ tests/artifacts/f5_flipper_test.tgz | Bin 11039 -> 11042 bytes tsconfig.json | 2 +- 12 files changed, 417 insertions(+), 175 deletions(-) create mode 100644 tests/007_parseNsOpts.unit.tests.ts create mode 100644 tests/artifacts/apps/namaste.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d81fc1..2b92059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,10 +17,12 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ### Fixed +[BUG] clean up quotes from strings with spaces #47 + --- -## [1.11.0] - (10-24-2024) +## [1.11.1] - (12-11-2024) ### Fixed @@ -29,6 +31,8 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how - Most of the time they are applied at the "service group" - [RFE] Processing vservers using IPv6 addresses #43 - [RFE] ns json output to main work flow #45 +- [BUG] options parsing breaks with spaces/quotes/special-chars #46 +- [BUG] not capturing DISABLED state of serviceGroup/service members #49 --- diff --git a/f5_flipper_test.tgz b/f5_flipper_test.tgz index 7279f893a437b550b71cb3b7656f7848dd79d513..98fd9b2ee4bd3fee01e96de536e4eb505c01676d 100644 GIT binary patch literal 11042 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 literal 11039 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 Z;&0El=iBq``S#G~{{z>zebfLF0RXKA)5rh- diff --git a/src/digLbVserver.ts b/src/digLbVserver.ts index 20c0e10..599f01b 100644 --- a/src/digLbVserver.ts +++ b/src/digLbVserver.ts @@ -59,20 +59,6 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { lines: [originalString] } - // app.name = app.name.replace(/"/g, '') - - // if (app.name === '"1 APPLE_443_HTTPS"') { - - // debugger; - // const x = RegExp(/\w+|"[\w\s]*"/); - // const y = x.test(app.name) - // const v = app.name.split(/\w+|"[\w\s]*"/gm) - // const z = y; - // } - - // function nameFilter() - - // start with 'bind lb vserver' // todo: update this filter to accomodate names with spaces, see above; @@ -103,66 +89,72 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { const serviceName = rxMatch.groups?.service; // dig "add service with supporting bind service lines" + await digService(serviceName, app, coa, rx) // dig service details -> do we have a service with this name? // there should only be one "add service" with this name since we are looking in this specific "bind lb vserver" - const serviceD = coa.add?.service?.filter(s => { - const name = serviceName; - const addServices = coa.add.service; - // pull out the app name from the binding - const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; - // does the app name match the bind lb object? - const y = name === sname; - return y; - }); - for await (const x of serviceD) { - const parent = 'add service'; - const originalString = parent + ' ' + serviceD; - app.lines.push(originalString); - const rxMatch = x.match(rx.parents[parent]) - const opts = parseNsOptions(rxMatch.groups?.opts, rx); - if (!rxMatch) { - /* istanbul ignore next */ - return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); - } - - // if we have service details create array to put them - if (!app.bindings.service) { - app.bindings.service = []; - } - - let serviceDetails = { - name: serviceName, - protocol: rxMatch.groups.protocol, - port: rxMatch.groups.port, - server: rxMatch.groups.server, - opts - } - - // todo: is this where we should dig the service binding options for -monitor references? - - // dig "bind service ..." - await digBindService(serviceName, app, coa, rx) - - - // dig "bind ssl service ..." - await digBindSslService(serviceName, app, coa, rx) - - // also get server reference under 'add server ' - if (rxMatch.groups.server) { - - // dig server from serviceGroup server reference - await digServer(rxMatch.groups.server, app, coa, rx) - .then(i => { - if (i) { - serviceDetails = Object.assign(serviceDetails, i) - } - }) - } - - // push service details to app config - app.bindings.service.push(serviceDetails) - } + // const serviceD = coa.add?.service?.filter(s => { + // const name = serviceName; + // const addServices = coa.add.service; + // // pull out the app name from the binding + // const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // // does the app name match the bind lb object? + // const y = name === sname; + // return y; + // }); + + + // if(serviceD.length > 0) { + + // for await (const x of serviceD) { + // const parent = 'add service'; + // const originalString = parent + ' ' + serviceD; + // app.lines.push(originalString); + // const rxMatch = x.match(rx.parents[parent]) + // const opts = parseNsOptions(rxMatch.groups?.opts, rx); + // if (!rxMatch) { + // /* istanbul ignore next */ + // return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + // } + + // // if we have service details create array to put them + // if (!app.bindings.service) { + // app.bindings.service = []; + // } + + // let serviceDetails = { + // name: serviceName, + // protocol: rxMatch.groups.protocol, + // port: rxMatch.groups.port, + // server: rxMatch.groups.server, + // opts + // } + + // // todo: is this where we should dig the service binding options for -monitor references? + + // // dig "bind service ..." + // await digBindService(serviceName, app, coa, rx) + + + // // dig "bind ssl service ..." + // await digBindSslService(serviceName, app, coa, rx) + + // // also get server reference under 'add server ' + // if (rxMatch.groups.server) { + + // // dig server from serviceGroup server reference + // await digServer(rxMatch.groups.server, app, coa, rx) + // .then(i => { + // if (i) { + // serviceDetails = Object.assign(serviceDetails, i) + // } + // }) + // } + + // // push service details to app config + // app.bindings.service.push(serviceDetails) + // } + // } // dig serviceGroup details await digServiceGroup(serviceName, app, coa, rx) @@ -172,6 +164,8 @@ export async function digLbVserver(coa: AdcConfObj, rx: AdcRegExTree) { const opts = parseNsOptions(rxMatch.groups?.opts, rx); if (opts['-policyName']) { const pName = opts['-policyName']; + + // initialize the policy array if (!app.bindings['-policyName']) { app.bindings['-policyName'] = []; } @@ -206,7 +200,7 @@ export async function digBindService(serviceName: string, app: AdcApp, obj: AdcC const bindServicesList = obj.bind.service; const sName = s.split(' ')[0] === serviceName - + return sName; }) @@ -297,11 +291,11 @@ export async function digBindService(serviceName: string, app: AdcApp, obj: AdcC */ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { + // todo: add support for spaces in names + const rwPolicies = obj.add?.rewrite?.policy?.filter(s => s.split(' ')[0] === name) - const policies = obj.add?.rewrite?.policy?.filter(s => s.split(' ')[0] === name) - - if (policies?.length > 0) { - for await (const x of policies) { + if (rwPolicies?.length > 0) { + for await (const x of rwPolicies) { const parent = 'add rewrite policy'; const originalString = parent + ' ' + x; const rxMatch = x.match(rx.parents[parent]); @@ -313,6 +307,24 @@ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: app.lines.push(originalString); } } + + const rsPolicies = obj.add?.responder?.policy?.filter(s => s.split(' ')[0] === name) + + if (rsPolicies?.length > 0) { + for await (const x of rsPolicies) { + const parent = 'add responder policy'; + const originalString = parent + ' ' + x; + const rxMatch = x.match(rx.parents[parent]); + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); + } + const opts = parseNsOptions(rxMatch.groups?.opts, rx); + + // todo: dig the responder policie actions from namaste app + app.lines.push(originalString); + } + } } @@ -324,7 +336,7 @@ export async function digPolicy(name: string, app: AdcApp, obj: AdcConfObj, rx: * @param obj * @param rx */ -export async function digService(serviceName: string, app: AdcApp, obj: AdcConfObj, rx: AdcRegExTree) { +export async function digService(serviceName: string, app: AdcApp, coa: AdcConfObj, rx: AdcRegExTree) { // this should be a single service name @@ -336,7 +348,7 @@ export async function digService(serviceName: string, app: AdcApp, obj: AdcConfO // filter out the services (single) we need // - const serviceD = obj.add?.service?.filter(s => { + const serviceD = coa.add?.service?.filter(s => { const name = serviceName; // pull out the app name from the binding const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; @@ -368,6 +380,27 @@ export async function digService(serviceName: string, app: AdcApp, obj: AdcConfO server: rxMatch.groups.server, opts } + + // dig "bind service ..." + await digBindService(serviceName, app, coa, rx) + + // dig "bind ssl service ..." + await digBindSslService(serviceName, app, coa, rx) + + // also get server reference under 'add server ' + if (rxMatch.groups.server) { + + // dig server from serviceGroup server reference + await digServer(rxMatch.groups.server, app, coa, rx) + .then(i => { + if (i) { + serviceDetails = Object.assign(serviceDetails, i) + } + }) + } + + // push service details to app config + app.bindings.service.push(serviceDetails) } } @@ -386,74 +419,100 @@ export async function digServiceGroup(serviceName: string, app: AdcApp, obj: Adc const serviceGroup: any = {}; // 'add serviceGroup ' - const sgs = obj.add?.serviceGroup?.filter(s => s.split(' ')[0] === serviceName) + const serviceGroupString = obj.add?.serviceGroup?.filter(s => { + const name = serviceName; + // pull out the app name from the binding + const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === sname; + return y; + })[0] + // should produce one since each "add serviceGroup" will be unique // there is a 1:many with "add serviceGroup" to "bind serviceGroup" - if (sgs?.length > 0) { + if (serviceGroupString) { - for await (const x of sgs) { - const parent = 'add serviceGroup'; - const originalString = parent + ' ' + x; - const rxMatch = x.match(rx.parents[parent]) - if (!rxMatch) { - /* istanbul ignore next */ - return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); - } - const opts = parseNsOptions(rxMatch.groups?.opts, rx); - app.lines.push(originalString); - serviceGroup.name = rxMatch.groups.name; - serviceGroup.protocol = rxMatch.groups.protocol; - deepmergeInto(serviceGroup, opts) + const parent = 'add serviceGroup'; + const originalString = parent + ' ' + serviceGroupString; + const rxMatch = serviceGroupString.match(rx.parents[parent]) + if (!rxMatch) { + /* istanbul ignore next */ + return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } + const opts = parseNsOptions(rxMatch.groups?.opts, rx); + app.lines.push(originalString); + serviceGroup.name = rxMatch.groups.name; + serviceGroup.protocol = rxMatch.groups.protocol; + deepmergeInto(serviceGroup, opts) } // 'bind serviceGroup ' - obj.bind?.serviceGroup?.filter(s => s.split(' ')[0] === serviceName) - .forEach(async x => { + // can have multiple bingings + const serviceGroupBindings = obj.bind?.serviceGroup?.filter(s => { + const name = serviceName; + // pull out the app name from the binding + const sname = s.match(/^(?("[\w.\- ]+"|[\w.\-]+)) (?("[\w.\- ]+"|[\w.\-]+))?/)?.groups?.name; + // does the app name match the bind lb object? + const y = name === sname; + return y; + }) + if(serviceGroupBindings?.length > 0) { + + for await (const x of serviceGroupBindings) { const parent = 'bind serviceGroup'; const originalString = parent + ' ' + x; + // demo/test at following site + // https://regex101.com/r/uEGKaI/1 const rxMatch = x.match(rx.parents[parent]) - + if (!rxMatch) { /* istanbul ignore next */ return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } - + const sgbOpts = parseNsOptions(rxMatch?.groups?.opts, rx) + app.lines.push(originalString); - if (rxMatch.groups.serv) { - - - const memberRef = rxMatch.groups.serv.split(' '); - + if (rxMatch?.groups?.serv) { + + // const memberRef = rxMatch.groups.serv.split(' '); + const sgMemberName = rxMatch.groups.serv; + const sgMemberPort = rxMatch.groups.port; + + const sgmOpts = parseNsOptions(rxMatch.groups.mbrOpts, rx) + // dig server from serviceGroup server reference - await digServer(memberRef[0], app, obj, rx) + await digServer(sgMemberName, app, obj, rx) .then(i => { - + const serverDetails = { - name: memberRef[0], - port: memberRef[1] + name: sgMemberName, + port: sgMemberPort } + if (!serviceGroup.servers) serviceGroup.servers = [] - - serviceGroup.servers.push(Object.assign(serverDetails, i)) + // merge all the details together and push to app.json + serviceGroup.servers.push( + Object.assign(serverDetails, i, sgmOpts) + ) }) - - - } else if (rxMatch.groups.monitor) { - - const monitorName = rxMatch.groups.monitor.split(' ').pop(); - + + + } else if (sgbOpts["-monitorName"]) { + + const monitorName = sgbOpts["-monitorName"]; + // add the object param/array if not already there if (!serviceGroup.monitors) serviceGroup.monitors = []; - + //todo: get a list of the default monitor names and add them to the config somehow - + // create the serviceGroup monitor object with the name const monitorObj = { name: monitorName }; - + // get monitor config line + // todo: add support for monitors with spaces in name obj.add?.lb?.monitor?.filter(m => m.split(' ')[0] === monitorName) .forEach(x => { const parent = 'add lb monitor'; @@ -465,28 +524,24 @@ export async function digServiceGroup(serviceName: string, app: AdcApp, obj: Adc return logger.error(`regex "${rx.parents[parent]}" - failed for line "${originalString}"`); } const opts = parseNsOptions(rxMatch.groups.opts, rx); - + // add any monitor object options deepmergeInto(monitorObj, opts) }) - + // push the full monitor object to the serviceGroup serviceGroup.monitors.push(monitorObj); - - } else if (rxMatch.groups.opts) { - deepmergeInto( - serviceGroup, - parseNsOptions(rxMatch.groups.opts, rx) - ) + } - }) + } + } + // if we discovered serviceGroup details, push them to the app.json if (Object.keys(serviceGroup).length > 0) { if (!app.bindings.serviceGroup) app.bindings.serviceGroup = []; app.bindings.serviceGroup.push(serviceGroup) } - // sortNsLines(app.lines, rx) digSslBinding(app, obj, rx) return; diff --git a/src/models.ts b/src/models.ts index 52129e4..5dd1c73 100644 --- a/src/models.ts +++ b/src/models.ts @@ -216,8 +216,8 @@ export type Stats = { export type AdcRegExTree = { adcVersion: RegExp; adcBuild: RegExp; - cfgOptions: RegExp; - cfgOptionsQuotes: RegExp; + // cfgOptions: RegExp; + // cfgOptionsQuotes: RegExp; verbs: RegExp; trimQuotes: RegExp; parents: { @@ -238,8 +238,10 @@ export type AdcRegExTree = { 'add gslb vserver': RegExp; 'add gslb service': RegExp; 'add gslb site': RegExp; - 'add rewrite action': RegExp; 'add rewrite policy': RegExp; + 'add rewrite action': RegExp; + 'add responder policy': RegExp; + 'add responder action': RegExp; 'add appflow policy': RegExp; 'add appflow action': RegExp; 'add appflow collector': RegExp; @@ -287,8 +289,12 @@ export type AdcConfObj = { service?: string[]; } rewrite?: { + policy?: string[]; action?: string[]; + }; + responder?: { policy?: string[]; + action?: string[]; }; cache?: string; dns?: { diff --git a/src/parseAdcUtils.ts b/src/parseAdcUtils.ts index 2f98806..2545469 100644 --- a/src/parseAdcUtils.ts +++ b/src/parseAdcUtils.ts @@ -17,38 +17,65 @@ import { AdcRegExTree } from "./models"; * @param rx regex tree for specific ns adc version * @returns options as an object */ -export function parseNsOptions(str: string, rx: AdcRegExTree): { [k: string]: string } { +export function parseNsOptions(str: string = "", rx: AdcRegExTree): { [k: string]: string } { const obj = {} - // grep out all the options with quotes/spaces - str.match(rx.cfgOptionsQuotes)?.forEach(el => { + // if(str === undefined) return obj; + + // 12.11.2024: this is a hack to get the regex working for now. + // The current rx doesn't pick up the last "-key value" since it uses forward lookups. + str = str.concat(" -devno 12345") + + // grep out all the options with quotes/spaces/normal + // tested with https://regex101.com/r/WCU928/1 + const matches = str.match(/(?-\S+) (?.*?) (?=-\S+)/g); + + matches?.forEach(el => { // split the name off by the first space - const [k, v] = el.split(/ (.*)/) - obj[k] = v; - str = str.replace(el, '') - }) + const k = el.substring(0, el.indexOf(' ')); + // everything after the first space and trim any trailing white space + const v = el.substring(el.indexOf(' ') + 1).trimEnd().replaceAll(/^\"|\"$/g, ""); - // capture everything else without spaces - str.match(rx.cfgOptions)?.forEach(el => { - const [k, v] = el.split(' ') - if (k === '-devno') { - // no nothing, devno is not needed + if(k === '-devno') { + + // skip adding it to the return object } else { - // add to object - obj[k] = v; - str = str.replace(el, '') + + obj[k] = trimQuotes(v); } + str = str.replace(el, '') }) - // // turn certain object values to arrays - // if () { - - // } + // only thing left in the string should be '-devno 123456' + // todo: add some logic to check if other things are left outside -devno and log those details for visibility return obj; } +/** + * detects and trims quotes at the beginning and end of string + * @param s string + * @returns + */ +export function trimQuotes(s: string): string { + + // what is the index of the first " + const first = s.indexOf('"'); + // what is the index of the last " + const last = s.lastIndexOf('"'); + // get the total length of string + const stringL = s.length; + + // Do we have a quote at the beginning and end? + if(first === 0 && last === s.length-1) { + // return the string between the first and last char (") + s = s.substring(1, s.length-1) + } + return s; +} + + /** * sort ns adc config by verbs * add -> set -> bind -> link -> enable -> disable diff --git a/src/regex.ts b/src/regex.ts index 5052c83..9b10937 100644 --- a/src/regex.ts +++ b/src/regex.ts @@ -43,8 +43,8 @@ export class RegExTree { * example; "set ns config -IPAddress 192.168.86.140 -netmask 255.255.255.0" * captures ['-IPAddress 192.168.86.140', '-netmask 255.255.255.0'] */ - public cfgOptions = /-\w+ \S+/g; - public cfgOptionsQuotes = /-\w+ "[\S ]+"/g; + // public cfgOptions = /-\w+ \S+/g; + // public cfgOptionsQuotes = /-\w+ "[\S ]+"/g; private ipAddr = /(?:[0-9]{1,3}\.){3}[0-9]{1,3}/; @@ -54,8 +54,8 @@ export class RegExTree { private regexTree: AdcRegExTree = { adcVersion: this.adcVersionBaseReg, adcBuild: this.adcVersionBuildReg, - cfgOptions: this.cfgOptions, - cfgOptionsQuotes: this.cfgOptionsQuotes, + // cfgOptions: this.cfgOptions, + // cfgOptionsQuotes: this.cfgOptionsQuotes, verbs: /^(add|set|bind|link|enable|disable) /, trimQuotes: /^"(.*)"$/, parents: { @@ -76,8 +76,10 @@ export class RegExTree { 'add gslb vserver': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\S ]+)/, 'add gslb service': /(?("[\S ]+"|[\S]+)) (?\S+) (?\S+) (?(\d+|\*)) (?[\S ]+)/, 'add gslb site': /(?("[\S ]+"|[\S]+)) (?\S+) (?[\S ]+)/, - 'add rewrite action': /(?\S+) (?[\S ]+)/, 'add rewrite policy': /(?\S+) (?[\S ]+)/, + 'add rewrite action': /(?\S+) (?[\S ]+)/, + 'add responder policy': /(?\S+) (?[\S ]+)/, + 'add responder action': /(?\S+) (?[\S ]+)/, 'add appflow policy': /(?\S+) (?[\S]+) (?[\S]+)/, 'add appflow action': /(?\S+) (?[\S ]+)/, 'add appflow collector': /(?\S+) (?[\S ]+)/, @@ -88,7 +90,7 @@ export class RegExTree { 'set ns hostName': /(?[\S ]+)/, 'set gslb vserver': /(?\S+) (?[\S ]+)/, 'bind service': /(?("[\S ]+"|[\S]+)) ((?\S+ (\d+|\*))|(?-monitorName \S+)|(?[\S ]+))/, - 'bind serviceGroup': /(?("[\S ]+"|[\S]+)) ((?\S+ (\d+|\*))|(?-monitorName \S+)|(?[\S ]+))/, + 'bind serviceGroup': /(?("[\S ]+"|[\S]+)) ((?\S+) (?\d+|\*))?(?[\S ]+)?/, 'bind lb vserver': /(?("[\S ]+"|[\S]+)) ((?-[\S ]+)|(?("[\S ]+"|[\S]+)))/, 'bind cs vserver': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, 'bind ssl service': /(?("[\S ]+"|[\S]+)) (?[\S ]+)/, diff --git a/tests/007_parseNsOpts.unit.tests.ts b/tests/007_parseNsOpts.unit.tests.ts new file mode 100644 index 0000000..6b07448 --- /dev/null +++ b/tests/007_parseNsOpts.unit.tests.ts @@ -0,0 +1,108 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* + * Copyright 2020. F5 Networks, Inc. See End User License Agreement ("EULA") for + * license terms. Notwithstanding anything to the contrary in the EULA, Licensee + * may copy and modify this software product for its internal business purposes. + * Further, Licensee may upload, publish and distribute the modified version of + * the software product on devcentral.f5.com. + */ + +'use strict'; + +import assert from 'assert'; +import { RegExTree } from '../src/regex'; +import { parseNsOptions } from '../src/parseAdcUtils'; + +const events = []; +const parsedFileEvents: any[] = [] +const parsedObjEvents: any[] = [] +const rx = new RegExTree().get('13.1'); + +describe('parse NS options function tests', function () { + + + + + before(async function () { + // log test file name - makes it easer for troubleshooting + console.log(' file:', __filename) + + // clear the events arrays + parsedFileEvents.length = 0 + parsedObjEvents.length = 0 + + }); + + afterEach(function () { + events.length = 0; + }) + + + + // it(`regular options single`, async () => { + + // const items = [ + // 'add server sprout135A_groot 192.168.160.120 -devno 108847', + // 'add server sprout135c_groot 192.168.160.69 -devno 108848', + // 'add server dorsal-nedc 10.8.101.46 -comment "automated deployment"', + // 'add server dorsal-swdc 10.12.101.46 -comment "automated deployment"', + // 'add server stpvec1 stpvec1.f5flipper.com -comment "automated deployment"', + // 'add server stpvec2 stpvec2.f5flipper.com -comment "automated deployment"', + // ]; + + // // strip off all the leading parent object details 'add server ' + // const slim = items.map(x => x.replace('add server ', '')) + + // const rxMatches = slim.map(x => x.match(rx.parents['add server'])) + + // const misses = rxMatches.filter(x => x === undefined) + + // assert.ok(misses.length === 0, 'should not have any rx misses'); + // }) + + it(`lb monitor options complicated`, async () => { + + // this test should accomodate all options on all NS configs. + // just happened to be monitors when this needed to get worked out. + // so add any other config lines with options and add additional tests as needed + + const items = [ + 'add lb monitor test01_http_ecv_mon HTTP-ECV -customHeaders "Host:flippy.doda.com\\r\\n" -send "GET /HealthCheck" -recv "\\\"Version\\\"" -LRTM DISABLED -secure YES -devno 12357', + 'add lb monitor http-custom-8202_mon HTTP -respCode 306-307 -httpRequest "HEAD /" -LRTM DISABLED -interval 10 -resptimeout 5 -destPort 8202 -secure YES -devno 12355', + 'add lb monitor test02_http_80_mon HTTP -respCode 200 -httpRequest "HEAD /to/know/all" -LRTM DISABLED -devno 12345', + 'add lb monitor basic_tcp_monitor TCP -LRTM DISABLED -interval 10 -resptimeout 5 -secure YES -devno 12356', + 'add lb monitor "test with spaces http mon" HTTP-ECV -send "GET /some/complicated/name" -recv OK -LRTM DISABLED -destPort 8081 -devno 12365', + 'add lb monitor redirect_http-mon HTTP -respCode 301 -httpRequest "HEAD /artifactory" -LRTM DISABLED -secure YES -devno 12366', + 'add lb monitor http200_mon HTTP -respCode 200 -httpRequest "GET /api/v1/system/health" -LRTM DISABLED -destPort 8082 -devno 12367', + 'add lb monitor kaizen_http1.1_mon HTTP-ECV -customHeaders "Host:modern.samurai.chi\\r\\n" -send "GET /focusReady" -recv "\"zen\"" -LRTM DISABLED -secure YES -devno 12363', + ]; + + // cut down array of monitors + // these shorter versions better represent what the function will actually see during processing + const slim: string[] = [] + + // strip off all the leading parent object details 'add lb monitor ... ... ' + for await(const x of items) { + //find the index of the first opt "-\S+" + const firstOptIdx = x.match(/ -\S+ /)?.index || 0; + // return the rest of the string from the first match index + const restOfString = x.substring(firstOptIdx); + slim.push(restOfString); + } + + // loop through the array and parse all the ns options + const optsObx = slim.map(i => parseNsOptions(i, rx)); + + assert.deepStrictEqual("Host:flippy.doda.com\\r\\n", optsObx[0]['-customHeaders']); + assert.deepStrictEqual('306-307', optsObx[1]['-respCode']); + assert.deepStrictEqual('HEAD /to/know/all', optsObx[2]['-httpRequest']); + assert.deepStrictEqual('YES', optsObx[3]['-secure']); + assert.deepStrictEqual('8081', optsObx[4]['-destPort']); + assert.deepStrictEqual('DISABLED', optsObx[5]['-LRTM']); + assert.deepStrictEqual("GET /api/v1/system/health", optsObx[6]['-httpRequest']); + assert.deepStrictEqual("Host:modern.samurai.chi\\r\\n", optsObx[7]['-customHeaders']); + assert.deepStrictEqual("GET /focusReady", optsObx[7]['-send']); + }) + + +}); \ No newline at end of file diff --git a/tests/024_service.unit.tests.ts b/tests/024_service.unit.tests.ts index d78149b..add2372 100644 --- a/tests/024_service.unit.tests.ts +++ b/tests/024_service.unit.tests.ts @@ -69,7 +69,7 @@ describe('service abstraction tests', function () { assert.deepStrictEqual(app!.bindings!.service!.length, 3, "should have three service bindings") assert.deepStrictEqual(app!.lines!.length, 16, "should have 16 total lines of ns config") - + }) it(`basic service reference non ssl with monitor`, async () => { @@ -81,7 +81,7 @@ describe('service abstraction tests', function () { assert.deepStrictEqual(app!.bindings!.service!.length, 1, "should have three service bindings") assert.deepStrictEqual(app!.lines!.length, 6, "should have 16 total lines of ns config") - + }) it(`basic service reference ssl`, async () => { @@ -93,7 +93,7 @@ describe('service abstraction tests', function () { assert.deepStrictEqual(app!.bindings!.service!.length, 1, "should have one service bindings") assert.deepStrictEqual(app!.lines!.length, 9, "should have 16 total lines of ns config") - + }) @@ -115,27 +115,27 @@ describe('service abstraction tests', function () { port: "82", server: "SERVERCORE1", opts: { - "-gslb": "NONE", - "-maxClient": "0", - "-maxReq": "0", - "-cip": "ENABLED", - "-ip": "-usip", - "-useproxyport": "YES", - "-sp": "OFF", - "-cltTimeout": "180", - "-svrTimeout": "360", - "-CKA": "NO", - "-TCPB": "NO", - "-CMP": "YES", + "-gslb": "NONE", + "-maxClient": "0", + "-maxReq": "0", + "-cip": "ENABLED client-ip", + "-usip": "NO", + "-useproxyport": "YES", + "-sp": "OFF", + "-cltTimeout": "180", + "-svrTimeout": "360", + "-CKA": "NO", + "-TCPB": "NO", + "-CMP": "YES", }, hostname: "sevcore1.jonny.dev", - }, "should have three service bindings") + }, "should have three service bindings") const addressService = appServices!.filter(x => x.address === "10.240.21.115")[0] // this just confirms that we got back the service with the right "address" and "name" assert.deepStrictEqual(addressService.name, "FUJI02_HTTPS_SVC") - + }) diff --git a/tests/031_sslCerts.unit.tests.ts b/tests/031_sslCerts.unit.tests.ts index 1e94218..31c7b7b 100644 --- a/tests/031_sslCerts.unit.tests.ts +++ b/tests/031_sslCerts.unit.tests.ts @@ -75,7 +75,7 @@ describe('ssl certificate tests', function () { "-key": "www.star.groot_2022.pfx", "-inform": "PFX", "-passcrypt": "XXXX", - "-encrypted": "-encryptmethod", + "-encrypted": "-encryptmethod ENCMTHD_3", profileName: "star.groot.cer", }) diff --git a/tests/artifacts/apps/namaste.conf b/tests/artifacts/apps/namaste.conf new file mode 100644 index 0000000..862a8d8 --- /dev/null +++ b/tests/artifacts/apps/namaste.conf @@ -0,0 +1,40 @@ +### "namaste 443 vip" ########## - Hover for more details - ########## +### 27 lines +### object names with spaces, serviceGroup members DISABLED, SSL-Bridge, full health monitor, service policy +### todo: add parsing of the "-state DISABLED" on the serviceGroup members + +add lb vserver "namaste 443 vip" SSL 10.240.18.68 443 -persistenceType NONE -lbMethod ROUNDROBIN -backupLBMethod LEASTCONNECTION -cltTimeout 180 -devno 50003968 +bind lb vserver "namaste 443 vip" "namaste 8443 svg" +add serviceGroup "namaste 8443 svg" SSL -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP NO -devno 47218688 +bind serviceGroup "namaste 8443 svg" lotus1.yoga.in 8443 -devno 62357504 +add server lotus1.yoga.in 10.240.20.64 -devno 11415 +bind serviceGroup "namaste 8443 svg" lotus2.yoga.in 8443 -devno 62390272 +add server lotus2.yoga.in 10.240.20.71 -devno 11416 +bind serviceGroup "namaste 8443 svg" lotus3.yoga.in 8443 -devno 62423040 +add server lotus3.yoga.in 10.240.20.72 -devno 11417 + +bind serviceGroup "namaste 8443 svg" dragonfly1.yoga.in 8443 -state DISABLED -devno 62521344 +add server dragonfly1.yoga.in 10.240.24.215 -devno 11476 +bind serviceGroup "namaste 8443 svg" dragonfly2.yoga.in 8443 -state DISABLED -devno 62554112 +add server dragonfly2.yoga.in 10.240.24.225 -devno 11477 +bind serviceGroup "namaste 8443 svg" dragonfly3.yoga.in 8443 -state DISABLED -devno 62586880 +add server dragonfly3.yoga.in 10.240.24.226 -devno 11478 + + +bind serviceGroup "namaste 8443 svg" -monitorName namaste_custome_tcp_mon -devno 62685184 +add lb monitor namaste_custome_tcp_mon TCP -LRTM DISABLED -interval 30 -resptimeout 15 -secure YES -devno 12356 +bind serviceGroup "namaste 8443 svg" -monitorName namaste_awaken_http8443_mon -devno 72876032 +add lb monitor namaste_awaken_http8443_mon HTTP-ECV -send "GET /look/within" -recv "\"find\":love" -LRTM DISABLED -secure YES -devno 12369 + +bind ssl vserver "namaste 443 vip" -cipherName DEFAULT +bind ssl vserver "namaste 443 vip" -certkeyName sinsvault-new +bind ssl vserver "namaste 443 vip" -eccCurveName P_256 +bind ssl vserver "namaste 443 vip" -eccCurveName P_384 +bind ssl vserver "namaste 443 vip" -eccCurveName P_224 +bind ssl vserver "namaste 443 vip" -eccCurveName P_521 + +bind lb vserver "namaste 443 vip" -policyName namaste_443_rsp -priority 100 -gotoPriorityExpression END -type REQUEST + + +add responder policy namaste_443_rsp "HTTP.REQ.URL.EQ(\"/\")" namaste_443_rspa +add responder action namaste_443_rspa redirect "\"https://\" + HTTP.REQ.HOSTNAME.HTTP_URL_SAFE + \"/rebirth/enlightenment=true\"" -responseStatusCode 302 diff --git a/tests/artifacts/f5_flipper_test.tgz b/tests/artifacts/f5_flipper_test.tgz index 7279f893a437b550b71cb3b7656f7848dd79d513..98fd9b2ee4bd3fee01e96de536e4eb505c01676d 100644 GIT binary patch literal 11042 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 literal 11039 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 Z;&0El=iBq``S#G~{{z>zebfLF0RXKA)5rh- diff --git a/tsconfig.json b/tsconfig.json index add7760..ccafcf4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "module": "commonjs", "moduleResolution": "node", "removeComments": false,