-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support F5 partition path #13391
Support F5 partition path #13391
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -481,6 +481,50 @@ func (f5 *f5LTM) delete(url string, result interface{}) error { | |
return f5.restRequest("DELETE", url, nil, result) | ||
} | ||
|
||
// | ||
// iControl REST resource helper methods. | ||
// | ||
|
||
// encodeiControlUriPathComponent returns an encoded resource path for use | ||
// in the URI for the iControl REST calls. | ||
// For example for a path /Common/foo, the corresponding encoded iControl | ||
// URI path component would be ~Common~foo and this can then be used in the | ||
// iControl REST calls ala: | ||
// https://<ip>:<port>/mgmt/tm/ltm/policy/~Common~foo/rules | ||
func encodeiControlUriPathComponent(pathName string) string { | ||
return strings.Replace(pathName, "/", "~", -1) | ||
} | ||
|
||
// iControlUriResourceId returns an encoded resource id (resource path | ||
// including the partition), which can be used the iControl REST calls. | ||
// For example, for a policy named openshift_secure_routes policy in the | ||
// /Common partition, the encoded resource id would be: | ||
// ~Common~openshift_secure_routes | ||
// which can then be used as a resource specifier in the URI ala: | ||
// https://<ip>:<port>/mgmt/tm/ltm/policy/~Common~openshift_secure_routes/rules | ||
func (f5 *f5LTM) iControlUriResourceId(resourceName string) string { | ||
resourcePath := path.Join(f5.partitionPath, resourceName) | ||
return encodeiControlUriPathComponent(resourcePath) | ||
} | ||
|
||
// iControlUriVserverId returns an encoded Virtual Server id (virtual | ||
// server name including the partition), which can be used in the iControl | ||
// REST calls. | ||
// For example, for a virtual server named ose-vserver in | ||
// the /rht/ose3/config partition path, the encoded Virtual Server id is: | ||
// ~rht~ose-vserver | ||
func (f5 *f5LTM) iControlUriVserverId(vserverName string) string { | ||
// Note: Most resources are stored under the configured partition | ||
// path, which may be a top-level folder or a sub-folder. | ||
// However a virtual server can only be stored under a | ||
// top-level folder. | ||
// Example: For a vserver named ose-server in the /rht/ose3/config | ||
// partition path, the encoded URI path component is: | ||
// ~rht~ose-vserver | ||
pathComponents := strings.Split(f5.partitionPath, "/") | ||
return encodeiControlUriPathComponent(path.Join("/", pathComponents[1], vserverName)) | ||
} | ||
|
||
// | ||
// Routines for controlling F5. | ||
// | ||
|
@@ -532,7 +576,7 @@ func (f5 *f5LTM) ensureVxLANTunnel() error { | |
AddressSource: "from-user", | ||
Floating: "disabled", | ||
InheritedTrafficGroup: "false", | ||
TrafficGroup: path.Join(f5.partitionPath, "traffic-group-local-only"), | ||
TrafficGroup: path.Join("/Common", "traffic-group-local-only"), // Traffic group is global | ||
Unit: 0, | ||
Vlan: path.Join(f5.partitionPath, F5VxLANTunnelName), | ||
AllowService: "all", | ||
|
@@ -552,8 +596,9 @@ func (f5 *f5LTM) ensureVxLANTunnel() error { | |
func (f5 *f5LTM) ensurePolicyExists(policyName string) error { | ||
glog.V(4).Infof("Checking whether policy %s exists...", policyName) | ||
|
||
policyResourceId := f5.iControlUriResourceId(policyName) | ||
policyUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s", | ||
f5.host, policyName) | ||
f5.host, policyResourceId) | ||
|
||
err := f5.get(policyUrl, nil) | ||
if err != nil && err.(F5Error).httpStatusCode != 404 { | ||
|
@@ -570,11 +615,13 @@ func (f5 *f5LTM) ensurePolicyExists(policyName string) error { | |
|
||
policiesUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy", f5.host) | ||
|
||
policyPath := path.Join(f5.partitionPath, policyName) | ||
|
||
if f5.setupOSDNVxLAN { | ||
// if vxlan needs to be setup, it will only happen | ||
// with ver12, for which we need to use a different payload | ||
policyPayload := f5Ver12Policy{ | ||
Name: policyName, | ||
Name: policyPath, | ||
TmPartition: f5.partitionPath, | ||
Controls: []string{"forwarding"}, | ||
Requires: []string{"http"}, | ||
|
@@ -584,10 +631,11 @@ func (f5 *f5LTM) ensurePolicyExists(policyName string) error { | |
err = f5.post(policiesUrl, policyPayload, nil) | ||
} else { | ||
policyPayload := f5Policy{ | ||
Name: policyName, | ||
Controls: []string{"forwarding"}, | ||
Requires: []string{"http"}, | ||
Strategy: "best-match", | ||
Name: policyPath, | ||
Partition: f5.partitionPath, | ||
Controls: []string{"forwarding"}, | ||
Requires: []string{"http"}, | ||
Strategy: "best-match", | ||
} | ||
err = f5.post(policiesUrl, policyPayload, nil) | ||
} | ||
|
@@ -602,7 +650,7 @@ func (f5 *f5LTM) ensurePolicyExists(policyName string) error { | |
glog.V(4).Infof("Policy %s created. Adding no-op rule...", policyName) | ||
|
||
rulesUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s/rules", | ||
f5.host, policyName) | ||
f5.host, policyResourceId) | ||
|
||
rulesPayload := f5Rule{ | ||
Name: "default_noop", | ||
|
@@ -624,11 +672,13 @@ func (f5 *f5LTM) ensureVserverHasPolicy(vserverName, policyName string) error { | |
glog.V(4).Infof("Checking whether vserver %s has policy %s...", | ||
vserverName, policyName) | ||
|
||
vserverResourceId := f5.iControlUriVserverId(vserverName) | ||
|
||
// We could use fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s/policies/%s", | ||
// f5.host, vserverName, policyName) here, except that F5 iControl REST | ||
// returns a 200 even if the policy does not exist. | ||
// f5.host, vserverResourceId, policyName) here, except that F5 | ||
// iControl REST returns a 200 even if the policy does not exist. | ||
vserverPoliciesUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s/policies", | ||
f5.host, vserverName) | ||
f5.host, vserverResourceId) | ||
|
||
res := f5VserverPolicies{} | ||
|
||
|
@@ -637,8 +687,10 @@ func (f5 *f5LTM) ensureVserverHasPolicy(vserverName, policyName string) error { | |
return err | ||
} | ||
|
||
policyPath := path.Join(f5.partitionPath, policyName) | ||
|
||
for _, policy := range res.Policies { | ||
if policy.Name == policyName { | ||
if policy.FullPath == policyPath { | ||
glog.V(4).Infof("Vserver %s has policy %s associated with it;"+ | ||
" nothing to do.", vserverName, policyName) | ||
return nil | ||
|
@@ -648,7 +700,8 @@ func (f5 *f5LTM) ensureVserverHasPolicy(vserverName, policyName string) error { | |
glog.V(4).Infof("Adding policy %s to vserver %s...", policyName, vserverName) | ||
|
||
vserverPoliciesPayload := f5VserverPolicy{ | ||
Name: policyName, | ||
Name: policyPath, | ||
Partition: f5.partitionPath, | ||
} | ||
|
||
err = f5.post(vserverPoliciesUrl, vserverPoliciesPayload, nil) | ||
|
@@ -709,7 +762,8 @@ func (f5 *f5LTM) ensureDatagroupExists(datagroupName string) error { | |
func (f5 *f5LTM) ensureIRuleExists(iRuleName, iRule string) error { | ||
glog.V(4).Infof("Checking whether iRule %s exists...", iRuleName) | ||
|
||
iRuleUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/rule/%s", f5.host, iRuleName) | ||
iRuleUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/rule/%s", f5.host, | ||
f5.iControlUriResourceId(iRuleName)) | ||
|
||
err := f5.get(iRuleUrl, nil) | ||
if err != nil && err.(F5Error).httpStatusCode != 404 { | ||
|
@@ -727,8 +781,9 @@ func (f5 *f5LTM) ensureIRuleExists(iRuleName, iRule string) error { | |
iRulesUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/rule", f5.host) | ||
|
||
iRulePayload := f5IRule{ | ||
Name: iRuleName, | ||
Code: iRule, | ||
Name: iRuleName, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. iRuleName or f5.iControlUriResourceId(iRuleName)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will confirm with F5 team, but this seems to work so far because the Partition field is also part of the payload |
||
Partition: f5.partitionPath, | ||
Code: iRule, | ||
} | ||
|
||
err = f5.post(iRulesUrl, iRulePayload, nil) | ||
|
@@ -748,7 +803,7 @@ func (f5 *f5LTM) ensureVserverHasIRule(vserverName, iRuleName string) error { | |
vserverName, iRuleName) | ||
|
||
vserverUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s", | ||
f5.host, vserverName) | ||
f5.host, f5.iControlUriVserverId(vserverName)) | ||
|
||
res := f5VserverIRules{} | ||
|
||
|
@@ -757,7 +812,7 @@ func (f5 *f5LTM) ensureVserverHasIRule(vserverName, iRuleName string) error { | |
return err | ||
} | ||
|
||
commonIRuleName := fmt.Sprintf("%s/%s", f5.partitionPath, iRuleName) | ||
commonIRuleName := path.Join("/", f5.partitionPath, iRuleName) | ||
|
||
for _, name := range res.Rules { | ||
if name == commonIRuleName { | ||
|
@@ -770,8 +825,9 @@ func (f5 *f5LTM) ensureVserverHasIRule(vserverName, iRuleName string) error { | |
|
||
glog.V(4).Infof("Adding iRule %s to vserver %s...", iRuleName, vserverName) | ||
|
||
sslPassthroughIRulePath := path.Join(f5.partitionPath, sslPassthroughIRuleName) | ||
vserverRulesPayload := f5VserverIRules{ | ||
Rules: []string{sslPassthroughIRuleName}, | ||
Rules: []string{sslPassthroughIRulePath}, | ||
} | ||
|
||
err = f5.patch(vserverUrl, vserverRulesPayload, nil) | ||
|
@@ -788,10 +844,8 @@ func (f5 *f5LTM) ensureVserverHasIRule(vserverName, iRuleName string) error { | |
func (f5 *f5LTM) checkPartitionPathExists(pathName string) (bool, error) { | ||
glog.V(4).Infof("Checking if partition path %q exists...", pathName) | ||
|
||
// F5 iControl REST API expects / characters in the path to be | ||
// escaped as ~. | ||
uri := fmt.Sprintf("https://%s/mgmt/tm/sys/folder/%s", | ||
f5.host, strings.Replace(pathName, "/", "~", -1)) | ||
f5.host, encodeiControlUriPathComponent(pathName)) | ||
|
||
err := f5.get(uri, nil) | ||
if err != nil { | ||
|
@@ -1012,9 +1066,10 @@ func (f5 *f5LTM) CreatePool(poolname string) error { | |
// From @Miciah: In the future, we should allow the administrator | ||
// to specify a different monitor to use. | ||
payload := f5Pool{ | ||
Mode: "round-robin", | ||
Monitor: "min 1 of /Common/http /Common/https", | ||
Name: poolname, | ||
Mode: "round-robin", | ||
Monitor: "min 1 of /Common/http /Common/https", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fmt.Sprintf("min 1 of /%s/http /%s/https", f5.partionPath, f5.partionPath)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cannot be sure of http/https monitors being present in the given partition. It is safe to re-use the common monitor just as we re-use TrafficGroup. Good point though, in ideal case we would want this to exist and probably we can write code to ensure the monitors' presence. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree with @rajatchopra, though long term it should be (as @Miciah hinted ) a configurable option, given that the default LTM traffic monitors don't work properly for the atomic registry console and the workaround right now is to manually change the traffic monitor used for the pool for the registry console (given that /Common/http and /Common/https are read-only objects in LTM) |
||
Partition: f5.partitionPath, | ||
Name: poolname, | ||
} | ||
|
||
err := f5.post(url, payload, nil) | ||
|
@@ -1036,7 +1091,8 @@ func (f5 *f5LTM) CreatePool(poolname string) error { | |
// DeletePool deletes the specified pool from F5 BIG-IP, and deletes | ||
// f5.poolMembers[poolname]. | ||
func (f5 *f5LTM) DeletePool(poolname string) error { | ||
url := fmt.Sprintf("https://%s/mgmt/tm/ltm/pool/%s", f5.host, poolname) | ||
url := fmt.Sprintf("https://%s/mgmt/tm/ltm/pool/%s", f5.host, | ||
f5.iControlUriResourceId(poolname)) | ||
|
||
err := f5.delete(url, nil) | ||
if err != nil { | ||
|
@@ -1062,7 +1118,7 @@ func (f5 *f5LTM) GetPoolMembers(poolname string) (map[string]bool, error) { | |
} | ||
|
||
url := fmt.Sprintf("https://%s/mgmt/tm/ltm/pool/%s/members", | ||
f5.host, poolname) | ||
f5.host, f5.iControlUriResourceId(poolname)) | ||
|
||
res := f5PoolMemberset{} | ||
|
||
|
@@ -1125,7 +1181,7 @@ func (f5 *f5LTM) AddPoolMember(poolname, member string) error { | |
glog.V(4).Infof("Adding pool member %s to pool %s.", member, poolname) | ||
|
||
url := fmt.Sprintf("https://%s/mgmt/tm/ltm/pool/%s/members", | ||
f5.host, poolname) | ||
f5.host, f5.iControlUriResourceId(poolname)) | ||
|
||
payload := f5PoolMember{ | ||
Name: member, | ||
|
@@ -1164,7 +1220,7 @@ func (f5 *f5LTM) DeletePoolMember(poolname, member string) error { | |
} | ||
|
||
url := fmt.Sprintf("https://%s/mgmt/tm/ltm/pool/%s/members/%s", | ||
f5.host, poolname, member) | ||
f5.host, f5.iControlUriResourceId(poolname), member) | ||
|
||
err = f5.delete(url, nil) | ||
if err != nil { | ||
|
@@ -1187,7 +1243,7 @@ func (f5 *f5LTM) getRoutes(policyname string) (map[string]bool, error) { | |
} | ||
|
||
url := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s/rules", | ||
f5.host, policyname) | ||
f5.host, f5.iControlUriResourceId(policyname)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/openshift/origin/pull/13391/files#diff-997442d3bc5e2bf387c3840016b5c335R1261 If there are two partitions with same policy name, then f5.routes[policyname] will overwrite the former. Do we expect this case? If yes, then f5.routes[f5.iControlUriResourceId(policyname)] = routes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each instance of the controller pod will run with only one given partition. All internal data structures need not bother about multiple partitions. |
||
|
||
res := f5PolicyRuleset{} | ||
|
||
|
@@ -1257,8 +1313,9 @@ func (f5 *f5LTM) addRoute(policyname, routename, poolname, hostname, | |
pathname string) error { | ||
success := false | ||
|
||
policyResourceId := f5.iControlUriResourceId(policyname) | ||
rulesUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s/rules", | ||
f5.host, policyname) | ||
f5.host, policyResourceId) | ||
|
||
rulesPayload := f5Rule{ | ||
Name: routename, | ||
|
@@ -1288,7 +1345,7 @@ func (f5 *f5LTM) addRoute(policyname, routename, poolname, hostname, | |
}() | ||
|
||
conditionUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s/rules/%s/conditions", | ||
f5.host, policyname, routename) | ||
f5.host, policyResourceId, routename) | ||
|
||
conditionPayload := f5RuleCondition{ | ||
Name: "0", | ||
|
@@ -1330,7 +1387,7 @@ func (f5 *f5LTM) addRoute(policyname, routename, poolname, hostname, | |
} | ||
|
||
actionUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s/rules/%s/actions", | ||
f5.host, policyname, routename) | ||
f5.host, policyResourceId, routename) | ||
|
||
actionPayload := f5RuleAction{ | ||
Name: "0", | ||
|
@@ -1523,7 +1580,7 @@ func (f5 *f5LTM) DeletePassthroughRoute(routename string) error { | |
// policy. | ||
func (f5 *f5LTM) deleteRoute(policyname, routename string) error { | ||
ruleUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/policy/%s/rules/%s", | ||
f5.host, policyname, routename) | ||
f5.host, f5.iControlUriResourceId(policyname), routename) | ||
|
||
err := f5.delete(ruleUrl, nil) | ||
if err != nil { | ||
|
@@ -1844,7 +1901,7 @@ func (f5 *f5LTM) associateClientSslProfileWithVserver(profilename, | |
profilename, vservername) | ||
|
||
vserverProfileUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s/profiles", | ||
f5.host, vservername) | ||
f5.host, f5.iControlUriVserverId(vservername)) | ||
|
||
vserverProfilePayload := f5VserverProfilePayload{ | ||
Name: profilename, | ||
|
@@ -1862,7 +1919,7 @@ func (f5 *f5LTM) associateServerSslProfileWithVserver(profilename, | |
profilename, vservername) | ||
|
||
vserverProfileUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s/profiles", | ||
f5.host, vservername) | ||
f5.host, f5.iControlUriVserverId(vservername)) | ||
|
||
vserverProfilePayload := f5VserverProfilePayload{ | ||
Name: profilename, | ||
|
@@ -1890,7 +1947,7 @@ func (f5 *f5LTM) deleteCertParts(routename string, | |
routename, f5.httpsVserver) | ||
serverSslProfileName := fmt.Sprintf("%s-server-ssl-profile", routename) | ||
serverSslVserverProfileUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s/profiles/%s", | ||
f5.host, f5.httpsVserver, serverSslProfileName) | ||
f5.host, f5.iControlUriVserverId(f5.httpsVserver), serverSslProfileName) | ||
err := f5.delete(serverSslVserverProfileUrl, nil) | ||
if err != nil { | ||
// Iff the profile is not associated with the vserver, we can continue on to | ||
|
@@ -1925,7 +1982,7 @@ func (f5 *f5LTM) deleteCertParts(routename string, | |
" from vserver %s...", routename, f5.httpsVserver) | ||
clientSslProfileName := fmt.Sprintf("%s-client-ssl-profile", routename) | ||
clientSslVserverProfileUrl := fmt.Sprintf("https://%s/mgmt/tm/ltm/virtual/%s/profiles/%s", | ||
f5.host, f5.httpsVserver, clientSslProfileName) | ||
f5.host, f5.iControlUriVserverId(f5.httpsVserver), clientSslProfileName) | ||
err := f5.delete(clientSslVserverProfileUrl, nil) | ||
if err != nil { | ||
// Iff the profile is not associated with the vserver, we can continue on | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vlan is local per partition or is it global similar to traffic-group?