Skip to content

Commit

Permalink
Merge pull request #13391 from rajatchopra/f5partition
Browse files Browse the repository at this point in the history
Merged by openshift-bot
  • Loading branch information
OpenShift Bot committed Apr 5, 2017
2 parents b39b0ed + 6ee3a7e commit 83a6089
Show file tree
Hide file tree
Showing 4 changed files with 405 additions and 248 deletions.
133 changes: 95 additions & 38 deletions pkg/router/f5/f5.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -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",
Expand 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 {
Expand All @@ -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"},
Expand All @@ -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)
}
Expand All @@ -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",
Expand All @@ -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{}

Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Partition: f5.partitionPath,
Code: iRule,
}

err = f5.post(iRulesUrl, iRulePayload, nil)
Expand All @@ -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{}

Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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",
Partition: f5.partitionPath,
Name: poolname,
}

err := f5.post(url, payload, nil)
Expand All @@ -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 {
Expand All @@ -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{}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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))

res := f5PolicyRuleset{}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions pkg/router/f5/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ func (p *F5Plugin) HandleEndpoints(eventType watch.EventType,

glog.V(4).Infof("Deleting pool %s", poolname)

// Note: deletePool will throw errors if the route
// has not been deleted as the policy would
// still refer to the pool. That is ok as the
// pool will still get deleted when the route
// gets deleted.
err = p.deletePool(poolname)
if err != nil {
return err
Expand Down
Loading

0 comments on commit 83a6089

Please sign in to comment.