"}},"componentScriptGroups({\"componentId\":\"custom.widget.Beta_Footer\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"component({\"componentId\":\"custom.widget.Tag_Manager_Helper\"})":{"__typename":"Component","render({\"context\":{\"component\":{\"entities\":[],\"props\":{}},\"page\":{\"entities\":[],\"name\":\"TagPage\",\"props\":{},\"url\":\"https://community.f5.com/tag/python\"}}})":{"__typename":"ComponentRenderResult","html":" "}},"componentScriptGroups({\"componentId\":\"custom.widget.Tag_Manager_Helper\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"component({\"componentId\":\"custom.widget.Consent_Blackbar\"})":{"__typename":"Component","render({\"context\":{\"component\":{\"entities\":[],\"props\":{}},\"page\":{\"entities\":[],\"name\":\"TagPage\",\"props\":{},\"url\":\"https://community.f5.com/tag/python\"}}})":{"__typename":"ComponentRenderResult","html":""}},"componentScriptGroups({\"componentId\":\"custom.widget.Consent_Blackbar\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/community/NavbarDropdownToggle\"]})":[{"__ref":"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageListTabs\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageListTabs-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageView/MessageViewInline\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageView/MessageViewInline-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/common/Pager/PagerLoadMore\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/common/Pager/PagerLoadMore-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/customComponent/CustomComponent\"]})":[{"__ref":"CachedAsset:text:en_US-components/customComponent/CustomComponent-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/common/OverflowNav\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/common/OverflowNav-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/users/UserLink\"]})":[{"__ref":"CachedAsset:text:en_US-components/users/UserLink-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageSubject\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageSubject-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageBody\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageBody-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageTime\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageTime-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeIcon\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeIcon-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageUnreadCount\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageUnreadCount-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageViewCount\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageViewCount-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/kudos/KudosCount\"]})":[{"__ref":"CachedAsset:text:en_US-components/kudos/KudosCount-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageRepliesCount\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageRepliesCount-1744046271000"}],"cachedText({\"lastModified\":\"1744046271000\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserAvatar\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1744046271000"}]},"Theme:customTheme1":{"__typename":"Theme","id":"customTheme1"},"User:user:-1":{"__typename":"User","id":"user:-1","uid":-1,"login":"Anonymous","email":"","avatar":null,"rank":null,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":"ANONYMOUS","registrationTime":null,"confirmEmailStatus":false,"registrationAccessLevel":"VIEW","ssoRegistrationFields":[]},"ssoId":null,"profileSettings":{"__typename":"ProfileSettings","dateDisplayStyle":{"__typename":"InheritableStringSettingWithPossibleValues","key":"layout.friendly_dates_enabled","value":"false","localValue":"true","possibleValues":["true","false"]},"dateDisplayFormat":{"__typename":"InheritableStringSetting","key":"layout.format_pattern_date","value":"dd-MMM-yyyy","localValue":"MM-dd-yyyy"},"language":{"__typename":"InheritableStringSettingWithPossibleValues","key":"profile.language","value":"en-US","localValue":null,"possibleValues":["en-US","es-ES"]},"repliesSortOrder":{"__typename":"InheritableStringSettingWithPossibleValues","key":"config.user_replies_sort_order","value":"DEFAULT","localValue":"DEFAULT","possibleValues":["DEFAULT","LIKES","PUBLISH_TIME","REVERSE_PUBLISH_TIME"]}},"deleted":false},"CachedAsset:pages-1744705076878":{"__typename":"CachedAsset","id":"pages-1744705076878","value":[{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetInvolved.MvpProgram","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved/mvp-program","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"BlogViewAllPostsPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId/all-posts/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CasePortalPage","type":"CASE_PORTAL","urlPath":"/caseportal","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CreateGroupHubPage","type":"GROUP_HUB","urlPath":"/groups/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CaseViewPage","type":"CASE_DETAILS","urlPath":"/case/:caseId/:caseNumber","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"InboxPage","type":"COMMUNITY","urlPath":"/inbox","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetInvolved.AdvocacyProgram","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved/advocacy-program","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetHelp.NonCustomer","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/non-customer","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HelpFAQPage","type":"COMMUNITY","urlPath":"/help","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetHelp.F5Customer","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/f5-customer","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"IdeaMessagePage","type":"IDEA_POST","urlPath":"/idea/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"IdeaViewAllIdeasPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/all-ideas/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"LoginPage","type":"USER","urlPath":"/signin","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"BlogPostPage","type":"BLOG","urlPath":"/category/:categoryId/blogs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetInvolved","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.Learn","type":"COMMUNITY","urlPath":"/c/how-do-i/learn","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1739501996000,"localOverride":null,"page":{"id":"Test","type":"CUSTOM","urlPath":"/custom-test-2","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ThemeEditorPage","type":"COMMUNITY","urlPath":"/designer/themes","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TkbViewAllArticlesPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId/all-articles/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"OccasionEditPage","type":"EVENT","urlPath":"/event/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"OAuthAuthorizationAllowPage","type":"USER","urlPath":"/auth/authorize/allow","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"PageEditorPage","type":"COMMUNITY","urlPath":"/designer/pages","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"PostPage","type":"COMMUNITY","urlPath":"/category/:categoryId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForumBoardPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TkbBoardPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"EventPostPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"UserBadgesPage","type":"COMMUNITY","urlPath":"/users/:login/:userId/badges","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"GroupHubMembershipAction","type":"GROUP_HUB","urlPath":"/membership/join/:nodeId/:membershipType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"MaintenancePage","type":"COMMUNITY","urlPath":"/maintenance","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"IdeaReplyPage","type":"IDEA_REPLY","urlPath":"/idea/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"UserSettingsPage","type":"USER","urlPath":"/mysettings/:userSettingsTab","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"GroupHubsPage","type":"GROUP_HUB","urlPath":"/groups","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForumPostPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"OccasionRsvpActionPage","type":"OCCASION","urlPath":"/event/:boardId/:messageSubject/:messageId/rsvp/:responseType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"VerifyUserEmailPage","type":"USER","urlPath":"/verifyemail/:userId/:verifyEmailToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"AllOccasionsPage","type":"OCCASION","urlPath":"/category/:categoryId/events/:boardId/all-events/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"EventBoardPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TkbReplyPage","type":"TKB_REPLY","urlPath":"/kb/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"IdeaBoardPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CommunityGuideLinesPage","type":"COMMUNITY","urlPath":"/communityguidelines","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CaseCreatePage","type":"SALESFORCE_CASE_CREATION","urlPath":"/caseportal/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TkbEditPage","type":"TKB","urlPath":"/kb/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForgotPasswordPage","type":"USER","urlPath":"/forgotpassword","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"IdeaEditPage","type":"IDEA","urlPath":"/idea/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TagPage","type":"COMMUNITY","urlPath":"/tag/:tagName","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"BlogBoardPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"OccasionMessagePage","type":"OCCASION_TOPIC","urlPath":"/event/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ManageContentPage","type":"COMMUNITY","urlPath":"/managecontent","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ClosedMembershipNodeNonMembersPage","type":"GROUP_HUB","urlPath":"/closedgroup/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetHelp.Community","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/community","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CommunityPage","type":"COMMUNITY","urlPath":"/","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetInvolved.ContributeCode","type":"COMMUNITY","urlPath":"/c/how-do-i/get-involved/contribute-code","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForumMessagePage","type":"FORUM_TOPIC","urlPath":"/discussions/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"IdeaPostPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"BlogMessagePage","type":"BLOG_ARTICLE","urlPath":"/blog/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"RegistrationPage","type":"USER","urlPath":"/register","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"EditGroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForumEditPage","type":"FORUM","urlPath":"/discussions/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ResetPasswordPage","type":"USER","urlPath":"/resetpassword/:userId/:resetPasswordToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TkbMessagePage","type":"TKB_ARTICLE","urlPath":"/kb/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.Learn.AboutIrules","type":"COMMUNITY","urlPath":"/c/how-do-i/learn/about-irules","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"BlogEditPage","type":"BLOG","urlPath":"/blog/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetHelp.F5Support","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/f5-support","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ManageUsersPage","type":"USER","urlPath":"/users/manage/:tab?/:manageUsersTab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForumReplyPage","type":"FORUM_REPLY","urlPath":"/discussions/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"PrivacyPolicyPage","type":"COMMUNITY","urlPath":"/privacypolicy","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"NotificationPage","type":"COMMUNITY","urlPath":"/notifications","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"UserPage","type":"USER","urlPath":"/users/:login/:userId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HealthCheckPage","type":"COMMUNITY","urlPath":"/health","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"OccasionReplyPage","type":"OCCASION_REPLY","urlPath":"/event/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ManageMembersPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/manage/:tab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"SearchResultsPage","type":"COMMUNITY","urlPath":"/search","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"BlogReplyPage","type":"BLOG_REPLY","urlPath":"/blog/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"GroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TermsOfServicePage","type":"COMMUNITY","urlPath":"/termsofservice","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetHelp","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI.GetHelp.SecurityIncident","type":"COMMUNITY","urlPath":"/c/how-do-i/get-help/security-incident","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"CategoryPage","type":"CATEGORY","urlPath":"/category/:categoryId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"ForumViewAllTopicsPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/all-topics/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"TkbPostPage","type":"TKB","urlPath":"/category/:categoryId/kbs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"GroupHubPostPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1744705076878,"localOverride":null,"page":{"id":"HowDoI","type":"COMMUNITY","urlPath":"/c/how-do-i","__typename":"PageDescriptor"},"__typename":"PageResource"}],"localOverride":false},"CachedAsset:text:en_US-components/context/AppContext/AppContextProvider-0":{"__typename":"CachedAsset","id":"text:en_US-components/context/AppContext/AppContextProvider-0","value":{"noCommunity":"Cannot find community","noUser":"Cannot find current user","noNode":"Cannot find node with id {nodeId}","noMessage":"Cannot find message with id {messageId}","userBanned":"We're sorry, but you have been banned from using this site.","userBannedReason":"You have been banned for the following reason: {reason}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-0":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-0","value":{"title":"Loading..."},"localOverride":false},"CachedAsset:theme:customTheme1-1744705075616":{"__typename":"CachedAsset","id":"theme:customTheme1-1744705075616","value":{"id":"customTheme1","animation":{"fast":"150ms","normal":"250ms","slow":"500ms","slowest":"750ms","function":"cubic-bezier(0.07, 0.91, 0.51, 1)","__typename":"AnimationThemeSettings"},"avatar":{"borderRadius":"50%","collections":["custom"],"__typename":"AvatarThemeSettings"},"basics":{"browserIcon":{"imageAssetName":"JimmyPackets-512-1702592938213.png","imageLastModified":"1702592945815","__typename":"ThemeAsset"},"customerLogo":{"imageAssetName":"f5_logo_fix-1704824537976.svg","imageLastModified":"1704824540697","__typename":"ThemeAsset"},"maximumWidthOfPageContent":"1600px","oneColumnNarrowWidth":"800px","gridGutterWidthMd":"30px","gridGutterWidthXs":"10px","pageWidthStyle":"WIDTH_OF_PAGE_CONTENT","__typename":"BasicsThemeSettings"},"buttons":{"borderRadiusSm":"5px","borderRadius":"5px","borderRadiusLg":"5px","paddingY":"5px","paddingYLg":"7px","paddingYHero":"var(--lia-bs-btn-padding-y-lg)","paddingX":"12px","paddingXLg":"14px","paddingXHero":"42px","fontStyle":"NORMAL","fontWeight":"400","textTransform":"NONE","disabledOpacity":0.5,"primaryTextColor":"var(--lia-bs-white)","primaryTextHoverColor":"var(--lia-bs-white)","primaryTextActiveColor":"var(--lia-bs-white)","primaryBgColor":"var(--lia-bs-primary)","primaryBgHoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.85))","primaryBgActiveColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.7))","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","primaryBorderActive":"1px solid transparent","primaryBorderFocus":"1px solid var(--lia-bs-white)","primaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","secondaryTextColor":"var(--lia-bs-gray-900)","secondaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","secondaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","secondaryBgColor":"var(--lia-bs-gray-400)","secondaryBgHoverColor":"hsl(var(--lia-bs-gray-400-h), var(--lia-bs-gray-400-s), calc(var(--lia-bs-gray-400-l) * 0.96))","secondaryBgActiveColor":"hsl(var(--lia-bs-gray-400-h), var(--lia-bs-gray-400-s), calc(var(--lia-bs-gray-400-l) * 0.92))","secondaryBorder":"1px solid transparent","secondaryBorderHover":"1px solid transparent","secondaryBorderActive":"1px solid transparent","secondaryBorderFocus":"1px solid transparent","secondaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","tertiaryTextColor":"var(--lia-bs-gray-900)","tertiaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","tertiaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","tertiaryBgColor":"transparent","tertiaryBgHoverColor":"transparent","tertiaryBgActiveColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.04)","tertiaryBorder":"1px solid transparent","tertiaryBorderHover":"1px solid hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","tertiaryBorderActive":"1px solid transparent","tertiaryBorderFocus":"1px solid transparent","tertiaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","destructiveTextColor":"var(--lia-bs-danger)","destructiveTextHoverColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.95))","destructiveTextActiveColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.9))","destructiveBgColor":"var(--lia-bs-gray-300)","destructiveBgHoverColor":"hsl(var(--lia-bs-gray-300-h), var(--lia-bs-gray-300-s), calc(var(--lia-bs-gray-300-l) * 0.96))","destructiveBgActiveColor":"hsl(var(--lia-bs-gray-300-h), var(--lia-bs-gray-300-s), calc(var(--lia-bs-gray-300-l) * 0.92))","destructiveBorder":"1px solid transparent","destructiveBorderHover":"1px solid transparent","destructiveBorderActive":"1px solid transparent","destructiveBorderFocus":"1px solid transparent","destructiveBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","__typename":"ButtonsThemeSettings"},"border":{"color":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","mainContent":"NONE","sideContent":"NONE","radiusSm":"3px","radius":"5px","radiusLg":"9px","radius50":"100vw","__typename":"BorderThemeSettings"},"boxShadow":{"xs":"0 0 0 1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.08), 0 3px 0 -1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.08)","sm":"0 2px 4px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.06)","md":"0 5px 15px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.15)","lg":"0 10px 30px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.15)","__typename":"BoxShadowThemeSettings"},"cards":{"bgColor":"var(--lia-panel-bg-color)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":"var(--lia-box-shadow-xs)","__typename":"CardsThemeSettings"},"chip":{"maxWidth":"300px","height":"30px","__typename":"ChipThemeSettings"},"coreTypes":{"defaultMessageLinkColor":"var(--lia-bs-primary)","defaultMessageLinkDecoration":"none","defaultMessageLinkFontStyle":"NORMAL","defaultMessageLinkFontWeight":"400","defaultMessageFontStyle":"NORMAL","defaultMessageFontWeight":"400","defaultMessageFontFamily":"var(--lia-bs-font-family-base)","forumColor":"#0C5C8D","forumFontFamily":"var(--lia-bs-font-family-base)","forumFontWeight":"var(--lia-default-message-font-weight)","forumLineHeight":"var(--lia-bs-line-height-base)","forumFontStyle":"var(--lia-default-message-font-style)","forumMessageLinkColor":"var(--lia-default-message-link-color)","forumMessageLinkDecoration":"var(--lia-default-message-link-decoration)","forumMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","forumMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","forumSolvedColor":"#62C026","blogColor":"#730015","blogFontFamily":"var(--lia-bs-font-family-base)","blogFontWeight":"var(--lia-default-message-font-weight)","blogLineHeight":"1.75","blogFontStyle":"var(--lia-default-message-font-style)","blogMessageLinkColor":"var(--lia-default-message-link-color)","blogMessageLinkDecoration":"var(--lia-default-message-link-decoration)","blogMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","blogMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","tkbColor":"#C20025","tkbFontFamily":"var(--lia-bs-font-family-base)","tkbFontWeight":"var(--lia-default-message-font-weight)","tkbLineHeight":"1.75","tkbFontStyle":"var(--lia-default-message-font-style)","tkbMessageLinkColor":"var(--lia-default-message-link-color)","tkbMessageLinkDecoration":"var(--lia-default-message-link-decoration)","tkbMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","tkbMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaColor":"#4099E2","qandaFontFamily":"var(--lia-bs-font-family-base)","qandaFontWeight":"var(--lia-default-message-font-weight)","qandaLineHeight":"var(--lia-bs-line-height-base)","qandaFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkColor":"var(--lia-default-message-link-color)","qandaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","qandaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaSolvedColor":"#3FA023","ideaColor":"#F3704B","ideaFontFamily":"var(--lia-bs-font-family-base)","ideaFontWeight":"var(--lia-default-message-font-weight)","ideaLineHeight":"var(--lia-bs-line-height-base)","ideaFontStyle":"var(--lia-default-message-font-style)","ideaMessageLinkColor":"var(--lia-default-message-link-color)","ideaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","ideaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","ideaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","contestColor":"#FCC845","contestFontFamily":"var(--lia-bs-font-family-base)","contestFontWeight":"var(--lia-default-message-font-weight)","contestLineHeight":"var(--lia-bs-line-height-base)","contestFontStyle":"var(--lia-default-message-link-font-style)","contestMessageLinkColor":"var(--lia-default-message-link-color)","contestMessageLinkDecoration":"var(--lia-default-message-link-decoration)","contestMessageLinkFontStyle":"ITALIC","contestMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","occasionColor":"#EE4B5B","occasionFontFamily":"var(--lia-bs-font-family-base)","occasionFontWeight":"var(--lia-default-message-font-weight)","occasionLineHeight":"var(--lia-bs-line-height-base)","occasionFontStyle":"var(--lia-default-message-font-style)","occasionMessageLinkColor":"var(--lia-default-message-link-color)","occasionMessageLinkDecoration":"var(--lia-default-message-link-decoration)","occasionMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","occasionMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","grouphubColor":"#491B62","categoryColor":"#949494","communityColor":"#FFFFFF","productColor":"#949494","__typename":"CoreTypesThemeSettings"},"colors":{"black":"#000000","white":"#FFFFFF","gray100":"#F7F7F7","gray200":"#F7F7F7","gray300":"#E8E8E8","gray400":"#D9D9D9","gray500":"#CCCCCC","gray600":"#949494","gray700":"#707070","gray800":"#545454","gray900":"#333333","dark":"#545454","light":"#F7F7F7","primary":"#0C5C8D","secondary":"#333333","bodyText":"#222222","bodyBg":"#F5F5F5","info":"#1D9CD3","success":"#62C026","warning":"#FFD651","danger":"#C20025","alertSystem":"#FF6600","textMuted":"#707070","highlight":"#FFFCAD","outline":"var(--lia-bs-primary)","custom":["#C20025","#081B85","#009639","#B3C6D7","#7CC0EB","#F29A36"],"__typename":"ColorsThemeSettings"},"divider":{"size":"3px","marginLeft":"4px","marginRight":"4px","borderRadius":"50%","bgColor":"var(--lia-bs-gray-600)","bgColorActive":"var(--lia-bs-gray-600)","__typename":"DividerThemeSettings"},"dropdown":{"fontSize":"var(--lia-bs-font-size-sm)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius-sm)","dividerBg":"var(--lia-bs-gray-300)","itemPaddingY":"5px","itemPaddingX":"20px","headerColor":"var(--lia-bs-gray-700)","__typename":"DropdownThemeSettings"},"email":{"link":{"color":"#0069D4","hoverColor":"#0061c2","decoration":"none","hoverDecoration":"underline","__typename":"EmailLinkSettings"},"border":{"color":"#e4e4e4","__typename":"EmailBorderSettings"},"buttons":{"borderRadiusLg":"5px","paddingXLg":"16px","paddingYLg":"7px","fontWeight":"700","primaryTextColor":"#ffffff","primaryTextHoverColor":"#ffffff","primaryBgColor":"#0069D4","primaryBgHoverColor":"#005cb8","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","__typename":"EmailButtonsSettings"},"panel":{"borderRadius":"5px","borderColor":"#e4e4e4","__typename":"EmailPanelSettings"},"__typename":"EmailThemeSettings"},"emoji":{"skinToneDefault":"#ffcd43","skinToneLight":"#fae3c5","skinToneMediumLight":"#e2cfa5","skinToneMedium":"#daa478","skinToneMediumDark":"#a78058","skinToneDark":"#5e4d43","__typename":"EmojiThemeSettings"},"heading":{"color":"var(--lia-bs-body-color)","fontFamily":"Inter","fontStyle":"NORMAL","fontWeight":"600","h1FontSize":"30px","h2FontSize":"25px","h3FontSize":"20px","h4FontSize":"18px","h5FontSize":"16px","h6FontSize":"16px","lineHeight":"1.2","subHeaderFontSize":"11px","subHeaderFontWeight":"500","h1LetterSpacing":"normal","h2LetterSpacing":"normal","h3LetterSpacing":"normal","h4LetterSpacing":"normal","h5LetterSpacing":"normal","h6LetterSpacing":"normal","subHeaderLetterSpacing":"2px","h1FontWeight":"var(--lia-bs-headings-font-weight)","h2FontWeight":"var(--lia-bs-headings-font-weight)","h3FontWeight":"var(--lia-bs-headings-font-weight)","h4FontWeight":"var(--lia-bs-headings-font-weight)","h5FontWeight":"var(--lia-bs-headings-font-weight)","h6FontWeight":"var(--lia-bs-headings-font-weight)","__typename":"HeadingThemeSettings"},"icons":{"size10":"10px","size12":"12px","size14":"14px","size16":"16px","size20":"20px","size24":"24px","size30":"30px","size40":"40px","size50":"50px","size60":"60px","size80":"80px","size120":"120px","size160":"160px","__typename":"IconsThemeSettings"},"imagePreview":{"bgColor":"var(--lia-bs-gray-900)","titleColor":"var(--lia-bs-white)","controlColor":"var(--lia-bs-white)","controlBgColor":"var(--lia-bs-gray-800)","__typename":"ImagePreviewThemeSettings"},"input":{"borderColor":"var(--lia-bs-gray-600)","disabledColor":"var(--lia-bs-gray-600)","focusBorderColor":"var(--lia-bs-primary)","labelMarginBottom":"10px","btnFontSize":"var(--lia-bs-font-size-sm)","focusBoxShadow":"0 0 0 3px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","checkLabelMarginBottom":"2px","checkboxBorderRadius":"3px","borderRadiusSm":"var(--lia-bs-border-radius-sm)","borderRadius":"var(--lia-bs-border-radius)","borderRadiusLg":"var(--lia-bs-border-radius-lg)","formTextMarginTop":"4px","textAreaBorderRadius":"var(--lia-bs-border-radius)","activeFillColor":"var(--lia-bs-primary)","__typename":"InputThemeSettings"},"loading":{"dotDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.2)","dotLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.5)","barDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.06)","barLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.4)","__typename":"LoadingThemeSettings"},"link":{"color":"var(--lia-bs-primary)","hoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) - 10%))","decoration":"none","hoverDecoration":"underline","__typename":"LinkThemeSettings"},"listGroup":{"itemPaddingY":"15px","itemPaddingX":"15px","borderColor":"var(--lia-bs-gray-300)","__typename":"ListGroupThemeSettings"},"modal":{"contentTextColor":"var(--lia-bs-body-color)","contentBg":"var(--lia-bs-white)","backgroundBg":"var(--lia-bs-black)","smSize":"440px","mdSize":"760px","lgSize":"1080px","backdropOpacity":0.3,"contentBoxShadowXs":"var(--lia-bs-box-shadow-sm)","contentBoxShadow":"var(--lia-bs-box-shadow)","headerFontWeight":"700","__typename":"ModalThemeSettings"},"navbar":{"position":"FIXED","background":{"attachment":null,"clip":null,"color":"var(--lia-bs-white)","imageAssetName":null,"imageLastModified":"0","origin":null,"position":"CENTER_CENTER","repeat":"NO_REPEAT","size":"COVER","__typename":"BackgroundProps"},"backgroundOpacity":0.8,"paddingTop":"15px","paddingBottom":"15px","borderBottom":"1px solid var(--lia-bs-border-color)","boxShadow":"var(--lia-bs-box-shadow-sm)","brandMarginRight":"30px","brandMarginRightSm":"10px","brandLogoHeight":"30px","linkGap":"10px","linkJustifyContent":"flex-start","linkPaddingY":"5px","linkPaddingX":"10px","linkDropdownPaddingY":"9px","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkColor":"var(--lia-bs-body-color)","linkHoverColor":"var(--lia-bs-primary)","linkFontSize":"var(--lia-bs-font-size-sm)","linkFontStyle":"NORMAL","linkFontWeight":"400","linkTextTransform":"NONE","linkLetterSpacing":"normal","linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkBgColor":"transparent","linkBgHoverColor":"transparent","linkBorder":"none","linkBorderHover":"none","linkBoxShadow":"none","linkBoxShadowHover":"none","linkTextBorderBottom":"none","linkTextBorderBottomHover":"none","dropdownPaddingTop":"10px","dropdownPaddingBottom":"15px","dropdownPaddingX":"10px","dropdownMenuOffset":"2px","dropdownDividerMarginTop":"10px","dropdownDividerMarginBottom":"10px","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","controllerIconColor":"var(--lia-bs-body-color)","controllerIconHoverColor":"var(--lia-bs-body-color)","controllerTextColor":"var(--lia-nav-controller-icon-color)","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","controllerHighlightColor":"hsla(30, 100%, 50%)","controllerHighlightTextColor":"var(--lia-yiq-light)","controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerColor":"var(--lia-nav-controller-icon-color)","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","hamburgerBgColor":"transparent","hamburgerBgHoverColor":"transparent","hamburgerBorder":"none","hamburgerBorderHover":"none","collapseMenuMarginLeft":"20px","collapseMenuDividerBg":"var(--lia-nav-link-color)","collapseMenuDividerOpacity":0.16,"__typename":"NavbarThemeSettings"},"pager":{"textColor":"var(--lia-bs-link-color)","textFontWeight":"var(--lia-font-weight-md)","textFontSize":"var(--lia-bs-font-size-sm)","__typename":"PagerThemeSettings"},"panel":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-bs-border-radius)","borderColor":"var(--lia-bs-border-color)","boxShadow":"none","__typename":"PanelThemeSettings"},"popover":{"arrowHeight":"8px","arrowWidth":"16px","maxWidth":"300px","minWidth":"100px","headerBg":"var(--lia-bs-white)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius)","boxShadow":"0 0.5rem 1rem hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.15)","__typename":"PopoverThemeSettings"},"prism":{"color":"#000000","bgColor":"#f5f2f0","fontFamily":"var(--font-family-monospace)","fontSize":"var(--lia-bs-font-size-base)","fontWeightBold":"var(--lia-bs-font-weight-bold)","fontStyleItalic":"italic","tabSize":2,"highlightColor":"#b3d4fc","commentColor":"#62707e","punctuationColor":"#6f6f6f","namespaceOpacity":"0.7","propColor":"#990055","selectorColor":"#517a00","operatorColor":"#906736","operatorBgColor":"hsla(0, 0%, 100%, 0.5)","keywordColor":"#0076a9","functionColor":"#d3284b","variableColor":"#c14700","__typename":"PrismThemeSettings"},"rte":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":" var(--lia-panel-box-shadow)","customColor1":"#bfedd2","customColor2":"#fbeeb8","customColor3":"#f8cac6","customColor4":"#eccafa","customColor5":"#c2e0f4","customColor6":"#2dc26b","customColor7":"#f1c40f","customColor8":"#e03e2d","customColor9":"#b96ad9","customColor10":"#3598db","customColor11":"#169179","customColor12":"#e67e23","customColor13":"#ba372a","customColor14":"#843fa1","customColor15":"#236fa1","customColor16":"#ecf0f1","customColor17":"#ced4d9","customColor18":"#95a5a6","customColor19":"#7e8c8d","customColor20":"#34495e","customColor21":"#000000","customColor22":"#ffffff","defaultMessageHeaderMarginTop":"14px","defaultMessageHeaderMarginBottom":"10px","defaultMessageItemMarginTop":"0","defaultMessageItemMarginBottom":"10px","diffAddedColor":"hsla(170, 53%, 51%, 0.4)","diffChangedColor":"hsla(43, 97%, 63%, 0.4)","diffNoneColor":"hsla(0, 0%, 80%, 0.4)","diffRemovedColor":"hsla(9, 74%, 47%, 0.4)","specialMessageHeaderMarginTop":"14px","specialMessageHeaderMarginBottom":"10px","specialMessageItemMarginTop":"0","specialMessageItemMarginBottom":"10px","__typename":"RteThemeSettings"},"tags":{"bgColor":"var(--lia-bs-gray-200)","bgHoverColor":"var(--lia-bs-gray-400)","borderRadius":"var(--lia-bs-border-radius-sm)","color":"var(--lia-bs-body-color)","hoverColor":"var(--lia-bs-body-color)","fontWeight":"var(--lia-font-weight-md)","fontSize":"var(--lia-font-size-xxs)","textTransform":"UPPERCASE","letterSpacing":"0.5px","__typename":"TagsThemeSettings"},"toasts":{"borderRadius":"var(--lia-bs-border-radius)","paddingX":"12px","__typename":"ToastsThemeSettings"},"typography":{"fontFamilyBase":"Atkinson Hyperlegible","fontStyleBase":"NORMAL","fontWeightBase":"400","fontWeightLight":"300","fontWeightNormal":"400","fontWeightMd":"500","fontWeightBold":"700","letterSpacingSm":"normal","letterSpacingXs":"normal","lineHeightBase":"1.3","fontSizeBase":"15px","fontSizeXxs":"11px","fontSizeXs":"12px","fontSizeSm":"13px","fontSizeLg":"20px","fontSizeXl":"24px","smallFontSize":"14px","customFonts":[],"__typename":"TypographyThemeSettings"},"unstyledListItem":{"marginBottomSm":"5px","marginBottomMd":"10px","marginBottomLg":"15px","marginBottomXl":"20px","marginBottomXxl":"25px","__typename":"UnstyledListItemThemeSettings"},"yiq":{"light":"#ffffff","dark":"#000000","__typename":"YiqThemeSettings"},"colorLightness":{"primaryDark":0.36,"primaryLight":0.74,"primaryLighter":0.89,"primaryLightest":0.95,"infoDark":0.39,"infoLight":0.72,"infoLighter":0.85,"infoLightest":0.93,"successDark":0.24,"successLight":0.62,"successLighter":0.8,"successLightest":0.91,"warningDark":0.39,"warningLight":0.68,"warningLighter":0.84,"warningLightest":0.93,"dangerDark":0.41,"dangerLight":0.72,"dangerLighter":0.89,"dangerLightest":0.95,"__typename":"ColorLightnessThemeSettings"},"localOverride":false,"__typename":"Theme"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-1744046271000","value":{"title":"Loading..."},"localOverride":false},"CachedAsset:text:en_US-components/common/EmailVerification-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/common/EmailVerification-1744046271000","value":{"email.verification.title":"Email Verification Required","email.verification.message.update.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. To change your email, visit My Settings.","email.verification.message.resend.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. Resend email."},"localOverride":false},"CachedAsset:text:en_US-pages/tags/TagPage-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-pages/tags/TagPage-1744046271000","value":{"tagPageTitle":"Tag:\"{tagName}\" | {communityTitle}","tagPageForNodeTitle":"Tag:\"{tagName}\" in \"{title}\" | {communityTitle}","name":"Tags Page","tag":"Tag: {tagName}"},"localOverride":false},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bi0zNC0xM2k0MzE3N0Q2NjFBRDg5NDAy\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bi0zNC0xM2k0MzE3N0Q2NjFBRDg5NDAy","mimeType":"image/png"},"Category:category:Articles":{"__typename":"Category","id":"category:Articles","entityType":"CATEGORY","displayId":"Articles","nodeType":"category","depth":1,"title":"Articles","shortTitle":"Articles","parent":{"__ref":"Category:category:top"},"categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:top":{"__typename":"Category","id":"category:top","displayId":"top","nodeType":"category","depth":0,"title":"Top"},"Tkb:board:TechnicalArticles":{"__typename":"Tkb","id":"board:TechnicalArticles","entityType":"TKB","displayId":"TechnicalArticles","nodeType":"board","depth":2,"conversationStyle":"TKB","title":"Technical Articles","description":"F5 SMEs share good practice.","avatar":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bi0zNC0xM2k0MzE3N0Q2NjFBRDg5NDAy\"}"},"profileSettings":{"__typename":"ProfileSettings","language":null},"parent":{"__ref":"Category:category:Articles"},"ancestors":{"__typename":"CoreNodeConnection","edges":[{"__typename":"CoreNodeEdge","node":{"__ref":"Community:community:zihoc95639"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:Articles"}}]},"userContext":{"__typename":"NodeUserContext","canAddAttachments":false,"canUpdateNode":false,"canPostMessages":false,"isSubscribed":false},"boardPolicies":{"__typename":"BoardPolicies","canPublishArticleOnCreate":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","args":[]}},"canReadNode":{"__typename":"PolicyResult","failureReason":null}},"theme":{"__ref":"Theme:customTheme1"},"tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"shortTitle":"Technical Articles","tagPolicies":{"__typename":"TagPolicies","canSubscribeTagOnNode":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.labels.action.corenode.subscribe_labels.allow.accessDenied","key":"error.lithium.policies.labels.action.corenode.subscribe_labels.allow.accessDenied","args":[]}},"canManageTagDashboard":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.labels.action.corenode.admin_labels.allow.accessDenied","key":"error.lithium.policies.labels.action.corenode.admin_labels.allow.accessDenied","args":[]}}}},"CachedAsset:quilt:f5.prod:pages/tags/TagPage:board:TechnicalArticles-1744705071648":{"__typename":"CachedAsset","id":"quilt:f5.prod:pages/tags/TagPage:board:TechnicalArticles-1744705071648","value":{"id":"TagPage","container":{"id":"Common","headerProps":{"removeComponents":["community.widget.bannerWidget"],"__typename":"QuiltContainerSectionProps"},"items":[{"id":"tag-header-widget","layout":"ONE_COLUMN","bgColor":"var(--lia-bs-white)","showBorder":"BOTTOM","sectionEditLevel":"LOCKED","columnMap":{"main":[{"id":"tags.widget.TagsHeaderWidget","__typename":"QuiltComponent"}],"__typename":"OneSectionColumns"},"__typename":"OneColumnQuiltSection"},{"id":"messages-list-for-tag-widget","layout":"ONE_COLUMN","columnMap":{"main":[{"id":"messages.widget.messageListForNodeByRecentActivityWidget","props":{"viewVariant":{"type":"inline","props":{"useUnreadCount":true,"useViewCount":true,"useAuthorLogin":true,"clampBodyLines":3,"useAvatar":true,"useBoardIcon":false,"useKudosCount":true,"usePreviewMedia":true,"useTags":false,"useNode":true,"useNodeLink":true,"useTextBody":true,"truncateBodyLength":-1,"useBody":true,"useRepliesCount":true,"useSolvedBadge":true,"timeStampType":"conversation.lastPostingActivityTime","useMessageTimeLink":true,"clampSubjectLines":2}},"panelType":"divider","useTitle":false,"hideIfEmpty":false,"pagerVariant":{"type":"loadMore"},"style":"list","showTabs":true,"tabItemMap":{"default":{"mostRecent":true,"mostRecentUserContent":false,"newest":false},"additional":{"mostKudoed":true,"mostViewed":true,"mostReplies":false,"noReplies":false,"noSolutions":false,"solutions":false}}},"__typename":"QuiltComponent"}],"__typename":"OneSectionColumns"},"__typename":"OneColumnQuiltSection"}],"__typename":"QuiltContainer"},"__typename":"Quilt"},"localOverride":false},"CachedAsset:quiltWrapper:f5.prod:Common:1744732624172":{"__typename":"CachedAsset","id":"quiltWrapper:f5.prod:Common:1744732624172","value":{"id":"Common","header":{"backgroundImageProps":{"assetName":"header.jpg","backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"LEFT_CENTER","lastModified":"1702932449000","__typename":"BackgroundImageProps"},"backgroundColor":"transparent","items":[{"id":"custom.widget.Beta_MetaNav","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"community.widget.navbarWidget","props":{"showUserName":false,"showRegisterLink":true,"style":{"boxShadow":"var(--lia-bs-box-shadow-sm)","linkFontWeight":"700","controllerHighlightColor":"hsla(30, 100%, 50%)","dropdownDividerMarginBottom":"10px","hamburgerBorderHover":"none","linkFontSize":"15px","linkBoxShadowHover":"none","backgroundOpacity":0.4,"controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerBgColor":"transparent","linkTextBorderBottom":"none","hamburgerColor":"var(--lia-nav-controller-icon-color)","brandLogoHeight":"48px","linkLetterSpacing":"normal","linkBgHoverColor":"transparent","collapseMenuDividerOpacity":0.16,"paddingBottom":"10px","dropdownPaddingBottom":"15px","dropdownMenuOffset":"2px","hamburgerBgHoverColor":"transparent","borderBottom":"0","hamburgerBorder":"none","dropdownPaddingX":"10px","brandMarginRightSm":"10px","linkBoxShadow":"none","linkJustifyContent":"center","linkColor":"var(--lia-bs-primary)","collapseMenuDividerBg":"var(--lia-nav-link-color)","dropdownPaddingTop":"10px","controllerHighlightTextColor":"var(--lia-yiq-dark)","background":{"imageAssetName":"","color":"var(--lia-bs-white)","size":"COVER","repeat":"NO_REPEAT","position":"CENTER_CENTER","imageLastModified":""},"linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkHoverColor":"var(--lia-bs-primary)","position":"FIXED","linkBorder":"none","linkTextBorderBottomHover":"2px solid #0C5C8D","brandMarginRight":"30px","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","linkBorderHover":"none","collapseMenuMarginLeft":"20px","linkFontStyle":"NORMAL","linkPaddingX":"10px","paddingTop":"10px","linkPaddingY":"5px","linkTextTransform":"NONE","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkBgColor":"transparent","linkDropdownPaddingY":"9px","controllerIconColor":"#0C5C8D","dropdownDividerMarginTop":"10px","linkGap":"10px","controllerIconHoverColor":"#0C5C8D"},"links":{"sideLinks":[],"mainLinks":[{"children":[{"linkType":"INTERNAL","id":"migrated-link-1","params":{"boardId":"TechnicalForum","categoryId":"Forums"},"routeName":"ForumBoardPage"},{"linkType":"INTERNAL","id":"migrated-link-2","params":{"boardId":"WaterCooler","categoryId":"Forums"},"routeName":"ForumBoardPage"}],"linkType":"INTERNAL","id":"migrated-link-0","params":{"categoryId":"Forums"},"routeName":"CategoryPage"},{"children":[{"linkType":"INTERNAL","id":"migrated-link-4","params":{"boardId":"codeshare","categoryId":"CrowdSRC"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"migrated-link-5","params":{"boardId":"communityarticles","categoryId":"CrowdSRC"},"routeName":"TkbBoardPage"}],"linkType":"INTERNAL","id":"migrated-link-3","params":{"categoryId":"CrowdSRC"},"routeName":"CategoryPage"},{"children":[{"linkType":"INTERNAL","id":"migrated-link-7","params":{"boardId":"TechnicalArticles","categoryId":"Articles"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"article-series","params":{"boardId":"article-series","categoryId":"Articles"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"security-insights","params":{"boardId":"security-insights","categoryId":"Articles"},"routeName":"TkbBoardPage"},{"linkType":"INTERNAL","id":"migrated-link-8","params":{"boardId":"DevCentralNews","categoryId":"Articles"},"routeName":"TkbBoardPage"}],"linkType":"INTERNAL","id":"migrated-link-6","params":{"categoryId":"Articles"},"routeName":"CategoryPage"},{"children":[{"linkType":"INTERNAL","id":"migrated-link-10","params":{"categoryId":"CommunityGroups"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"migrated-link-11","params":{"categoryId":"F5-Groups"},"routeName":"CategoryPage"}],"linkType":"INTERNAL","id":"migrated-link-9","params":{"categoryId":"GroupsCategory"},"routeName":"CategoryPage"},{"children":[],"linkType":"INTERNAL","id":"migrated-link-12","params":{"boardId":"Events","categoryId":"top"},"routeName":"EventBoardPage"},{"children":[],"linkType":"INTERNAL","id":"migrated-link-13","params":{"boardId":"Suggestions","categoryId":"top"},"routeName":"IdeaBoardPage"},{"children":[],"linkType":"EXTERNAL","id":"Common-external-link","url":"https://community.f5.com/c/how-do-i","target":"SELF"}]},"className":"QuiltComponent_lia-component-edit-mode__lQ9Z6","showSearchIcon":false},"__typename":"QuiltComponent"},{"id":"community.widget.bannerWidget","props":{"backgroundColor":"transparent","visualEffects":{"showBottomBorder":false},"backgroundImageProps":{"backgroundSize":"COVER","backgroundPosition":"CENTER_CENTER","backgroundRepeat":"NO_REPEAT"},"fontColor":"#222222"},"__typename":"QuiltComponent"},{"id":"community.widget.breadcrumbWidget","props":{"backgroundColor":"var(--lia-bs-primary)","linkHighlightColor":"#FFFFFF","visualEffects":{"showBottomBorder":false},"backgroundOpacity":60,"linkTextColor":"#FFFFFF"},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"footer":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"var(--lia-bs-body-color)","items":[{"id":"custom.widget.Beta_Footer","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.Tag_Manager_Helper","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.Consent_Blackbar","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"__typename":"QuiltWrapper","localOverride":false},"localOverride":false},"CachedAsset:text:en_US-components/common/ActionFeedback-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/common/ActionFeedback-1744046271000","value":{"joinedGroupHub.title":"Welcome","joinedGroupHub.message":"You are now a member of this group and are subscribed to updates.","groupHubInviteNotFound.title":"Invitation Not Found","groupHubInviteNotFound.message":"Sorry, we could not find your invitation to the group. The owner may have canceled the invite.","groupHubNotFound.title":"Group Not Found","groupHubNotFound.message":"The grouphub you tried to join does not exist. It may have been deleted.","existingGroupHubMember.title":"Already Joined","existingGroupHubMember.message":"You are already a member of this group.","accountLocked.title":"Account Locked","accountLocked.message":"Your account has been locked due to multiple failed attempts. Try again in {lockoutTime} minutes.","editedGroupHub.title":"Changes Saved","editedGroupHub.message":"Your group has been updated.","leftGroupHub.title":"Goodbye","leftGroupHub.message":"You are no longer a member of this group and will not receive future updates.","deletedGroupHub.title":"Deleted","deletedGroupHub.message":"The group has been deleted.","groupHubCreated.title":"Group Created","groupHubCreated.message":"{groupHubName} is ready to use","accountClosed.title":"Account Closed","accountClosed.message":"The account has been closed and you will now be redirected to the homepage","resetTokenExpired.title":"Reset Password Link has Expired","resetTokenExpired.message":"Try resetting your password again","invalidUrl.title":"Invalid URL","invalidUrl.message":"The URL you're using is not recognized. Verify your URL and try again.","accountClosedForUser.title":"Account Closed","accountClosedForUser.message":"{userName}'s account is closed","inviteTokenInvalid.title":"Invitation Invalid","inviteTokenInvalid.message":"Your invitation to the community has been canceled or expired.","inviteTokenError.title":"Invitation Verification Failed","inviteTokenError.message":"The url you are utilizing is not recognized. Verify your URL and try again","pageNotFound.title":"Access Denied","pageNotFound.message":"You do not have access to this area of the community or it doesn't exist","eventAttending.title":"Responded as Attending","eventAttending.message":"You'll be notified when there's new activity and reminded as the event approaches","eventInterested.title":"Responded as Interested","eventInterested.message":"You'll be notified when there's new activity and reminded as the event approaches","eventNotFound.title":"Event Not Found","eventNotFound.message":"The event you tried to respond to does not exist.","redirectToRelatedPage.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.message":"The content you are trying to access is archived","redirectToRelatedPage.message":"The content you are trying to access is archived","relatedUrl.archivalLink.flyoutMessage":"The content you are trying to access is archived View Archived Content"},"localOverride":false},"CachedAsset:component:custom.widget.Beta_MetaNav-en-us-1744705107577":{"__typename":"CachedAsset","id":"component:custom.widget.Beta_MetaNav-en-us-1744705107577","value":{"component":{"id":"custom.widget.Beta_MetaNav","template":{"id":"Beta_MetaNav","markupLanguage":"HANDLEBARS","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"MetaNav menu at the top of every page.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Beta_MetaNav","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"MetaNav menu at the top of every page.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Beta_Footer-en-us-1744705107577":{"__typename":"CachedAsset","id":"component:custom.widget.Beta_Footer-en-us-1744705107577","value":{"component":{"id":"custom.widget.Beta_Footer","template":{"id":"Beta_Footer","markupLanguage":"HANDLEBARS","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"DevCentral´s custom footer.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Beta_Footer","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"DevCentral´s custom footer.","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Tag_Manager_Helper-en-us-1744705107577":{"__typename":"CachedAsset","id":"component:custom.widget.Tag_Manager_Helper-en-us-1744705107577","value":{"component":{"id":"custom.widget.Tag_Manager_Helper","template":{"id":"Tag_Manager_Helper","markupLanguage":"HANDLEBARS","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"Helper widget to inject Tag Manager scripts into head element","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Tag_Manager_Helper","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"Helper widget to inject Tag Manager scripts into head element","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:component:custom.widget.Consent_Blackbar-en-us-1744705107577":{"__typename":"CachedAsset","id":"component:custom.widget.Consent_Blackbar-en-us-1744705107577","value":{"component":{"id":"custom.widget.Consent_Blackbar","template":{"id":"Consent_Blackbar","markupLanguage":"HTML","style":null,"texts":{},"defaults":{"config":{"applicablePages":[],"description":"","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.Consent_Blackbar","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"TEXTHTML","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":null,"form":null},"localOverride":false},"CachedAsset:text:en_US-components/community/Breadcrumb-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/community/Breadcrumb-1744046271000","value":{"navLabel":"Breadcrumbs","dropdown":"Additional parent page navigation"},"localOverride":false},"CachedAsset:text:en_US-components/tags/TagsHeaderWidget-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/tags/TagsHeaderWidget-1744046271000","value":{"tag":"{tagName}","topicsCount":"{count} {count, plural, one {Topic} other {Topics}}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageListForNodeByRecentActivityWidget-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageListForNodeByRecentActivityWidget-1744046271000","value":{"title@userScope:other":"Recent Content","title@userScope:self":"Contributions","title@board:FORUM@userScope:other":"Recent Discussions","title@board:BLOG@userScope:other":"Recent Blogs","emptyDescription":"No content to show","MessageListForNodeByRecentActivityWidgetEditor.nodeScope.label":"Scope","title@instance:1706288370055":"Content Feed","title@instance:1743095186784":"Most Recent Updates","title@instance:1704317906837":"Content Feed","title@instance:1743095018194":"Most Recent Updates","title@instance:1702668293472":"Community Feed","title@instance:1743095117047":"Most Recent Updates","title@instance:1704319314827":"Blog Feed","title@instance:1743095235555":"Most Recent Updates","title@instance:1704320290851":"My Contributions","title@instance:1703720491809":"Forum Feed","title@instance:1743095311723":"Most Recent Updates","title@instance:1703028709746":"Group Content Feed","title@instance:VTsglH":"Content Feed"},"localOverride":false},"Category:category:Forums":{"__typename":"Category","id":"category:Forums","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Forum:board:TechnicalForum":{"__typename":"Forum","id":"board:TechnicalForum","forumPolicies":{"__typename":"ForumPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Forum:board:WaterCooler":{"__typename":"Forum","id":"board:WaterCooler","forumPolicies":{"__typename":"ForumPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:DevCentralNews":{"__typename":"Tkb","id":"board:DevCentralNews","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:GroupsCategory":{"__typename":"Category","id":"category:GroupsCategory","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:F5-Groups":{"__typename":"Category","id":"category:F5-Groups","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:CommunityGroups":{"__typename":"Category","id":"category:CommunityGroups","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Occasion:board:Events":{"__typename":"Occasion","id":"board:Events","boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"occasionPolicies":{"__typename":"OccasionPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Idea:board:Suggestions":{"__typename":"Idea","id":"board:Suggestions","boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"ideaPolicies":{"__typename":"IdeaPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:CrowdSRC":{"__typename":"Category","id":"category:CrowdSRC","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:codeshare":{"__typename":"Tkb","id":"board:codeshare","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:communityarticles":{"__typename":"Tkb","id":"board:communityarticles","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:security-insights":{"__typename":"Tkb","id":"board:security-insights","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Tkb:board:article-series":{"__typename":"Tkb","id":"board:article-series","tkbPolicies":{"__typename":"TkbPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Conversation:conversation:284930":{"__typename":"Conversation","id":"conversation:284930","topic":{"__typename":"TkbTopicMessage","uid":284930},"lastPostingActivityTime":"2024-12-17T19:36:21.859-08:00","solved":false},"User:user:51154":{"__typename":"User","uid":51154,"login":"JRahm","registrationData":{"__typename":"RegistrationData","status":null},"deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://community.f5.com/t5/s/zihoc95639/images/dS01MTE1NC1uYzdSVFk?image-coordinates=0%2C0%2C1067%2C1067"},"id":"user:51154"},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtMTAxMDFpOEY4QTY2NDAyMjlEODAxOA?revision=1\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtMTAxMDFpOEY4QTY2NDAyMjlEODAxOA?revision=1","title":"0EM1T000003Jj5t.png","associationType":"BODY","width":781,"height":205,"altText":null},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtNzAzMmlENkY2RTlDQjM5QzMyODYw?revision=1\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtNzAzMmlENkY2RTlDQjM5QzMyODYw?revision=1","title":"0EM1T000003Jj63.png","associationType":"BODY","width":348,"height":110,"altText":null},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtODg1NGlFOTlCNUFEN0ZCNjlFN0Ex?revision=1\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtODg1NGlFOTlCNUFEN0ZCNjlFN0Ex?revision=1","title":"0EM1T000003Jj6D.png","associationType":"BODY","width":533,"height":137,"altText":null},"TkbTopicMessage:message:284930":{"__typename":"TkbTopicMessage","subject":"Managing ZoneRunner Resource Records with Bigsuds","conversation":{"__ref":"Conversation:conversation:284930"},"id":"message:284930","revisionNum":1,"uid":284930,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:51154"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":1099},"postTime":"2021-09-23T10:54:35.000-07:00","lastPublishTime":"2021-09-23T10:54:35.000-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" Over the last several years, there have been questions internal and external on how to manage ZoneRunner (the GUI tool in F5 DNS that allows you to manage DNS zones and records) resources via the REST interface. But that's a no can do with the iControl REST--it doesn't have that functionality. It was brought to my attention by one of our solutions engineers that a customer is using some methods in the SOAP interface that allows you to do just that...which was news to me! The things you learn... In this article, I'll highlight a few of the methods available to you and work on a sample domain in the python module bigsuds that utilizes the suds SOAP library for communication duties with the BIG-IP iControl SOAP interface. \n\n Test Domain & Procedure \n\n For demonstration purposes, I'll create a domain in the external view, dctest1.local, with the following attributes that mirrors nearly identically one I created in the GUI: \n\n Type: master Zone Name: dctest1.local. Zone File Name: db.external.dctest1.local. Options: allow-update from localhost TTL: 500 SOA: ns1.dctest1.local. Email: hostmaster.ns1.dctest1.local. Serial: 2021092201 Refresh: 10800 Retry: 3600 Expire: 604800 Negative TTL: 60 \n\n I'll also add a couple type A records to that domain: \n\n name: mail.dctest1.local., address: 10.0.2.25, TTL: 86400 name: www.dctest1.local., address: 10.0.2.80, TTL: 3600 \n\n After adding the records, I'll update one of them, changing the IP and the TTL: \n\n name: mail.dctest1.local., address: 10.0.2.110, ttl: 900 \n\n Then I'll delete the other one: \n\n name: www.dctest1.local., address: 10.0.2.80, TTL: 3600 \n\n And finally, I'll delete the zone: \n\n name: dctest1.local. \n\n ZoneRunner Methods \n\n All the methods can be found on Clouddocs in the ZoneRunner, Zone, and ResourceRecord method pages. The specific methods we'll use in our highlight real are: \n\n Management.ResourceRecord.add_a Management.ResourceRecord.delete_a Management.ResourceRecord.get_rrs Management.ResourceRecord.update_a Management.Zone.add_zone_text Management.Zone.get_zone_v2 Management.Zone.zone_exist \n\n With each method, there is a data structure that the interface expects. Each link above provides the details, but let's look at an example with the add_a method. The method requires three parameters, view_zones, a_records, and sync_ptrs, which the image of the table shows below. \n\n The boolean is just a True/False value in a list. The reason the list ( [] ) is there for all the attributes is because you can send a single request to update more than one zone, and add more than one record within each zone if desired. The data structure for view_zones and a_records is in the following two images. \n\n Now that we have an idea of what the methods require, let's take a look at some code! \n\n Methods In Action \n\n First, I import bigsuds and initialize the BIG-IP. The arguments are ordered in bigsuds for host, username, and password. If the default “admin/admin” is used, they are assumed, as is shown here. \n\n \nimport bigsuds\n\nb = bigsuds.BIGIP(hostname='ltm3.test.local')\n \n\n Next, I need to format the ViewZone data in a native python dictionary, and then I check for the existence of that zone. \n\n \nzone_view = {'view_name': 'external',\n 'zone_name': 'dctest1.local.'\n }\nb.Management.Zone.zone_exist([zone_view])\n\n# [0]\n \n\n Note that the return value, which should be a list of booleans, is a list with a 0. I’m guessing that’s either suds or the bigsuds implementation doing that, but it’s important to note if you’re checking for a boolean False. It’s also necessary to set the booleans as 0 or 1 as well when sending requests to BIG-IP with bigsuds. \n\n Now I will create the zone since it does not yet exist. From the add_zone_text method description on Clouddocs, note that I need to supply, in separate parameters, the zone info, the appropriate zone records, and the boolean to sync reverse records or not. \n\n \nzone_add_info = {'view_name': 'external',\n 'zone_name': 'dctest1.local.',\n 'zone_type': 'MASTER',\n 'zone_file': 'db.external.dctest1.local.',\n 'option_seq': ['allow-update { localhost;};']}\nzone_add_records = 'dctest1.local. 500 IN SOA ns1.dctest1.local. hostmaster.ns1.dctest1.local. 2021092201 10800 3600 604800 60;\\n' \\\n 'dctest1.local. 3600 IN NS ns1.dctest1.local.;\\n' \\\n 'ns1.dctest1.local. 3600 IN A 10.0.2.1;'\n\nb.Management.Zone.add_zone_text([zone_add_info],\n [[zone_add_records]],\n [0])\nb.Management.Zone.zone_exist([zone_view])\n\n# [1]\n \n\n Note that the strings here require a detailed understanding of DNS record formatting, the individual fields are not parameters that can be set like in the ZoneRunner GUI. But, I am confident there is an abundance of modules that manage DNS formatting in the python ecosystem that could simplify the data structuring. After creating the zone, another check to see if the zone exists results in a true condition. Huzzah! \n\n Now I’ll check the zone info and the existing records for that zone. \n\n \nzone = b.Management.Zone.get_zone_v2([zone_view])\nfor k, v in zone[0].items():\n print(f'{k}: {v}')\n\n# view_name: external\n# zone_name: dctest1.local.\n# zone_type: MASTER\n# zone_file: \"db.external.dctest1.local.\"\n# option_seq: ['allow-update { localhost;};']\n\nrrs = b.Management.ResourceRecord.get_rrs([zone_view])\nfor rr in rrs[0]:\n print(rr)\n\n# dctest1.local. 500 IN SOA ns1.dctest1.local. hostmaster.ns1.dctest1.local. 2021092201 10800 3600 604800 60\n# dctest1.local. 3600 IN NS ns1.dctest1.local.\n# ns1.dctest1.local. 3600 IN A 10.0.2.1\n \n\n Everything checks outs! Next I’ll create the A records for the mail and www services. I’m going to add a filter to only check for the mail/www services for printing to cut down on the lines, but know that they’re still there going forward. \n\n \na1 = {'domain_name': 'mail.dctest1.local.',\n 'ip_address': '10.0.2.25',\n 'ttl': 86400}\na2 = {'domain_name': 'www.dctest1.local.',\n 'ip_address': '10.0.2.80',\n 'ttl': 3600}\nb.Management.ResourceRecord.add_a(view_zones=[zone_view],\n a_records=[[a1, a2]],\n sync_ptrs=[0])\nrrs = b.Management.ResourceRecord.get_rrs([zone_view])\nfor rr in rrs[0]:\n if any(item in rr for item in ['mail', 'www']):\n print(rr)\n\n# mail.dctest1.local. 86400 IN A 10.0.2.25\n# www.dctest1.local. 3600 IN A 10.0.2.80\n \n\n Here you can see that I’m adding two records to the zone specified and not creating the reverse records (not included for brevity, but in prod would be likely). Now I’ll update the mail address and TTL. \n\n \nb.Management.ResourceRecord.update_a([zone_view],\n [[a1]],\n [[a1_update]],\n [0])\nrrs = b.Management.ResourceRecord.get_rrs([zone_view])\nfor rr in rrs[0]:\n if any(item in rr for item in ['mail', 'www']):\n print(rr)\n\n# mail.dctest1.local. 900 IN A 10.0.2.110\n# www.dctest1.local. 3600 IN A 10.0.2.80\n \n\n You can see that the address and TTL updated as expected. Note that with the update_/N/ methods, you need to provide the old and new, not just the new. Let’s get destruction and delete the www record! \n\n \nb.Management.ResourceRecord.delete_a([zone_view],\n [[a2]],\n [0])\nrrs = b.Management.ResourceRecord.get_rrs([zone_view])\nfor rr in rrs[0]:\n if any(item in rr for item in ['mail', 'www']):\n print(rr)\n\n# mail.dctest1.local. 900 IN A 10.0.2.110\n \n\n And your web service is now unreachable via DNS. Congratulations! But there’s more damage we can do: it’s time to delete the whole zone. \n\n \nb.Management.Zone.delete_zone([zone_view])\nb.Management.Zone.zone_exist([zone_view])\n\n# [0]\n \n\n And that’s a wrap! As I said, it’s been years since I have spent time with the iControl SOAP interface. It’s nice to know that even though most of what we do is done through REST, imperatively or declaratively, that some missing functionality in that interface is still alive and kicking via SOAP. H/T to Scott Huddy for the nudge to investigate this. Questions? Drop me a comment below. Happy coding! A gist of these samples is available on GitHub. ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"8628","kudosSumWeight":2,"repliesCount":2,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDE","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtMTAxMDFpOEY4QTY2NDAyMjlEODAxOA?revision=1\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDI","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtNzAzMmlENkY2RTlDQjM5QzMyODYw?revision=1\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDM","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODQ5MzAtODg1NGlFOTlCNUFEN0ZCNjlFN0Ex?revision=1\"}"}}],"totalCount":3,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:333178":{"__typename":"Conversation","id":"conversation:333178","topic":{"__typename":"TkbTopicMessage","uid":333178},"lastPostingActivityTime":"2024-09-11T08:23:19.422-07:00","solved":false},"User:user:275883":{"__typename":"User","uid":275883,"login":"Tony_Marfil","registrationData":{"__typename":"RegistrationData","status":null},"deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://community.f5.com/t5/s/zihoc95639/images/dS0yNzU4ODMtQ2o3aUZ6?image-coordinates=0%2C0%2C3022%2C3022"},"id":"user:275883"},"TkbTopicMessage:message:333178":{"__typename":"TkbTopicMessage","subject":"VIPTest: Rapid Application Testing for F5 Environments","conversation":{"__ref":"Conversation:conversation:333178"},"id":"message:333178","revisionNum":4,"uid":333178,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:275883"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":" VIPTest is a Python-based tool for efficiently testing multiple URLs in F5 environments, allowing quick assessment of application behavior before and after configuration changes. It supports concurrent processing, handles various URL formats, and provides detailed reports on HTTP responses, TLS versions, and connectivity status, making it useful for migrations and routine maintenance. ","introduction":"","metrics":{"__typename":"MessageMetrics","views":749},"postTime":"2024-08-20T05:00:00.039-07:00","lastPublishTime":"2024-09-04T09:43:48.631-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" The Motivation Behind VIPTest \n As an F5 Solutions Architect, I noticed a recurring challenge with prospective customers: there was no quick way to \"grade\" the success of migrating from their current environment to F5 solutions. This gap made it difficult to quickly identify and address problem areas. To solve this, I developed VIPTest - a tool designed as a convenience for anyone considering moving their applications from a different vendor solution (e.g., Citrix NetScaler) to F5. \n However, VIPTest's utility extends far beyond migration scenarios. It can be used in any environment where you introduce a change and need an automated process to assess the success of these changes. Whether you're upgrading software, modifying configurations, or performing routine maintenance, VIPTest provides a rapid, comprehensive snapshot of your application landscape. \n F5's Application Services and Security Solutions \n Before we dive into VIPTest, let's briefly explore F5's three new families of Application Services and Security solutions: \n \n \n F5 Distributed Cloud (XC): A SaaS-based security, networking, and application management solution that provides multi-cloud networking, web application and API protection, and edge-based computing. \n \n \n BIG-IP Next: The next generation of F5's flagship product, offering advanced traffic management, security, and analytics in a more flexible, software-first architecture. \n \n \n NGINX: A suite of cloud native technologies that provide API gateway, reverse proxy, and application security, known for its high performance and scalability. \n \n \n With VIPTest, you can confidently introduce changes to any of these solutions and quickly assess their impact. It's equally valuable for evaluating initial migrations and for ongoing operational changes within the same solution. \n The Problem VIPTest Solves \n Customers often lack a quick, automated way to assess the impact of environment changes on their applications. VIPTest addresses this by providing: \n \n Fast, concurrent testing of multiple URLs \n Pre and post-change comparisons \n Detection of improvements (e.g., TLS upgrades, HTTP/1.1 to HTTP/2 upgrades) and issues (e.g., broken connectivity) \n Identification of deprecated applications so you don't wast time testing 'dead' URLs \n Rapid assessment when migrating from another vendor's solution, such as Citrix NetScaler \n A simple mechanism to target just a staging environment during testing by overriding system DNS with static IP entries \n \n Let's explore how VIPTest works and how you can leverage it in your F5 environment. \n How VIPTest Works \n VIPTest, available on GitHub, accepts a CSV file containing URLs and IP addresses. It then: \n \n Validates URLs and IP addresses \n Performs HTTP/HTTPS requests \n Checks TLS versions and ciphers \n Tests connectivity (ping and telnet) \n Reports results for easy comparison \n \n Key Features \n \n \n Concurrent Processing: VIPTest uses Python's multiprocessing module to test as many URLs simultaneously as your client machine can support. On my modest Intel 10th gen / 6 Core laptop, I can test 1000 URLs in less than one minute. \n \n \n Flexible URL Handling: It supports various URL formats, including: \n \n Full URLs (http://example.com) \n IP addresses with ports (192.168.1.1:80) \n URLs with custom ports (https://example.com:8443) \n \n \n \n Detailed Reporting: VIPTest provides information on: \n \n HTTP response codes \n TLS versions and ciphers \n IP resolution \n Connectivity status \n \n \n \n VIPTest works with Linux, Windows (WSL2), and MacOS (Zsh using homebrew). \n Setting Up VIPTest \n See README.md for full installation and useage details. \n \n \n Ensure you have Python 3.8+ installed: \n python3 --version \n \n Create a virtual environment: \n python3 -m venv myenv\nsource myenv/bin/activate \n \n Clone the VIPTest repository: \n git clone https://github.com/tmarfil/viptest.git\ncd viptest \n \n Install requirements: \n pip3 install -r requirements.txt \n \n Make viptest.py executable: \n chmod +x viptest.py \n \n Using VIPTest \n \n \n Create a CSV file with your URLs (see README.md for format details). \n \n \n Run VIPTest: \n viptest.py --csv your_file.csv -c 50 \n This tests URLs concurrently using 50 processes. \n \n \n Save the output: \n viptest.py --csv your_file.csv -c 50 > pre_change_results.log \n \n Make your environment changes. \n \n \n Run VIPTest again: \n viptest.py --csv your_file.csv -c 50 > post_change_results.log \n \n Compare results: \n diff pre_change_results.log post_change_results.log \n \n The Power of HTTPX \n VIPTest leverages the HTTPX client for Python3: \n \n HTTP/2 Support: Enables testing of modern application stacks. \n Automatic Retries: Improves reliability in unstable network conditions. \n Connection Pooling: Enhances performance for multiple requests to the same host. \n Timeout Handling: Prevents hung requests from slowing down the entire test suite. \n \n Concurrent Testing with Multiprocessing \n The Python multiprocessing package is crucial for VIPTest's performance. Here's how it works: \n \n The URL list is divided into chunks. \n Each chunk is processed by a separate Python process. \n Results are collected in a shared queue. \n \n This approach allows VIPTest to test one thousand URLs in less than one minute. The Python code implements this using: \n if args.concurrent:\n chunk_size = len(urls) // args.concurrent\n chunks = list(chunked_iterable(urls, chunk_size))\n processes = []\n for chunk in chunks:\n p = multiprocessing.Process(target=process_urls, args=(chunk, output_queue, counter))\n processes.append(p)\n p.start() \n Automating with CI/CD \n Integrating VIPTest into your CI/CD pipeline ensures consistent testing. Here's a basic GitHub Actions workflow: \n name: VIPTest\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v2\n - name: Set up Python\n uses: actions/setup-python@v2\n with:\n python-version: '3.12'\n - name: Install dependencies\n run: |\n python -m pip install --upgrade pip\n pip install -r requirements.txt\n - name: Run VIPTest\n run: |\n viptest.py --csv testfile.csv -c 50 > test_results.log\n - name: Upload results\n uses: actions/upload-artifact@v2\n with:\n name: test-results\n path: test_results.log \n This workflow runs VIPTest on every push to the main branch or pull request, saving the results as an artifact. \n For GitLab users, a similar pipeline can be created using .gitlab-ci.yml: \n stages:\n - test\n\nviptest:\n stage: test\n image: python:3.12\n script:\n - pip install -r requirements.txt\n - viptest.py --csv testfile.csv -c 50 > test_results.log\n artifacts:\n paths:\n - test_results.log \n Conclusion \n VIPTest empowers F5 customers to confidently make environment changes across all F5 solutions - from Distributed Cloud (XC) to BIG-IP Next and NGINX. By providing quick, comprehensive application testing, it reduces risk and improves the success rate of migrations, upgrades, and day-to-day operations. Whether you're moving from a competitor's solution or optimizing your current F5 setup, VIPTest is an invaluable tool in your toolkit. \n Start using VIPTest today to ensure your applications stay healthy through every environment change! ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"7381","kudosSumWeight":5,"repliesCount":2,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:290328":{"__typename":"Conversation","id":"conversation:290328","topic":{"__typename":"TkbTopicMessage","uid":290328},"lastPostingActivityTime":"2024-08-29T03:26:38.635-07:00","solved":false},"TkbTopicMessage:message:290328":{"__typename":"TkbTopicMessage","subject":"Demystifying iControl REST Part 7 - Understanding Transactions","conversation":{"__ref":"Conversation:conversation:290328"},"id":"message:290328","revisionNum":2,"uid":290328,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:51154"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":3182},"postTime":"2016-08-12T14:30:00.000-07:00","lastPublishTime":"2023-06-05T22:49:39.204-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" iControl REST. It’s iControl SOAP’s baby, brother, introduced back in TMOS version 11.4 as an early access feature but released fully in version 11.5. Several articles on basic usage have been written about the rest interface so the intent here isn’t basic use, but rather to demystify some of the finer details of using the API. \n\n A few months ago, a question in Q&A from community member spirrello asking how to update a tcp profile on a virtual. He was using bigsuds, the python wrapper for the soap interface. For the rest interface on this particular object, this is easy; just use the put method and supply the payload mapping the updated profile. But for soap, this requires a transaction. There are some changes to BIG-IP via the rest interface, however, like updating an ssl cert or key, that likewise will require a transaction to accomplish. In this article, I’ll show you how to use transactions with the rest interface. \n\n The Fine Print \n\n From the iControl REST user guide, the life cycle of a transaction progresses through three phases: \n\n Creation - This phase occurs when the transaction is created using a POST command. Modification - This phase occurs when commands are added to the transaction, or changes are made to the sequence of commands in the transaction. Commit - This phase occurs when iControl REST runs the transaction. \n\n To create a transaction, post to /tm/transaction \n\n \n POST https://192.168.25.42/mgmt/tm/transaction\n\n {}\n\n Response:\n\n {\n \"transId\":1389812351,\n \"state\":\"STARTED\",\n \"timeoutSeconds\":30,\n \"kind\":\"tm:transactionstate\",\n \"selfLink\":\"https://localhost/mgmt/tm/transaction/1389812351?ver=11.5.0\"\n } \n\n Note the transId, the state, and the timeoutSeconds. You'll need the transId to add or re-sequence commands within the transaction, and the transaction will expire after 30 seconds if no commands are added. You can list all transactions, or the details of a specific transaction with a get request. \n\n \n GET https://192.168.25.42/mgmt/tm/transaction\n GET https://192.168.25.42/mgmt/tm/transaction/transId \n\n To add a command to the transaction, you use the normal method uris, but include the X-F5-REST-Coordination-Id header. This example creates a pool with a single member. \n\n \n POST https://192.168.25.42/mgmt/tm/ltm/pool\n X-F5-REST-Coordination-Id:1389812351\n {\n \"name\":\"tcb-xact-pool\",\n \"members\": [ {\"name\":\"192.168.25.32:80\",\"description\":\"First pool for transactions\"} ]\n } \n\n Not a great example because there is no need for a transaction here, but we'll roll with it! There are several other option methods for interrogating the transaction itself, see the user guide for details. Now we can commit the transaction. To do that, you reference the transaction id in the URI, remove the X-F5-REST-Coordination-Id header and use the patch method with payload key/value state: VALIDATING . \n\n \n PATCH https://localhost/mgmt/tm/transaction/1389812351\n { \"state\":\"VALIDATING\" } \n\n That's all there is to it! Now that you've seen the nitty gritty details, let's take a look at some code samples. \n\n Roll Your Own \n\n In this example, I am needing to update and ssl key and certificate. If you try to update the cert or the key, it will complain that they do not match, so you need to update both at the same time. Assuming you are writing all your code from scratch, this is all it takes in python. Note on line 21 I post with an empty payload, and then on line 23, I add the header with the transaction id. I make my modifications and then in line 31, I remove the header, and finally on line 32, I patch to the transaction id with the appropriate payload. \n\n \n import json\n import requests\n\n btx = requests.session()\n btx.auth = (f5_user, f5_password)\n btx.verify = False\n btx.headers.update({'Content-Type':'application/json'})\n urlb = 'https://{0}/mgmt/tm'.format(f5_host)\n\n domain = 'mydomain.local_sslobj'\n chain = 'mychain_sslobj\n\n try:\n key = btx.get('{0}/sys/file/ssl-key/~Common~{1}'.format(urlb, domain))\n cert = btx.get('{0}/sys/file/ssl-cert/~Common~{1}'.format(urlb, domain))\n chain = btx.get('{0}/sys/file/ssl-cert/~Common~{1}'.format(urlb, 'chain'))\n\n if (key.status_code == 200) and (cert.status_code == 200) and (chain.status_code == 200):\n\n # use a transaction\n txid = btx.post('{0}/transaction'.format(urlb), json.dumps({})).json()['transId']\n # set the X-F5-REST-Coordination-Id header with the transaction id\n btx.headers.update({'X-F5-REST-Coordination-Id': txid})\n\n # make modifications\n modkey = btx.put('{0}/sys/file/ssl-key/~Common~{1}'.format(urlb, domain), json.dumps(keyparams))\n modcert = btx.put('{0}/sys/file/ssl-cert/~Common~{1}'.format(urlb, domain), json.dumps(certparams))\n modchain = btx.put('{0}/sys/file/ssl-cert/~Common~{1}'.format(urlb, 'le-chain'), json.dumps(chainparams))\n\n # remove header and patch to commit the transaction\n del btx.headers['X-F5-REST-Coordination-Id']\n cresult = btx.patch('{0}/transaction/{1}'.format(urlb, txid), json.dumps({'state':'VALIDATING'})).json() \n\n A Little Help from a Friend \n\n The f5-common-python library was released a few months ago to relieve you of a lot of the busy work with building requests. This is great, especially for transactions. To simplify the above code just to the transaction steps, consider: \n\n \n # use a transaction\n txid = btx.post('{0}/transaction'.format(urlb), json.dumps({})).json()['transId']\n # set the X-F5-REST-Coordination-Id header with the transaction id\n btx.headers.update({'X-F5-REST-Coordination-Id': txid})\n\n# do stuff here\n\n # remove header and patch to commit the transaction\n del btx.headers['X-F5-REST-Coordination-Id']\n cresult = btx.patch('{0}/transaction/{1}'.format(urlb, txid), json.dumps({'state':'VALIDATING'})).json() \n\n With the library, it's simplified to: \n\n \n tx = b.tm.transactions.transaction\n with TransactionContextManager(tx) as api:\n # do stuff here\n api.do_stuff \n\n Yep, it's that simple. So if you haven't checked out the f5-common-python library, I highly suggest you do! I'll be writing about how to get started using it next week, and perhaps a follow up on how to contribute to it as well, so stay tuned! ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"6407","kudosSumWeight":2,"repliesCount":9,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:331086":{"__typename":"Conversation","id":"conversation:331086","topic":{"__typename":"TkbTopicMessage","uid":331086},"lastPostingActivityTime":"2024-07-09T08:53:30.497-07:00","solved":false},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzEwODYtTFQwTzlw?revision=2\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzEwODYtTFQwTzlw?revision=2","title":"CoverPhoto_0018_philipp-trubchenko-jObj_p885Gg-unsplash.jpg","associationType":"TEASER","width":1000,"height":1000,"altText":""},"TkbTopicMessage:message:331086":{"__typename":"TkbTopicMessage","subject":"BIG-IP Next Central Manager API with Python","conversation":{"__ref":"Conversation:conversation:331086"},"id":"message:331086","revisionNum":2,"uid":331086,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:51154"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":" \n ","introduction":"","metrics":{"__typename":"MessageMetrics","views":143},"postTime":"2024-06-28T14:57:28.614-07:00","lastPublishTime":"2024-07-09T08:53:30.497-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" I'm working my way through the various tools in the toolbox to interact with the Central Manager API. I've covered cURL and Postman already, and in this live stream recorded June 27, 2024, I shared a utility I'm currently building (running title F5-NCM for Next Central Manager) to work with the API in the python programming language. It's early stages, and how much time I spend developing it will depend on how much interest there is from the community. \n \n \n Make sure to check out the f5-ncm project on GitHub and let me know what you think! ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"558","kudosSumWeight":1,"repliesCount":0,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDE","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzEwODYtTFQwTzlw?revision=2\"}"}}],"totalCount":1,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:330438":{"__typename":"Conversation","id":"conversation:330438","topic":{"__typename":"TkbTopicMessage","uid":330438},"lastPostingActivityTime":"2024-06-28T05:00:00.043-07:00","solved":false},"User:user:72057":{"__typename":"User","uid":72057,"login":"ArvinF","registrationData":{"__typename":"RegistrationData","status":null},"deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://community.f5.com/t5/s/zihoc95639/images/dS03MjA1Ny1ndTdUdTE?image-coordinates=90%2C126%2C444%2C481"},"id":"user:72057"},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtMHpHbUZX?revision=7\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtMHpHbUZX?revision=7","title":"f5sirt.jpg","associationType":"TEASER","width":680,"height":383,"altText":""},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtdGhPeGI4?revision=7\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtdGhPeGI4?revision=7","title":"dns_stat_sample.PNG","associationType":"BODY","width":551,"height":1005,"altText":""},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0Mzgtc0hFTXZR?revision=7\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0Mzgtc0hFTXZR?revision=7","title":"fsmtabulated_ss.JPG","associationType":"BODY","width":586,"height":708,"altText":""},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtWWFRYXBZ?revision=7\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtWWFRYXBZ?revision=7","title":"afm-dns-dos.JPG","associationType":"BODY","width":2061,"height":911,"altText":""},"TkbTopicMessage:message:330438":{"__typename":"TkbTopicMessage","subject":"Parsing tmsh command output with Python TextFSM module and some Lessons learned","conversation":{"__ref":"Conversation:conversation:330438"},"id":"message:330438","revisionNum":7,"uid":330438,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:72057"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":" \n ","introduction":"","metrics":{"__typename":"MessageMetrics","views":457},"postTime":"2024-06-28T05:00:00.043-07:00","lastPublishTime":"2024-06-28T05:00:00.043-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" It’s been a while! I’m back with some code to parse tmsh command output, extracting specific sections of the output and present it in easy-to-read format. \n In my previous article, BIG-IP Advanced Firewall Manager (AFM) DNS NXDOMAIN Query Attack Type Walkthrough, I intended to gather DNS statistics and packet captures from the BIG-IP, use the observed stats to understand the DNS traffic, and use it as basis for detection and mitigation threshold for BIG-IP AFM DNS NXDOMAIN Query Attack Types. \n With the same goal, my use case is parsing DNS statistics from the command output tmsh show ltm profile dns <DNS profile>. I'm interested in the \"A\" Question Type and \"NXDOMAIN\" Response Message Return Code (RCODE) values. \n \n TextFSM \n TextFSM is a library used to parse semi-structured text data into structured data using templates that define patterns to match. \n As I'm using VSCode, here is the description of the textfsm module: \n ============= \n (module) textfsm \n Template based text parser. \n This module implements a parser, intended to be used for converting human readable text, such as command output from a router CLI, into a list of records, containing values extracted from the input text. \n A simple template language is used to describe a state machine to parse a specific type of text input, returning a record of values for each input entity. \n ============= \n I will be using TextFSM python module to create a template where regular expressions are defined to match values of interest and store them into records. \n I created 4 files and can be found in https://github.com/arvfopa/scripts \n dns_stat.txt is the output of the bash script that gather periodically the tmsh show ltm profile dns command output. Here's the sample: \n =========== \n while true; do date >> dns_stat.txt; tmsh show ltm profile dns {dns profile name} >> dns_stat.txt; sleep 20; echo \"#######\" >> dns_stat.txt; done \n =========== \n In this dns stat file, I wanted to extract the Date the command was run, the value of the A (host) record in the Question Type section and the No Name (NXDOMAIN) value in the Return Code (RCODE) section. \n \n https://github.com/arvfopa/scripts/blob/main/dns_stat.txt \n dns_t_1column.txt is the TextFSM template where I defined regular expressions to match the values of interest. \n https://github.com/arvfopa/scripts/blob/main/dns_t_1column.txt \n ======= \n Value DATE (\\S+.*) \n Value DNS_Q_typeA (\\d+) \n Value DNS_R_NXDOMAIN (\\d+) \n \n Start \n ^Date: ${DATE} \n ^\\s+A\\s+${DNS_Q_typeA} \n ^Return Code \\(RCODE\\) \n ^\\s{3}No Name \\(NXDOMAIN\\)\\s+${DNS_R_NXDOMAIN} -> Record \n ======= \n \"Value\" Definitions \n These lines define variables (values) that will capture specific parts of the text based on the provided regular expressions (regex). \n \"Start\" State \n This section defines the start state of the FSM (Finite State Machine) and the patterns it should match to transition states and capture values. \n As in my previous article, I also used ChatGPT a lot in creating and testing the scripts. When I had questions, I simply pasted the code and ChatGPT gives the explanation such as when I was trying to figure out the regular expression to match specifically the values in the dns stat sample file - which text and values, how many spaces in between. ChatGPT was very handy in the coding process especially when I can just prompt it to \"explain\". \n textfsm_dns_stat_parse.py is the python script which imports the textfsm module and use the TextFSM template created to parse the dns stat sample file. The parsed values are then tabulated. \n https://github.com/arvfopa/scripts/blob/main/textfsm_dns_stat_parse.py \n =========== \n import textfsm \n from tabulate import tabulate \n \n with open('dns_t_1column.txt') as template, open('dns_stat.txt') as dnsstat: \n re_table = textfsm.TextFSM(template) \n header = re_table.header \n result = re_table.ParseText(dnsstat.read()) \n tabulated = (tabulate(result, headers=header)) \n print(tabulated) \n \n with open('fsmtabulated.txt', mode='w', newline='') as file: \n file.write(tabulated) \n =========== \n fsmtabulated.txt is the output file of the textfsm_dns_stat_parse.py where the extracted values from the dns stat sample file are neatly tabulated. \n https://github.com/arvfopa/scripts/blob/main/fsmtabulated.txt \n \n As observed from the tabulated output, DNS_Q_typeA values refer to the A type DNS queries and DNS_R_NXDOMAIN are the DNS NXDOMAIN responses. Both values show an increase almost proportionally. \n For a BIG-IP administrator, an increase of DNS A queries may be expected, however, if the DNS NXDOMAIN response also increases, it may be that the DNS listener Virtual Server that uses the DNS profile is receiving a flood of DNS A queries for hostnames that do not exist in the backend DNS server pool and consumes resources - CPU, memory and bandwidth - and overwhelm the DNS servers. \n BIG-IP AFM have DNS DoS Attack Vectors that can be configured to detect and mitigate DNS DoS attacks. Making use of the dns stat data, we can configure detection and mitigation thresholds for DNS A Query and DNS NXDOMAIN Query to detect at 4000 EPS (events per second) and mitigate at 6000 EPS. Exceeding packets on the detected and mitigated vector will be dropped until the mitigation threshold is not exceeded. \n This will prevent excess DNS queries of certain types from reaching the backend DNS server and keep the service available. \n \n \n Lessons Learned \n In the previous Parsing F5 BIG-IP LTM DNS profile statistics and extracting values with Python article, I created a python script that sent iControl REST request thru the BIGREST module to a BIG-IP for DNS stats from a VS with a DNS profile. \n Regarding this error: \n raise SSLError(e, request=request) \n requests.exceptions.SSLError: HTTPSConnectionPool(host='IP address', port=443): Max retries exceeded with url: /mgmt/shared/echo-query (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1007)'))) \n The \"certificate verify failed: self signed certificate\" error can be side stepped by setting the \"session_verify=False\" of the BIGIP class of the BIGREST module. While this is possible, using a TLS certificate on the BIG-IP that is trusted by the client that will run the python script is the secure option. \n Procedure on creating TLS device certificate for the BIG-IP can be found in these articles. \n K9114: Creating a new SSL device certificate and key pair \n K6353: Updating a self-signed SSL device certificate on a BIG-IP system \n Import the BIG-IP device certificate to the client's trusted certificate store or if the BIG-IP device TLS certificate is signed by a trusted CA, the client should be able to verify the BIG-IP device TLS certificate. \n On the python script, the variable that requests the stats from the DNS profile used in the DNS listener VS value is set to the URI for the specific VS name and the DNS profile. \n data = device.show(\"/mgmt/tm/ltm/virtual/dns-10.73.125.137/profiles/~Common~dns-p-1/\") \n The script can be modified to reference a different VS and a different DNS profile configured on the VS \n data = device.show(\"/mgmt/tm/ltm/virtual/<DNS Listener VS name>/profiles/<DNS Profile>/\") \n \n Conclusion \n Gathering periodic data from tmsh output and parsing it using tools such as python scripts can help BIG-IP administrators as it presents the collected stats in a human friendly format, instead of scrolling thru many lines of tmsh command output. The observed values can be then used to make configuration decisions as in the use case for this python script such as configuring detection and mitigation thresholds for DNS A Query and DNS NXDOMAIN Query to detect and mitigate DNS DoS attacks. Lastly, while there are existing tools/reporting - example, in the BIG-IP GUI, one can go to Statistics ›› Module Statistics : DNS : Delivery ›› GTM Listeners : <DNS VS Listener> and observe the profile statistics including from DNS profiles. This exercise offers alternative data gathering and analysis option for a BIG-IP administrator. \n I hope this article has been educational. \n The F5 SIRT creates security-related content posted here in DevCentral, sharing the team’s security mindset and knowledge. Feel free to view the articles that are tagged with the following: \n \n F5 SIRT \n series-F5SIRT-this-week-in-security \n TWIS \n ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"8724","kudosSumWeight":0,"repliesCount":0,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDE","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtMHpHbUZX?revision=7\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDI","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtdGhPeGI4?revision=7\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDM","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0Mzgtc0hFTXZR?revision=7\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDQ","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMzA0MzgtWWFRYXBZ?revision=7\"}"}}],"totalCount":4,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:286793":{"__typename":"Conversation","id":"conversation:286793","topic":{"__typename":"TkbTopicMessage","uid":286793},"lastPostingActivityTime":"2024-01-05T16:01:57.515-08:00","solved":false},"TkbTopicMessage:message:286793":{"__typename":"TkbTopicMessage","subject":"Demystifying iControl REST Part 6: Token-Based Authentication","conversation":{"__ref":"Conversation:conversation:286793"},"id":"message:286793","revisionNum":2,"uid":286793,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:51154"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":19424},"postTime":"2015-11-18T03:00:00.000-08:00","lastPublishTime":"2024-01-05T16:01:57.515-08:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" iControl REST. It’s iControl SOAP’s baby, brother, introduced back in TMOS version 11.4 as an early access feature but released fully in version 11.5. \n Several articles on basic usage have been written on iControl REST so the intent here isn’t basic use, but rather to demystify some of the finer details of using the API. This article will cover the details on how to retrieve and use an authentication token from the BIG-IP using iControl REST and the python programming language. This token is used in place of basic authentication on API calls, which is a requirement for external authentication. Note that for configuration changes, version 12.0 or higher is required as earlier versions will trigger an un-authorized error. \n \n The tacacs config in this article is dependent on a version that I am no longer able to get installed on a modern linux flavor. Instead, try this Dockerized tacacs+ server for your testing. \n \n The Fine Print \n The details of the token provider are here in the wiki. We’ll focus on a provider not listed there: tmos. This provider instructs the API interface to use the provider that is configured in tmos. For this article, I’ve configured a tacacs server and the BIG-IP with custom remote roles as shown below to show BIG-IP version 12’s iControl REST support for remote authentication and authorization. Details for how this configuration works can be found in the tacacs+ article I wrote a while back. \n BIG-IP tacacs+ configuration \n auth remote-role {\n role-info {\n adm {\n attribute F5-LTM-User-Info-1=adm\n console %F5-LTM-User-Console\n line-order 1\n role %F5-LTM-User-Role\n user-partition %F5-LTM-User-Partition\n }\n mgr {\n attribute F5-LTM-User-Info-1=mgr\n console %F5-LTM-User-Console\n line-order 2\n role %F5-LTM-User-Role\n user-partition %F5-LTM-User-Partition\n }\n }\n}\nauth remote-user { }\nauth source {\n type tacacs\n}\nauth tacacs system-auth {\n debug enabled\n protocol ip\n secret $M$Zq$T2SNeIqxi29CAfShLLqw8Q==\n servers { 172.16.44.20 }\n service ppp\n} \n Tacacs+ Server configuration \n id = tac_plus {\ndebug = PACKET AUTHEN AUTHOR\n\naccess log = /var/log/access.log\naccounting log = /var/log/acct.log\n\nhost = world {\naddress = ::/0\nprompt = \"\\nAuthorized Access Only!\\nTACACS+ Login\\n\"\nkey = devcentral\n}\n\ngroup = adm {\nservice = ppp {\nprotocol = ip {\nset F5-LTM-User-Info-1 = adm\nset F5-LTM-User-Console = 1\nset F5-LTM-User-Role = 0\nset F5-LTM-User-Partition = all\n}\n}\n}\n\n group = mgr {\n service = ppp {\n protocol = ip {\n set F5-LTM-User-Info-1 = mgr\n set F5-LTM-User-Console = 1\n set F5-LTM-User-Role = 100\n set F5-LTM-User-Partition = all\n }\n }\n }\n\nuser = user_admin {\npassword = clear letmein00\nmember = adm\n}\nuser = user_mgr {\npassword = clear letmein00\nmember = mgr\n}\n} \n Basic Requirements \n Before we look at code, however, let’s take a look at the json payload requirements, followed by response data from a query using Chrome’s Advanced REST Client plugin. First, since we are sending json payload, we need to add the Content-Type: application/json header to the query. The payload we are sending with the post looks like this: \n {\n\"username\": \"remote_auth_user\",\n\"password\": \"remote_auth_password\",\n\"loginProviderName\": \"tmos\"\n} \n You submit the same remote authentication credentials in the initial basic authentication as well, no need to have access to the default admin account credentials. A successful query for a token returns data like this: \n {\nusername: \"user_admin\"\nloginReference: {\nlink: \"https://localhost/mgmt/cm/system/authn/providers/tmos/1f44a60e-11a7-3c51-a49f-82983026b41b/login\"\n}-\ntoken: {\nuuid: \"4d1bd79f-dca7-406b-8627-3ad262628f31\"\nname: \"5C0F982A0BF37CBE5DE2CB8313102A494A4759E5704371B77D7E35ADBE4AAC33184EB3C5117D94FAFA054B7DB7F02539F6550F8D4FA25C4BFF1145287E93F70D\"\ntoken: \"5C0F982A0BF37CBE5DE2CB8313102A494A4759E5704371B77D7E35ADBE4AAC33184EB3C5117D94FAFA054B7DB7F02539F6550F8D4FA25C4BFF1145287E93F70D\"\nuserName: \"user_admin\"\nuser: {\nlink: \"https://localhost/mgmt/cm/system/authn/providers/tmos/1f44a60e-11a7-3c51-a49f-82983026b41b/users/34ba3932-bfa3-4738-9d55-c81a1c783619\"\n}-\ngroupReferences: [1]\n0: {\nlink: \"https://localhost/mgmt/cm/system/authn/providers/tmos/1f44a60e-11a7-3c51-a49f-82983026b41b/user-groups/21232f29-7a57-35a7-8389-4a0e4a801fc3\"\n}-\n-\ntimeout: 1200\nstartTime: \"2015-11-17T19:38:50.415-0800\"\naddress: \"172.16.44.1\"\npartition: \"[All]\"\ngeneration: 1\nlastUpdateMicros: 1447817930414518\nexpirationMicros: 1447819130415000\nkind: \"shared:authz:tokens:authtokenitemstate\"\nselfLink: \"https://localhost/mgmt/shared/authz/tokens/4d1bd79f-dca7-406b-8627-3ad262628f31\"\n}-\ngeneration: 0\nlastUpdateMicros: 0\n} \n Among many other fields, you can see the token field with a very long hexadecimal token. That’s what we need to authenticate future API calls. \n Requesting the token programmatically \n In order to request the token, you first have to supply basic auth credentials like normal. This is currently required to access the /mgmt/shared/authn/login API location. The basic workflow is as follows (with line numbers from the code below in parentheses): \n \n Make a POST request to BIG-IP with basic authentication header and json payload with username, password, and the login provider (9-16, 41-47) \n Remove the basic authentication (49) \n Add the token from the post response to the X-F5-Auth-Token header (50) \n Continue further requests like normal. In this example, we’ll create a pool to verify read/write privileges. (1-6, 52-53) \n \n And here’s the code (in python) to make that happen: \n def create_pool(bigip, url, pool):\n payload = {}\n payload['name'] = pool\n\n pool_config = bigip.post(url, json.dumps(payload)).json()\n return pool_config\n\n\ndef get_token(bigip, url, creds):\n payload = {}\n payload['username'] = creds[0]\n payload['password'] = creds[1]\n payload['loginProviderName'] = 'tmos'\n\n token = bigip.post(url, json.dumps(payload)).json()['token']['token']\n return token\n\n\nif __name__ == \"__main__\":\n import os, requests, json, argparse, getpass\n requests.packages.urllib3.disable_warnings()\n\n parser = argparse.ArgumentParser(description='Remote Authentication Test - Create Pool')\n\n parser.add_argument(\"host\", help='BIG-IP IP or Hostname', )\n parser.add_argument(\"username\", help='BIG-IP Username')\n parser.add_argument(\"poolname\", help='Key/Cert file names (include the path.)')\n args = vars(parser.parse_args())\n\n hostname = args['host']\n username = args['username']\n poolname = args['poolname']\n\n print \"%s, enter your password: \" % args['username'],\n password = getpass.getpass()\n\n url_base = 'https://%s/mgmt' % hostname\n url_auth = '%s/shared/authn/login' % url_base\n url_pool = '%s/tm/ltm/pool' % url_base\n\n b = requests.session()\n b.headers.update({'Content-Type':'application/json'})\n b.auth = (username, password)\n b.verify = False\n\n token = get_token(b, url_auth, (username, password))\n print '\\nToken: %s\\n' % token\n\n b.auth = None\n b.headers.update({'X-F5-Auth-Token': token})\n\n response = create_pool(b, url_pool, poolname)\n print '\\nNew Pool: %s\\n' % response \n Running this script from the command line, we get the following response: \n FLD-ML-RAHM:scripts rahm$ python remoteauth.py 172.16.44.15 user_admin myNewestPool1\nPassword: user_admin, enter your password:\n \nToken: 2C61FE257C7A8B6E49C74864240E8C3D3592FDE9DA3007618CE11915F1183BF9FBAF00D09F61DE15FCE9CAB2DC2ACC165CBA3721362014807A9BF4DEA90BB09F\n\n\nNew Pool: {u'generation': 453, u'minActiveMembers': 0, u'ipTosToServer': u'pass-through', u'loadBalancingMode': u'round-robin', u'allowNat': u'yes', u'queueDepthLimit': 0, u'membersReference': {u'isSubcollection': True, u'link': u'https://localhost/mgmt/tm/ltm/pool/~Common~myNewestPool1/members?ver=12.0.0'}, u'minUpMembers': 0, u'slowRampTime': 10, u'minUpMembersAction': u'failover', u'minUpMembersChecking': u'disabled', u'queueTimeLimit': 0, u'linkQosToServer': u'pass-through', u'queueOnConnectionLimit': u'disabled', u'fullPath': u'myNewestPool1', u'kind': u'tm:ltm:pool:poolstate', u'name': u'myNewestPool1', u'allowSnat': u'yes', u'ipTosToClient': u'pass-through', u'reselectTries': 0, u'selfLink': u'https://localhost/mgmt/tm/ltm/pool/myNewestPool1?ver=12.0.0', u'serviceDownAction': u'none', u'ignorePersistedWeight': u'disabled', u'linkQosToClient': u'pass-through'}\n \n You can test this out in the Chrome Advanced Rest Client plugin, or from the command line with curl or any other language supporting REST clients as well, I just use python for the examples well, because I like it. I hope you all are digging into iControl REST! What questions do you have? What else would you like clarity on? Drop a comment below. ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"8919","kudosSumWeight":0,"repliesCount":42,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:325476":{"__typename":"Conversation","id":"conversation:325476","topic":{"__typename":"TkbTopicMessage","uid":325476},"lastPostingActivityTime":"2024-01-02T08:56:15.032-08:00","solved":false},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjcxMjdpRTE3NjA1ODYyMUE2QUE1Mg?revision=14\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjcxMjdpRTE3NjA1ODYyMUE2QUE1Mg?revision=14","title":"detection.jpg","associationType":"COVER","width":510,"height":510,"altText":""},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTZpMzIyMDYwMjNDQjFERUI3Nw?revision=14\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTZpMzIyMDYwMjNDQjFERUI3Nw?revision=14","title":"VSCode-tooltips-2.PNG","associationType":"BODY","width":3635,"height":1474,"altText":null},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTdpQkMxOTI3RDgwNjI2MEJFQw?revision=14\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTdpQkMxOTI3RDgwNjI2MEJFQw?revision=14","title":"bigrest+show-ltm-profile-dns-dnsprofile+json+csv.PNG","associationType":"BODY","width":1980,"height":786,"altText":null},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MThpQzAwNjkxNUFFRUVGMkVCOQ?revision=14\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MThpQzAwNjkxNUFFRUVGMkVCOQ?revision=14","title":"bigrest+show-ltm-profile-dns-dnsprofile+json+csv-output.PNG","associationType":"BODY","width":1099,"height":832,"altText":null},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTlpMkY5MkIzQTE5MEVBN0NGMA?revision=14\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTlpMkY5MkIzQTE5MEVBN0NGMA?revision=14","title":"bigrest+show-ltm-profile-dns-dnsprofile+json+csv-output-csv.PNG","associationType":"BODY","width":969,"height":552,"altText":null},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MjBpNDY1NjcwNDFBNjYwQkU2MQ?revision=14\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MjBpNDY1NjcwNDFBNjYwQkU2MQ?revision=14","title":"bigrest_properties_py_dictionary_json.PNG","associationType":"BODY","width":1846,"height":410,"altText":null},"TkbTopicMessage:message:325476":{"__typename":"TkbTopicMessage","subject":"Parsing F5 BIG-IP LTM DNS profile statistics and extracting values with Python","conversation":{"__ref":"Conversation:conversation:325476"},"id":"message:325476","revisionNum":14,"uid":325476,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:72057"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":831},"postTime":"2023-12-26T05:00:00.062-08:00","lastPublishTime":"2024-01-02T08:56:15.032-08:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" \n Introduction \n Hello there! Arvin here from the F5 SIRT. \n A little while ago, I published F5 BIG-IP Advanced Firewall Manager (AFM) DNS NXDOMAIN Query Attack Type Walkthrough part one and two, where I went through the process of reviewing BIG-IP LTM DNS profile statistics and used it to set BIG-IP AFM DNS NXDOMAIN Query attack type detection and mitigation thresholds with the goal of mitigating DNS NXDOMAIN Floods. \n In this article, I continue to look at BIG-IP LTM DNS profile statistics, find ways of parsing it and to extract specific values of interest through Python. \n Python for Network Engineers \n Python has emerged as a go-to language for network engineers, providing a powerful and accessible toolset for managing and automating network tasks. Known for its simplicity and readability, Python enables network engineers to script routine operations, automate repetitive tasks, and interact with network devices through APIs. With extensive libraries and frameworks tailored to networking, Python empowers engineers to streamline configurations, troubleshoot issues, and enhance network efficiency. Its versatility makes it an invaluable asset for network automation, allowing engineers to adapt to evolving network requirements and efficiently manage complex infrastructures. Whether you're retrieving data, configuring devices, or optimizing network performance, Python simplifies the process for network engineers, making it an essential skill in the modern networking landscape. \n The Tools \n ChatGPT3.5 \n The \"Python for Network Engineers\" intro came from ChatGPT3.5 [:)]. Throughout this article, the python coding \"bumps\" avoidance and approaches came from ChatGPT3.5. Instead of googling, I asked ChatGPT \"a lot\" so I could get the python scripts to get the output I wanted. \n https://chat.openai.com/ \n Visual Studio Code \n Using Visual Studio Code (VSCode) to build the scripts was very helpful, especially the tooltip / hints which tells me and help make sense of the available options for the modules used and describing the python data structures. \n \n Python 3.10 \n (From ChatGPT) Python 3.10, the latest version of the Python programming language, brings forth new features and optimizations that enhance the language's power and simplicity. With Python's commitment to readability and ease of use, version 3.10 introduces structural pattern matching, allowing developers to express complex logic more concisely. Other improvements include precise types, performance enhancements, and updates to syntax for cleaner code. Python 3.10 continues to be a versatile and accessible language, serving diverse needs from web development to data science and automation. Its vibrant community and extensive ecosystem of libraries make Python 3.10 a top choice for developers seeking both efficiency and clarity in their code. \n \n \n \n \n \n \n \n \n \n \n \n \n Python Script - extract DNS A requests value from LTM DNS profile statistics iControl REST output \n This python script will extract DNS A requests value from LTM DNS profile statistics iControl REST output. Python has many modules that can be used to simplify tasks. iControl REST output is in json format, so as expected, I used the json module. I wanted to format the output data in csv format so the extracted data can later be used in other tools that consume csv formatted data, thus, I used the csv module. I also used the os, time/datetime and tabulate modules for working with the filesystem (I used a Windows machine to run Python and VSCode) to write csv files. Create variables with date and time information that will be used in formatting the csv file name, keep track of the \"A record requests\" value at script execution, and present a tabulated output of the captured time and data when the script is executed. \n I also used BIGREST module to query/retrieve the \"show ltm dns profile <DNS profile> statistics\" instead of getting the output from iControl REST request sent through other methods. \n https://bigrest.readthedocs.io/introduction.html \n https://bigrest.readthedocs.io/bigip_show.html \n \n \n \n \n \n \n \n \n \n \n \n \n \n Here is the sample script output \n \n Here is the sample CSV-formatted data in a csv file with timestamp of the script run \n \n I created a github repository for the Python script and its sample script output and csv data \n see https://github.com/arvfopa/scripts/tree/main \n https://github.com/arvfopa/scripts/blob/main/extractAreq - Python Script \"extractAreq\" \n https://github.com/arvfopa/scripts/blob/main/extractAreq_output - \"extractAreq\" output \n Bumps along the way \n BIGREST module \n I initially encountered an error 'certificate verify failed: self signed certificate' when provided only the IP address and credentials used in the BIGIP class of the bigrest.bigip python module \n raise SSLError(e, request=request) requests.exceptions.SSLError: HTTPSConnectionPool(host='IP address', port=443): Max retries exceeded with url: /mgmt/shared/echo-query (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1007)'))) \n This is fixed by setting \"session_verify\" argument of the BIGIP class to \"false\" to disables SSL certificate validation \n \n \n device = BIGIP(\"<IP address>\", \"<username>\", \"<password>\" , session_verify=False) \n \n https://bigrest.readthedocs.io/utils.html \n I also received this error \"TypeError: Object of type RESTObject is not JSON serializable\" \n raise TypeError(f'Object of type {o.__class__.__name__} ' TypeError: Object of type RESTObject is not JSON serializable \n I reread the BIGREST documentation and found that the output is a python dictionary and can is printed in json format. I rechecked the script and removed the json related syntax and module, and the script runs fine and still gets the same output. I updated the script on github with the simplified changes. \n https://bigrest.readthedocs.io/restobject.html \n Here's a sample of the RESTObject properties dictionary values. Plenty of data can be extracted. Example, \"clientside.pktsIn\" value, a virtual server statistic, can be observed and should detection and mitigation thresholds for AFM, say, UDP protocol DoS attack type, need to be set. This value can be monitored over time to understand how many packets a virtual server receives. \n \n ============== \n {'clientside.bitsIn': {'value': 0}, 'clientside.bitsOut': {'value': 0}, 'clientside.curConns': {'value': 0}, 'clientside.evictedConns': {'value': 0}, 'clientside.maxConns': {'value': 0}, 'clientside.pktsIn': {'value': 0}, 'clientside.pktsOut': {'value': 0}, 'clientside.slowKilled': {'value': 0}, 'clientside.totConns': {'value': 0}, 'cmpEnableMode': {'description': 'all-cpus'}, 'cmpEnabled': {'description': 'enabled'}, 'csMaxConnDur': {'value': 0}, 'csMeanConnDur': {'value': 0}, 'csMinConnDur': {'value': 0}, 'destination': {'description': '10.73.125.137:53'}, 'ephemeral.bitsIn': {'value': 0}, 'ephemeral.bitsOut': {'value': 0}, 'ephemeral.curConns': {'value': 0}, 'ephemeral.evictedConns': {'value': 0}, \n ============== \n CSV filename issue \n I encountered this error, \"OSError: [Errno 22] Invalid argument: 'dns_stats_2023-12-07_18:01:11.csv\". This is related to writing of the output csv file. I asked ChatGPT what this was about and was provided with this answer. \n ======================= \n The error you're encountering, \"[Errno 22] Invalid argument,\" typically suggests an issue with the filename or file path. In this case, it seems to be related to the colon (':') character in the filename. \n In some operating systems (like Windows), certain characters are not allowed in filenames, and \":\" is one of them. Since you're including a timestamp in the filename, it's common to replace such characters with alternatives. \n You can modify the timestamp format to use underscores or hyphens instead of colons. \n ==================== \n The timestamp variable in the script stores the value of the formatted timestamp that will be used in the filename. It initially used a colon (:) as the hour/min/sec separator. It was changed to dash (-) so it would not encounter this error. \n timestamp = datetime.now().strftime(\"%Y-%m-%d_%H-%M-%S\") \n Checking the file csv file if it exists \n The function \"write_to_csv\" writes the time of collection (formatted_date) and extracted value of DNS A requests count (AReqsvalue). It is called every 10 seconds [time.sleep(10)] for a minute [end_time = time.time() + 60] and writes the output to a file in csv format. The \"tabulate\" function formats the output of the script. Getting the arrangement of the execution wrong would result in unexpected output. The \"file_exists\" check to write the \"headers\" was added to make sure that the \"headers\" are only written once. \n \"write_to_csv\" function \n ======================== \n def write_to_csv(formatted_date, AReqsvalue): current_datetime = datetime.now() formatted_date = current_datetime.strftime(\"%Y-%m-%d %H:%M:%S\") \n csv_filename = f\"dns_stats_{timestamp}.csv\" headers = [\"Date\", \"DNS A requests\"] stats = [[formatted_date, AReqsvalue]] \n file_exists = os.path.exists(csv_filename) \n print(tabulate(stats, headers, tablefmt=\"fancy_grid\")) \n with open(csv_filename, mode='a', newline='') as file: writer = csv.writer(file) \n if not file_exists: writer.writerow(headers) \n writer.writerows(stats) \n end_time = time.time() + 60 \n while time.time() < end_time: write_to_csv(formatted_date, AReqsvalue) time.sleep(10) \n ========================== \n Using ChatGPT \n In building this script, I used ChatGPT \"a lot\" and it helped to provide make more sense of the module options, errors and sample scripts. It has been a helpful tool. \n It tracks your conversation/questions to it and kind of understands the context/topic. \n \"ChatGPT can make mistakes. Consider checking important information.\" is written at the bottom of the page. \n The data I used in this article are data from a lab environment. That said, when using public AI/ML systems, we should ensure we do not send any sensitive, proprietary information. Organizations have rolled out their own privacy policies when using AI/ML systems, be sure to follow your own organization's policies. \n Conclusion \n Using python to parse and extract values of interest from LTM profile statistics offers flexibility and hopefully simplifying observing and recording these data for further use. In particular, setting values for BIG-IP AFM DoS Detection and Mitigation thresholds will be easier if such data has been observed as it, in my opinion, is the \"pulse\" of the traffic the BIG-IP processes. As noted in the sample json data output, we can see many statistics that can be reviewed and observed to make configuration changes relevant, for example, mitigating a connection spike by setting a VS connection/rate limit. We can look at the \"Conns\" values and use the observed values to set a connection limit. \n Example: \n 'clientside.curConns': {'value': 0}, 'clientside.evictedConns': {'value': 0}, 'clientside.maxConns': {'value': 0}, 'clientside.totConns':{'value': 0} \n That's it for now. I hope this article has been educational. \n The F5 SIRT creates security-related content posted here in DevCentral, sharing the team's security mindset and knowledge. Feel free to view the articles that are tagged with the following: \n \n \n F5 SIRT \n series-F5SIRT-this-week-in-security \n TWIS \n \n \n \n \n \n \n ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"11821","kudosSumWeight":2,"repliesCount":0,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDE","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjcxMjdpRTE3NjA1ODYyMUE2QUE1Mg?revision=14\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDI","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTZpMzIyMDYwMjNDQjFERUI3Nw?revision=14\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDM","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTdpQkMxOTI3RDgwNjI2MEJFQw?revision=14\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDQ","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MThpQzAwNjkxNUFFRUVGMkVCOQ?revision=14\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDU","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MTlpMkY5MkIzQTE5MEVBN0NGMA?revision=14\"}"}},{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDY","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0zMjU0NzYtMjY4MjBpNDY1NjcwNDFBNjYwQkU2MQ?revision=14\"}"}}],"totalCount":6,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:286518":{"__typename":"Conversation","id":"conversation:286518","topic":{"__typename":"TkbTopicMessage","uid":286518},"lastPostingActivityTime":"2023-11-28T16:39:49.516-08:00","solved":false},"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODY1MTgtMTE1MTJpMjkwOTkyOTM4MzQ4REM1MA?revision=1\"}":{"__typename":"AssociatedImage","url":"https://community.f5.com/t5/s/zihoc95639/images/bS0yODY1MTgtMTE1MTJpMjkwOTkyOTM4MzQ4REM1MA?revision=1","title":"0151T000003d6Y3QAI.png","associationType":"BODY","width":300,"height":104,"altText":null},"TkbTopicMessage:message:286518":{"__typename":"TkbTopicMessage","subject":"Demystifying iControl REST Part 1 - Understanding the request URI","conversation":{"__ref":"Conversation:conversation:286518"},"id":"message:286518","revisionNum":1,"uid":286518,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:51154"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":5867},"postTime":"2015-06-17T08:00:00.000-07:00","lastPublishTime":"2015-06-17T08:00:00.000-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" iControl REST. It’s iControl SOAP’s baby brother, introduced back in TMOS version 11.4 as an early access feature but was released fully in version 11.5. \n\n \n\n Several articles on basic usage have been written on iControl REST (see the resources at the bottom of this article) so the intent here isn’t basic use, but rather to demystify some of the finer details of using the API. This article is the first of a four part series and will cover how the URI path plays a role in how the API functions. \n\n Examining the REST interface URI \n\n With iControl SOAP, all the interfaces and methods are defined in the WSDLs. With REST, the bulk of the interface and method is part of the URI. Take for example a request to get the list of iRules on a BIG-IP. \n\n https://x.x.x.x/mgmt/tm/ltm/rule \n\n With a simple get request with the appropriate headers and credentials (see below for curl and python examples,) this breaks down to the Base URI, the Module URI, and the Sub-Module URI. \n\n \n#python example\n>>> import requests\n>>> import json\n>>> b = requests.session()\n>>> b.auth = ('admin', 'admin')\n>>> b.verify = False\n>>> b.headers.update({'Content-Type':'application/json'})\n>>> b.get('https://172.16.44.128/mgmt/tm/ltm/rule')\n\n\n#curl example\ncurl -k -u admin:admin -H “Content-Type: application/json” -X GET https://172.16.44.128/mgmt/tm/ltm/rule\n \n\n Beyond the Sub-Module URI is the component URI. \n\n https://x.x.x.x/mgmt/tm/ltm/rule/testlog \n\n The component URI is the spot that snags most beginners. When creating a new configuration object on an F5, it is fairly obvious which URI to use for the REST call. If you were creating a new virtual server, it would be /mgmt/tm/ltm/virtual, while a new pool would be /mgmt/tm/ltm/pool. Thus a REST call to create a new pool on an LTM with the IP address 172.16.44.128 would look like the following: \n\n \n#python example\n>>> import requests\n>>> import json\n>>> b = requests.session()\n>>> b.auth = ('admin', 'admin')\n>>> b.verify = False\n>>> b.headers.update({'Content-Type':'application/json'})\n\n>>> pm = ['192.168.25.32:80', '192.168.25.33:80']\n>>> payload = { }\n>>> payload['kind'] = 'tm:ltm:pool:poolstate'\n>>> payload['name'] = 'tcb-pool'\n>>> payload['members'] = [ {'kind': 'ltm:pool:members', 'name': member } for member in pm]\n>>> b.post('https://172.16.44.128/mgmt/tm/ltm/pool', data=json.dumps(payload))\n\n\n#curl example\ncurl -k -u admin:admin -H \"Content-Type: \\\napplication/json\" -X POST -d \\\n'{\"name\":\"tcb-pool\",\"members\":[ \\\n{\"name\":\"192.168.25.32:80\",\"description\":\"first member”}, \\\n{\"name\":\"192.168.25.33:80\",\"description\":\"second member”} ] }' \\\nhttps://172.16.44.128/mgmt/tm/ltm/pool \n\n The call would create the pool “tcb-pool” with members at 192.168.25.32:80 and 192.168.25.33:80. All other aspects of the pool would be the defaults. Thus this pool would be created in the Common partition, have the Round Robin load balancing method, and no monitor. At first glance, some programmers would then modify the pool “tcb-pool” with a command like the following (same payload in python, added the .text attribute to see the error response on the requests object): \n\n \n#python example\n>>> b.put('https://172.16.44.128/mgmt/tm/ltm/pool', data=json.dumps(payload)).text\n#return data\nu'{\"code\":403,\"message\":\"Operation is not supported on component /ltm/pool.\",\"errorStack\":[]}'\n\n#curl example\ncurl -k -u admin:admin -H \"Content-Type: \\\napplication/json\" -X PUT -d \\\n'{\"name\":\"tcb-pool\",\"members\":[ \\\n{\"name\":\"192.168.25.32:80\",\"description\":\"first member\"}\n{\"name\":\"192.168.25.33:80\",\"description\":\"second member\"} ] }' \\\nhttps://172.16.44.128/mgmt/tm/ltm/pool\n#return data\n{\"code\":403,\"message\":\"Operation is not supported on component /ltm/pool.\",\"errorStack\":[]}\n \n\n You can see because they use the sub module URI used to create the pool this returns a 403 error. The command fails because one is trying to modify a specific pool at the generic pool level. Now that the pool exists, one must use the URI that specifies the pool. Thus, the correct command would be: \n\n \n#python example\n>>> b.put('https://172.16.44.128/mgmt/tm/ltm/pool/~Common~tcb-pool', data=json.dumps(payload))\n\n#curl example\ncurl -k -u admin:admin -H \"Content-Type: \\\napplication/json\" -X PUT -d \\\n'{\"members\":[ \\\n{\"name\":\"192.168.25.32:80\",\"description\":\"first member\"}\n{\"name\":\"192.168.25.33:80\",\"description\":\"second member\"} ] }' \\\nhttps://172.16.44.128/mgmt/tm/ltm/pool/~Common~tcb-pool\n \n\n We add the ~Common~ in front of the pool name because it is in the Common partition. However, this would also work with https://172.16.44.128/mgmt/tm/ltm/pool/tcb-pool. It is just good practice to explicitly insert the partition name since not all configuration objects will be in the default Common partition. Because we specify the pool in the URI, it is no longer necessary to have the “name” key value pair. \n\n In practice, programmers usually correctly modify items such as virtual servers and pools. However, we encounter this confusion much more often in configuration items that are ifiles. This may be because the creation of configuration items that are ifiles is a 3-step process. For instance, in order to create an external data group, one would first scp the file to 172.16.44.128/config/filestore/data_mda_1, then issue 2 Rest commands: \n\n \ncurl -sk -u admin:admin -H \"Content-Type: application/json\" -X POST -d '{\"name\":\"data_mda_1\",\"type\":\"string\",\"source-path\":\"file:///config/filestore/data_mda_1\"}' https://172.16.44.128/mgmt/tm/sys/file/data-group\ncurl -sk -u admin:admin -H \"Content-Type: application/json\" -X POST -d '{\"name\":\"dg_mda\",\"external-file-name\":\"/Common/data_mda_1\"}' https://172.16.44.128/mgmt/tm/ltm/data-group/external/ \n\n To update the external data group, many programmers first try something like the following: \n\n \ncurl -sk -u admin:admin -H \"Content-Type: application/json\" -X POST -d '{\"name\":\"data_mda_2\",\"type\":\"string\",\"source-path\":\"file:///config/filestore/data_mda_2”}’ https://172.16.44.128/mgmt/tm/sys/file/data-group\ncurl -sk -u admin:admin -H \"Content-Type: application/json\" -X PUT -d '{\"name\":\"dg_mda\",\"external-file-name\":\"/Common/data_mda_2\"}' https://172.16.44.128/mgmt/tm/ltm/data-group/external/ \n\n The first command works because we are creating a new ifile object. However, the second command fails because we are trying to modify a specific external data group at the generic external data group level. The proper command is: \n\n \ncurl -sk -u admin:admin -H \"Content-Type: application/json\" -X PUT -d '{\"external-file-name\":\"/Common/data_mda_2\"}' https://172.16.44.128/mgmt/tm/ltm/data-group/external/dg_mda \n\n The python code gets a little more complex with the data-group examples, so I've uploaded it to the codeshare here. Much thanks to Pat Chang for the bulk of the content in this article. Stay tuned for part 2, where we'll cover sub collections and how to use them. ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"7079","kudosSumWeight":1,"repliesCount":8,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[{"__typename":"AssociatedImageEdge","cursor":"MjUuM3wyLjF8b3wyNXxfTlZffDE","node":{"__ref":"AssociatedImage:{\"url\":\"https://community.f5.com/t5/s/zihoc95639/images/bS0yODY1MTgtMTE1MTJpMjkwOTkyOTM4MzQ4REM1MA?revision=1\"}"}}],"totalCount":1,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:286689":{"__typename":"Conversation","id":"conversation:286689","topic":{"__typename":"TkbTopicMessage","uid":286689},"lastPostingActivityTime":"2023-08-04T09:24:35.305-07:00","solved":false},"TkbTopicMessage:message:286689":{"__typename":"TkbTopicMessage","subject":"Demystifying iControl REST Part 5: Transferring Files","conversation":{"__ref":"Conversation:conversation:286689"},"id":"message:286689","revisionNum":5,"uid":286689,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:51154"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":9240},"postTime":"2015-11-17T03:00:00.000-08:00","lastPublishTime":"2023-08-04T09:24:35.305-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" iControl REST. It’s iControl SOAP’s baby, brother, introduced back in TMOS version 11.4 as an early access feature but released fully in version 11.5. \n Several articles on basic usage have been written on iControl REST so the intent here isn’t basic use, but rather to demystify some of the finer details of using the API. This article will cover the details on how to transfer files to/from the BIG-IP using iControl REST and the python programming language. (Note: this functionality requires 12.0+.) \n The REST File Transfer Worker \n The file transfer worker allows a client to transfer files through a series of GET operations for downloads and POST operations for uploads. The Content-Range header is used for both as a means to chunk the content. For downloads, the worker listens on the following interfaces. \n \n \n \n Description \n Method \n URI \n File Location \n \n \n Download a File \n GET \n /mgmt/cm/autodeploy/software-image-downloads/ \n /shared/images/ \n \n \n Upload an Image File \n POST \n /mgmt/cm/autodeploy/software-image-uploads/ \n /shared/images/ \n \n \n Upload a File \n POST \n /mgmt/shared/file-transfer/uploads/ \n /var/config/rest/downloads/ \n \n \n Download a QKView \n GET \n /mgmt/shared/file-transfer/qkview-downloads/ \n /var/tmp/ \n \n \n Download a UCS \n GET \n /mgmt/shared/file-transfer/ucs-downloads/ \n /var/local/ucs/ \n \n \n Upload ASM Policy \n POST \n /mgmt/tm/asm/file-transfer/uploads/ \n /var/ts/var/rest/ \n \n \n Download ASM Policy \n GET \n /mgmt/tm/asm/file-transfer/downloads/ \n /var/ts/var/rest/ \n \n \n \n Binary and text files are supported. The magic in the transfer is the Content-Range header, which has the following format: \n Content-Range: start-end/filesize \n Where start/end are the chunk's delimiters in the file and filesize is well, the file size. Any file larger than 1M needs to be chunked with this header as that limit is enforced by the worker. This is done to avoid potential denial of service attacks and out of memory errors. There are benefits of chunking as well: \n \n Accurate progress bars \n Resuming interrupted downloads \n Random access to file content possible \n \n Uploading a File \n The function is shown below. Note that whereas normally with the REST API the Content-Type is application/json, with file transfers that changes to application/octet-stream. The workflow for the function works like this (line number in parentheses) : \n \n Set the Chunk Size (3) \n Set the Content-Type header (4-6) \n Open the file (7) \n Get the filename (apart from the path) from the absolute path (8) \n If the extension is an .iso file (image) put it in /shared/images, otherwise it’ll go in /var/config/rest/downloads (9-12) \n Disable ssl warnings requests (required with my version: 2.8.1. YMMV) (14) \n Set the total file size for use with the Content-Range header (15) \n Set the start variable to 0 (17) \n Begin loop to iterate through the file and upload in chunks (19) \n Read data from the file and if there is no more data, break the loop (20-22) \n set the current bytes read, if less than the chunk size, then this is the last chunk, so set the end to the size from step 7. Otherwise, add current bytes length to the start value and set that as the end. (24-28) \n Set the Content-Range header value and then add that to the header (30-31) \n Make the POST request, uploading the content chunk (32-36) \n Increment the start value by the current bytes content length (38) \n \n def _upload(host, creds, fp):\n\n chunk_size = 512 * 1024\n headers = {\n 'Content-Type': 'application/octet-stream'\n }\n fileobj = open(fp, 'rb')\n filename = os.path.basename(fp)\n if os.path.splitext(filename)[-1] == '.iso':\n uri = 'https://%s/mgmt/cm/autodeploy/software-image-uploads/%s' % (host, filename)\n else:\n uri = 'https://%s/mgmt/shared/file-transfer/uploads/%s' % (host, filename)\n\n requests.packages.urllib3.disable_warnings()\n size = os.path.getsize(fp)\n\n start = 0\n\n while True:\n file_slice = fileobj.read(chunk_size)\n if not file_slice:\n break\n\n current_bytes = len(file_slice)\n if current_bytes < chunk_size:\n end = size\n else:\n end = start + current_bytes\n\n content_range = \"%s-%s/%s\" % (start, end - 1, size)\n headers['Content-Range'] = content_range\n requests.post(uri,\n auth=creds,\n data=file_slice,\n headers=headers,\n verify=False)\n\n start += current_bytes \n Downloading a File \n Downloading is very similar but there are some differences. Here is the workflow that is different, followed by the code. Note that the local path where the file will be downloaded to is given as part of the filename. \n \n URI is set to downloads worker. The only supported download directory at this time is /shared/images. (8) \n Open the local file so received data can be written to it (11) \n Make the request (22-26) \n If response code is 200 and if size is greater than 0, increment the current bytes and write the data to file, otherwise exit the loop (28-40) \n Set the value of the returned Content-Range header to crange and if initial size (0), set the file size to the size variable (42-46) \n If the file is smaller than the chunk size, adjust the chunk size down to the total file size and continue (51-55) \n Do the math to get ready to download the next chunk (57-62) \n \n def _download(host, creds, fp):\n chunk_size = 512 * 1024\n\n headers = {\n 'Content-Type': 'application/octet-stream'\n }\n filename = os.path.basename(fp)\n uri = 'https://%s/mgmt/cm/autodeploy/software-image-downloads/%s' % (host, filename)\n requests.packages.urllib3.disable_warnings()\n\n with open(fp, 'wb') as f:\n start = 0\n end = chunk_size - 1\n size = 0\n current_bytes = 0\n\n while True:\n content_range = \"%s-%s/%s\" % (start, end, size)\n headers['Content-Range'] = content_range\n\n #print headers\n resp = requests.get(uri,\n auth=creds,\n headers=headers,\n verify=False,\n stream=True)\n\n if resp.status_code == 200:\n # If the size is zero, then this is the first time through the\n # loop and we don't want to write data because we haven't yet\n # figured out the total size of the file.\n if size > 0:\n current_bytes += chunk_size\n for chunk in resp.iter_content(chunk_size):\n f.write(chunk)\n\n # Once we've downloaded the entire file, we can break out of\n # the loop\n if end == size:\n break\n\n crange = resp.headers['Content-Range']\n\n # Determine the total number of bytes to read\n if size == 0:\n size = int(crange.split('/')[-1]) - 1\n\n # If the file is smaller than the chunk size, BIG-IP will\n # return an HTTP 400. So adjust the chunk_size down to the\n # total file size...\n if chunk_size > size:\n end = size\n\n # ...and pass on the rest of the code\n continue\n\n start += chunk_size\n\n if (current_bytes + chunk_size) > size:\n end = size\n else:\n end = start + chunk_size - 1 \n Now you know how to upload and download files. Let’s do something with it! \n A Use Case - Upload Cert & Key to BIG-IP and Create a Clientssl Profile! \n This whole effort was sparked by a use case in Q&A, so I had to deliver the goods with more than just moving files around. The complete script is linked at the bottom, but there are a few steps required to get to a clientssl certificate: \n \n Upload the key & certificate \n Create the file object for key/cert \n Create the clientssl profile \n \n You know how to do step 1 now. Step 2 is to create the file object for the key and certificate. After a quick test to see which file is the certificate, you set both files, build the payload, then make the POST requests to bind the uploaded files to the file object. \n def create_cert_obj(bigip, b_url, files):\n\n f1 = os.path.basename(files[0])\n f2 = os.path.basename(files[1])\n if f1.endswith('.crt'):\n certfilename = f1\n keyfilename = f2\n else:\n keyfilename = f1\n certfilename = f2\n\n certname = f1.split('.')[0]\n\n payload = {}\n payload['command'] = 'install'\n payload['name'] = certname\n\n # Map Cert to File Object\n payload['from-local-file'] = '/var/config/rest/downloads/%s' % certfilename\n bigip.post('%s/sys/crypto/cert' % b_url, json.dumps(payload))\n\n # Map Key to File Object\n payload['from-local-file'] = '/var/config/rest/downloads/%s' % keyfilename\n bigip.post('%s/sys/crypto/key' % b_url, json.dumps(payload))\n\n return certfilename, keyfilename \n Notice we return the key/cert filenames so they can be used for step 3 to establish the clientssl profile. In this example, I name the file object and the clientssl profile to the name of the certfilename (minus the extension) but you can alter this to allow the objects names to be provided. To build the profile, just create the payload with the custom key/cert and make the POST request and you are done! \n def create_ssl_profile(bigip, b_url, certname, keyname):\n payload = {}\n payload['name'] = certname.split('.')[0]\n payload['cert'] = certname\n payload['key'] = keyname\n bigip.post('%s/ltm/profile/client-ssl' % b_url, json.dumps(payload)) \n Much thanks to Tim Rupp who helped me get across the finish line with some counting and rest worker errors we were troubleshooting on the download function. \n Get the Code \n \n Upload a File \n Download a File \n Upload Cert/Key & Build a Clientssl Profile \n \n ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"10046","kudosSumWeight":4,"repliesCount":45,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Conversation:conversation:273660":{"__typename":"Conversation","id":"conversation:273660","topic":{"__typename":"TkbTopicMessage","uid":273660},"lastPostingActivityTime":"2023-06-05T22:51:18.734-07:00","solved":false},"User:user:4542":{"__typename":"User","uid":4542,"login":"carldubois_1031","registrationData":{"__typename":"RegistrationData","status":null},"deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://community.f5.com/t5/s/zihoc95639/m_assets/avatars/default/avatar-12.svg?time=0"},"id":"user:4542"},"TkbTopicMessage:message:273660":{"__typename":"TkbTopicMessage","subject":"BIG-IQ Central Management API automation and programmability - Workflow - Python","conversation":{"__ref":"Conversation:conversation:273660"},"id":"message:273660","revisionNum":2,"uid":273660,"depth":0,"board":{"__ref":"Tkb:board:TechnicalArticles"},"author":{"__ref":"User:user:4542"},"teaser@stripHtml({\"removeProcessingText\":true,\"truncateLength\":-1})":"","introduction":"","metrics":{"__typename":"MessageMetrics","views":598},"postTime":"2016-06-28T14:27:45.000-07:00","lastPublishTime":"2023-06-05T22:51:18.734-07:00","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})":" Summary \n\n In conjunction with the announcement of BIG-IQ 5.0 we are excited to bring the field greater flexibility when centrally managing BIG-IP devices utilizing well defined workflows and the BIG-IQ iControl REST API. We understand that automation and programmability is becoming more the norm these days as the network is evolving into a software defined application aware environment. \n\n This article will provide a basic example for first establishing trust while adding a device to inventory, second importing device configuration into BIG-IQ's configuration database and finally adding device to a license pool all utilizing the BIG-IQ rest api uri provided by the interface. \n\n Response is parsed in JSON format so there are dependencies required. \n\n This is intended to be an introduction to leveraging the API directly. We are in the process of developing a python object abstraction for the BIG-IQ CM REST API to provide even greater simplicity when leveraging automation for central management programmability. But we thought this level of detail may be appropriate to get the ball rolling. \n\n The BIG-IQ CM SDK will be structured similar to version 0.1.7 BIG-IP iControl REST API described here: \n\n \n BIGIP SDK - https://devcentral.f5.com/s/articles/f5-friday-python-sdk-for-big-ip-18233 \n\n BIGIP SDK Docs - https://f5-sdk.readthedocs.io/en/v0.1.7/ \n\n \n Let's get started ... READY ... SET ... GO ... \n\n Here are the API URIs' used as reference for both Python and Perl examples below: \n\n \n Trust: https://'bigiq-ip'/mgmt/cm/global/tasks/device-trust \n\n Discovery: https://'bigiq-ip'/mgmt/cm/global/tasks/device-discovery \n\n Import: ADC - https://'bigiq-ip'/mgmt/cm/adc-core/tasks/declare-mgmt-authority \n\n Import: Firewall - https://'bigiq-ip'/mgmt/cm/firewall/tasks/declare-mgmt-authority \n\n License: Pools - https://'bigiq-ip'/mgmt/cm/shared/licensing/pools \n\n License: Members: - https://'bigiq-ip'/mgmt/cm/shared/licensing/pools/'member_uuid'/members \n\n \n Using a python class abstraction (libraries can be located in /lib/) for Discovery, Import and Licensing, these tasks are a single call from one place which will provide the flexibility to create scripts defined by workflow. \n\n For example, lets - \n\n 1. Negotiate trust certificates for a newly deployed BIGIP. \n\n 2. Discover the BIGIP device and add to inventory. \n\n 3. Import ADC configuration and resolve all differences to BIGIQ’s management database. \n\n 4. Import Firewall configuration and resolve all differences to BIGIQ’s management database. \n\n 5. Add this newly discovered BIGIP to a provisioned license pool using the base-registration-key as a filter. \n\n\n\n Using the following python libraries, we can accomplish these tasks defined as methods in each class definition: \n\n \n ## Discovery \n\n ../lib/discovery.py: class Discover(object) \n\n def device_trust(dev_id): \n\n def ltm_discover(modules adc_core, firewall, asm, apm etc..): \n\n\n\n ## Import \n\n ../lib/import.py: class Import(object) \n\n def import_ltm(dev_id): \n\n def import_security(dev_id, afm=True): \n\n\n\n ## License \n\n ../lib/license.py : class License(object) \n\n def license_pool(config): \n\n \n Now it’s just a matter of putting the puzzle together in a script that will accomplish the workflow described above. \n\n BIG-IQ and BIG-IP device address and credentials are read in from a configuration file (../config/..) the script first and passed into def workflow for processing. Each method is called with argument <config> dictionary to perform the task required. After completion each will return True or False depending on result from rest response code and some evaluation. \n\n \n ## Import libraries to leverage methods described above \n\n \n from discovery import Discover \n\n from import_module import Import \n\n from license import License \n\n ## main def \"workflow\" \n\n \n def workflow (Discover, License, Import, config): \n\n ## Trust \"establish certificate exchange\" \n\n result_trust = Discover.device_trust(config) \n\n ## Discover \"add a BIGIP to inventory\" \n\n result = Discover.ltm_discover(config, result_trust) \n\n ## Import ADC \"import adc configuration into database\" \n\n result = Import.import_ltm(config, result_trust) \n\n ## Import AFM, ASM, APM \"import module configuration into database\" \n\n result = Import.import_sec(config, result_trust, afm=True) \n\n ## Add BIGIP member to license pool \n\n result = License.license_pool(config) \n\n ## Python main body \n\n \n if __name__ == '__main__': \n\n # read config file from ../config/.. \n\n config = {} \n\n with open (file) as infile: \n\n config[str(key)] = val.strip('\\n') \n\n ## create an instance of each imported class defined in the libraries \n\n Discover = Discover(config) \n\n Import = Import(config) \n\n License = License(config) \n\n ## call the main program method \"workflow\" \n\n \n workflow (Discover, Import, License, config) \n\n \n If you are interested in this code for collaboration or solution, search on key words \"bigiq\" \"api\" \"python\" in code share section on dev central or you can refer to the reference link: \n\n Device trust, discovery and import using python requests - supported in Python version 2.7.9 and greater. \n\n https://devcentral.f5.com/s/articles/big-iq-trust-discovery-and-import-of-big-ip-using-rest-api-python-oo-973 \n\n Grants a license to a BIG-IP from an already-activated purchaced pool. \n\n https://devcentral.f5.com/s/articles/big-iq-licensing-of-big-ip-using-rest-api-python-oo-974 \n\n \n\n We will also be adding to github and will update this article once completed. So please come back and visit us soon and often for additional content. \n\n ","body@stripHtml({\"removeProcessingText\":true,\"removeSpoilerMarkup\":true,\"removeTocMarkup\":true,\"truncateLength\":-1})@stringLength":"5749","kudosSumWeight":0,"repliesCount":0,"readOnly":false,"images":{"__typename":"AssociatedImageConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"videos":{"__typename":"VideoConnection","edges":[],"totalCount":0,"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"CachedAsset:text:en_US-components/community/Navbar-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/community/Navbar-1744046271000","value":{"community":"Community Home","inbox":"Inbox","manageContent":"Manage Content","tos":"Terms of Service","forgotPassword":"Forgot Password","themeEditor":"Theme Editor","edit":"Edit Navigation Bar","skipContent":"Skip to content","migrated-link-9":"Groups","migrated-link-7":"Technical Articles","migrated-link-8":"DevCentral News","migrated-link-1":"Technical Forum","migrated-link-10":"Community Groups","migrated-link-2":"Water Cooler","migrated-link-11":"F5 Groups","Common-external-link":"How Do I...?","migrated-link-0":"Forums","article-series":"Article Series","migrated-link-5":"Community Articles","migrated-link-6":"Articles","security-insights":"Security Insights","migrated-link-3":"CrowdSRC","migrated-link-4":"CodeShare","migrated-link-12":"Events","migrated-link-13":"Suggestions"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarHamburgerDropdown-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarHamburgerDropdown-1744046271000","value":{"hamburgerLabel":"Side Menu"},"localOverride":false},"CachedAsset:text:en_US-components/community/BrandLogo-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/community/BrandLogo-1744046271000","value":{"logoAlt":"Khoros","themeLogoAlt":"Brand Logo"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarTextLinks-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarTextLinks-1744046271000","value":{"more":"More"},"localOverride":false},"CachedAsset:text:en_US-components/authentication/AuthenticationLink-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/authentication/AuthenticationLink-1744046271000","value":{"title.login":"Sign In","title.registration":"Register","title.forgotPassword":"Forgot Password","title.multiAuthLogin":"Sign In"},"localOverride":false},"CachedAsset:text:en_US-components/nodes/NodeLink-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/nodes/NodeLink-1744046271000","value":{"place":"Place {name}"},"localOverride":false},"CachedAsset:text:en_US-components/tags/TagSubscriptionAction-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/tags/TagSubscriptionAction-1744046271000","value":{"success.follow.title":"Following Tag","success.unfollow.title":"Unfollowed Tag","success.follow.message.followAcrossCommunity":"You will be notified when this tag is used anywhere across the community","success.unfollowtag.message":"You will no longer be notified when this tag is used anywhere in this place","success.unfollowtagAcrossCommunity.message":"You will no longer be notified when this tag is used anywhere across the community","unexpected.error.title":"Error - Action Failed","unexpected.error.message":"An unidentified problem occurred during the action you took. Please try again later.","buttonTitle":"{isSubscribed, select, true {Unfollow} false {Follow} other{}}","unfollow":"Unfollow"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/QueryHandler-1744046271000","value":{"title":"Query Handler"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarDropdownToggle-1744046271000","value":{"ariaLabelClosed":"Press the down arrow to open the menu"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageListTabs-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageListTabs-1744046271000","value":{"mostKudoed":"{value, select, IDEA {Most Votes} other {Most Likes}}","mostReplies":"Most Replies","mostViewed":"Most Viewed","newest":"{value, select, IDEA {Newest Ideas} OCCASION {Newest Events} other {Newest Topics}}","newestOccasions":"Newest Events","mostRecent":"Most Recent","noReplies":"No Replies Yet","noSolutions":"No Solutions Yet","solutions":"Solutions","mostRecentUserContent":"Most Recent","trending":"Trending","draft":"Drafts","spam":"Spam","abuse":"Abuse","moderation":"Moderation","tags":"Tags","PAST":"Past","UPCOMING":"Upcoming","sortBymostRecent":"Sort By Most Recent","sortBymostRecentUserContent":"Sort By Most Recent","sortBymostKudoed":"Sort By Most Likes","sortBymostReplies":"Sort By Most Replies","sortBymostViewed":"Sort By Most Viewed","sortBynewest":"Sort By Newest Topics","sortBynewestOccasions":"Sort By Newest Events","otherTabs":" Messages list in the {tab} for {conversationStyle}","guides":"Guides","archives":"Archives"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageView/MessageViewInline-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageView/MessageViewInline-1744046271000","value":{"bylineAuthor":"{bylineAuthor}","bylineBoard":"{bylineBoard}","anonymous":"Anonymous","place":"Place {bylineBoard}","gotoParent":"Go to parent {name}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Pager/PagerLoadMore-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Pager/PagerLoadMore-1744046271000","value":{"loadMore":"Show More"},"localOverride":false},"CachedAsset:text:en_US-components/customComponent/CustomComponent-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/customComponent/CustomComponent-1744046271000","value":{"errorMessage":"Error rendering component id: {customComponentId}","bannerTitle":"Video provider requires cookies to play the video. Accept to continue or {url} it directly on the provider's site.","buttonTitle":"Accept","urlText":"watch"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/OverflowNav-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/OverflowNav-1744046271000","value":{"toggleText":"More"},"localOverride":false},"CachedAsset:text:en_US-components/users/UserLink-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/users/UserLink-1744046271000","value":{"authorName":"View Profile: {author}","anonymous":"Anonymous"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageSubject-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageSubject-1744046271000","value":{"noSubject":"(no subject)"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBody-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBody-1744046271000","value":{"showMessageBody":"Show More","mentionsErrorTitle":"{mentionsType, select, board {Board} user {User} message {Message} other {}} No Longer Available","mentionsErrorMessage":"The {mentionsType} you are trying to view has been removed from the community.","videoProcessing":"Video is being processed. Please try again in a few minutes.","bannerTitle":"Video provider requires cookies to play the video. Accept to continue or {url} it directly on the provider's site.","buttonTitle":"Accept","urlText":"watch"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageTime-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageTime-1744046271000","value":{"postTime":"Published: {time}","lastPublishTime":"Last Update: {time}","conversation.lastPostingActivityTime":"Last posting activity time: {time}","conversation.lastPostTime":"Last post time: {time}","moderationData.rejectTime":"Rejected time: {time}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeIcon-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeIcon-1744046271000","value":{"contentType":"Content Type {style, select, FORUM {Forum} BLOG {Blog} TKB {Knowledge Base} IDEA {Ideas} OCCASION {Events} other {}} icon"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageUnreadCount-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageUnreadCount-1744046271000","value":{"unread":"{count} unread","comments":"{count, plural, one { unread comment} other{ unread comments}}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageViewCount-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageViewCount-1744046271000","value":{"textTitle":"{count, plural,one {View} other{Views}}","views":"{count, plural, one{View} other{Views}}"},"localOverride":false},"CachedAsset:text:en_US-components/kudos/KudosCount-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/kudos/KudosCount-1744046271000","value":{"textTitle":"{count, plural,one {{messageType, select, IDEA{Vote} other{Like}}} other{{messageType, select, IDEA{Votes} other{Likes}}}}","likes":"{count, plural, one{like} other{likes}}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageRepliesCount-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageRepliesCount-1744046271000","value":{"textTitle":"{count, plural,one {{conversationStyle, select, IDEA{Comment} OCCASION{Comment} other{Reply}}} other{{conversationStyle, select, IDEA{Comments} OCCASION{Comments} other{Replies}}}}","comments":"{count, plural, one{Comment} other{Comments}}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1744046271000":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserAvatar-1744046271000","value":{"altText":"{login}'s avatar","altTextGeneric":"User's avatar"},"localOverride":false}}}},"page":"/tags/TagPage/TagPage","query":{"nodeId":"board:TechnicalArticles","tagName":"python"},"buildId":"FP3zsFp6DJl70wDujOvtu","runtimeConfig":{"buildInformationVisible":false,"logLevelApp":"info","logLevelMetrics":"info","openTelemetryClientEnabled":false,"openTelemetryConfigName":"f5","openTelemetryServiceVersion":"25.3.0","openTelemetryUniverse":"prod","openTelemetryCollector":"http://localhost:4318","openTelemetryRouteChangeAllowedTime":"5000","apolloDevToolsEnabled":false,"inboxMuteWipFeatureEnabled":false},"isFallback":false,"isExperimentalCompile":false,"dynamicIds":["./components/customComponent/CustomComponent/CustomComponent.tsx","./components/community/Navbar/NavbarWidget.tsx","./components/community/Breadcrumb/BreadcrumbWidget.tsx","./components/tags/TagsHeaderWidget/TagsHeaderWidget.tsx","./components/messages/MessageListForNodeByRecentActivityWidget/MessageListForNodeByRecentActivityWidget.tsx","./components/tags/TagSubscriptionAction/TagSubscriptionAction.tsx","./components/customComponent/CustomComponentContent/TemplateContent.tsx","../shared/client/components/common/List/ListGroup/ListGroup.tsx","./components/messages/MessageView/MessageView.tsx","./components/messages/MessageView/MessageViewInline/MessageViewInline.tsx","../shared/client/components/common/Pager/PagerLoadMore/PagerLoadMore.tsx","./components/customComponent/CustomComponentContent/HtmlContent.tsx","./components/customComponent/CustomComponentContent/CustomComponentScripts.tsx"],"appGip":true,"scriptLoader":[]}