func waitForPoolDelete(networkingClient *gophercloud.ServiceClient, poolID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Pool %s", poolID) pool, err := pools.Get(networkingClient, poolID).Extract() if err != nil { errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return pool, "ACTIVE", err } if errCode.Actual == 404 { log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID) return pool, "DELETED", nil } } log.Printf("[DEBUG] Openstack LBaaSV2 Pool: %+v", pool) err = pools.Delete(networkingClient, poolID).ExtractErr() if err != nil { errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return pool, "ACTIVE", err } if errCode.Actual == 404 { log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID) return pool, "DELETED", nil } } log.Printf("[DEBUG] OpenStack LBaaSV2 Pool %s still active.", poolID) return pool, "ACTIVE", nil } }
func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Service, nodeNames []string) (*v1.LoadBalancerStatus, error) { glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v, %v)", clusterName, apiService.Namespace, apiService.Name, apiService.Spec.LoadBalancerIP, apiService.Spec.Ports, nodeNames, apiService.Annotations) ports := apiService.Spec.Ports if len(ports) == 0 { return nil, fmt.Errorf("no ports provided to openstack load balancer") } // Check for TCP protocol on each port // TODO: Convert all error messages to use an event recorder for _, port := range ports { if port.Protocol != v1.ProtocolTCP { return nil, fmt.Errorf("Only TCP LoadBalancer is supported for openstack load balancers") } } sourceRanges, err := service.GetLoadBalancerSourceRanges(apiService) if err != nil { return nil, err } if !service.IsAllowAll(sourceRanges) && !lbaas.opts.ManageSecurityGroups { return nil, fmt.Errorf("Source range restrictions are not supported for openstack load balancers without managing security groups") } affinity := v1.ServiceAffinityNone var persistence *v2pools.SessionPersistence switch affinity { case v1.ServiceAffinityNone: persistence = nil case v1.ServiceAffinityClientIP: persistence = &v2pools.SessionPersistence{Type: "SOURCE_IP"} default: return nil, fmt.Errorf("unsupported load balancer affinity: %v", affinity) } name := cloudprovider.GetLoadBalancerName(apiService) loadbalancer, err := getLoadbalancerByName(lbaas.network, name) if err != nil { if err != ErrNotFound { return nil, fmt.Errorf("Error getting loadbalancer %s: %v", name, err) } glog.V(2).Infof("Creating loadbalancer %s", name) loadbalancer, err = lbaas.createLoadBalancer(apiService, name) if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating loadbalancer %s: %v", name, err) } } else { glog.V(2).Infof("LoadBalancer %s already exists", name) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) lbmethod := v2pools.LBMethod(lbaas.opts.LBMethod) if lbmethod == "" { lbmethod = v2pools.LBMethodRoundRobin } oldListeners, err := getListenersByLoadBalancerID(lbaas.network, loadbalancer.ID) if err != nil { return nil, fmt.Errorf("Error getting LB %s listeners: %v", name, err) } for portIndex, port := range ports { listener := getListenerForPort(oldListeners, port) if listener == nil { glog.V(4).Infof("Creating listener for port %d", int(port.Port)) listener, err = listeners.Create(lbaas.network, listeners.CreateOpts{ Name: fmt.Sprintf("listener_%s_%d", name, portIndex), Protocol: listeners.Protocol(port.Protocol), ProtocolPort: int(port.Port), LoadbalancerID: loadbalancer.ID, }).Extract() if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating LB listener: %v", err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } glog.V(4).Infof("Listener for %s port %d: %s", string(port.Protocol), int(port.Port), listener.ID) // After all ports have been processed, remaining listeners are removed as obsolete. // Pop valid listeners. oldListeners = popListener(oldListeners, listener.ID) pool, err := getPoolByListenerID(lbaas.network, loadbalancer.ID, listener.ID) if err != nil && err != ErrNotFound { // Unknown error, retry later return nil, fmt.Errorf("Error getting pool for listener %s: %v", listener.ID, err) } if pool == nil { glog.V(4).Infof("Creating pool for listener %s", listener.ID) pool, err = v2pools.Create(lbaas.network, v2pools.CreateOpts{ Name: fmt.Sprintf("pool_%s_%d", name, portIndex), Protocol: v2pools.Protocol(port.Protocol), LBMethod: lbmethod, ListenerID: listener.ID, Persistence: persistence, }).Extract() if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating pool for listener %s: %v", listener.ID, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } glog.V(4).Infof("Pool for listener %s: %s", listener.ID, pool.ID) members, err := getMembersByPoolID(lbaas.network, pool.ID) if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error getting pool members %s: %v", pool.ID, err) } for _, nodeName := range nodeNames { addr, err := getAddressByName(lbaas.compute, types.NodeName(nodeName)) if err != nil { if err == ErrNotFound { // Node failure, do not create member glog.Warningf("Failed to create LB pool member for node %s: %v", nodeName, err) continue } else { return nil, fmt.Errorf("Error getting address for node %s: %v", nodeName, err) } } if !memberExists(members, addr, int(port.NodePort)) { glog.V(4).Infof("Creating member for pool %s", pool.ID) _, err := v2pools.CreateAssociateMember(lbaas.network, pool.ID, v2pools.MemberCreateOpts{ ProtocolPort: int(port.NodePort), Address: addr, SubnetID: lbaas.opts.SubnetId, }).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB pool member for node: %s, %v", nodeName, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } else { // After all members have been processed, remaining members are deleted as obsolete. members = popMember(members, addr, int(port.NodePort)) } glog.V(4).Infof("Ensured pool %s has member for %s at %s", pool.ID, nodeName, addr) } // Delete obsolete members for this pool for _, member := range members { glog.V(4).Infof("Deleting obsolete member %s for pool %s address %s", member.ID, pool.ID, member.Address) err := v2pools.DeleteMember(lbaas.network, pool.ID, member.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete member %s for pool %s address %s: %v", member.ID, pool.ID, member.Address, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } monitorID := pool.MonitorID if monitorID == "" && lbaas.opts.CreateMonitor { glog.V(4).Infof("Creating monitor for pool %s", pool.ID) monitor, err := v2monitors.Create(lbaas.network, v2monitors.CreateOpts{ PoolID: pool.ID, Type: string(port.Protocol), Delay: int(lbaas.opts.MonitorDelay.Duration.Seconds()), Timeout: int(lbaas.opts.MonitorTimeout.Duration.Seconds()), MaxRetries: int(lbaas.opts.MonitorMaxRetries), }).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB pool healthmonitor: %v", err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) monitorID = monitor.ID } glog.V(4).Infof("Monitor for pool %s: %s", pool.ID, monitorID) } // All remaining listeners are obsolete, delete for _, listener := range oldListeners { glog.V(4).Infof("Deleting obsolete listener %s:", listener.ID) // get pool for listener pool, err := getPoolByListenerID(lbaas.network, loadbalancer.ID, listener.ID) if err != nil && err != ErrNotFound { return nil, fmt.Errorf("Error getting pool for obsolete listener %s: %v", listener.ID, err) } if pool != nil { // get and delete monitor monitorID := pool.MonitorID if monitorID != "" { glog.V(4).Infof("Deleting obsolete monitor %s for pool %s", monitorID, pool.ID) err = v2monitors.Delete(lbaas.network, monitorID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete monitor %s for pool %s: %v", monitorID, pool.ID, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // get and delete pool members members, err := getMembersByPoolID(lbaas.network, pool.ID) if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error getting members for pool %s: %v", pool.ID, err) } if members != nil { for _, member := range members { glog.V(4).Infof("Deleting obsolete member %s for pool %s address %s", member.ID, pool.ID, member.Address) err := v2pools.DeleteMember(lbaas.network, pool.ID, member.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete member %s for pool %s address %s: %v", member.ID, pool.ID, member.Address, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } } glog.V(4).Infof("Deleting obsolete pool %s for listener %s", pool.ID, listener.ID) // delete pool err = v2pools.Delete(lbaas.network, pool.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete pool %s for listener %s: %v", pool.ID, listener.ID, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete listener err = listeners.Delete(lbaas.network, listener.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleteting obsolete listener: %v", err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) glog.V(2).Infof("Deleted obsolete listener: %s", listener.ID) } status := &v1.LoadBalancerStatus{} status.Ingress = []v1.LoadBalancerIngress{{IP: loadbalancer.VipAddress}} port, err := getPortByIP(lbaas.network, loadbalancer.VipAddress) if err != nil { return nil, fmt.Errorf("Error getting port for LB vip %s: %v", loadbalancer.VipAddress, err) } floatIP, err := getFloatingIPByPortID(lbaas.network, port.ID) if err != nil && err != ErrNotFound { return nil, fmt.Errorf("Error getting floating ip for port %s: %v", port.ID, err) } if floatIP == nil && lbaas.opts.FloatingNetworkId != "" { glog.V(4).Infof("Creating floating ip for loadbalancer %s port %s", loadbalancer.ID, port.ID) floatIPOpts := floatingips.CreateOpts{ FloatingNetworkID: lbaas.opts.FloatingNetworkId, PortID: port.ID, } floatIP, err = floatingips.Create(lbaas.network, floatIPOpts).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB floatingip %+v: %v", floatIPOpts, err) } } if floatIP != nil { status.Ingress = append(status.Ingress, v1.LoadBalancerIngress{IP: floatIP.FloatingIP}) } if lbaas.opts.ManageSecurityGroups { lbSecGroupCreateOpts := groups.CreateOpts{ Name: getSecurityGroupName(clusterName, apiService), Description: fmt.Sprintf("Securty Group for %v Service LoadBalancer", apiService.Name), } lbSecGroup, err := groups.Create(lbaas.network, lbSecGroupCreateOpts).Extract() if err != nil { // cleanup what was created so far _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } for _, port := range ports { for _, sourceRange := range sourceRanges.StringSlice() { ethertype := "IPv4" network, _, err := net.ParseCIDR(sourceRange) if err != nil { // cleanup what was created so far glog.Errorf("Error parsing source range %s as a CIDR", sourceRange) _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } if network.To4() == nil { ethertype = "IPv6" } lbSecGroupRuleCreateOpts := rules.CreateOpts{ Direction: "ingress", PortRangeMax: int(port.Port), PortRangeMin: int(port.Port), Protocol: strings.ToLower(string(port.Protocol)), RemoteIPPrefix: sourceRange, SecGroupID: lbSecGroup.ID, EtherType: ethertype, } _, err = rules.Create(lbaas.network, lbSecGroupRuleCreateOpts).Extract() if err != nil { // cleanup what was created so far _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } } err := createNodeSecurityGroup(lbaas.network, lbaas.opts.NodeSecurityGroupID, int(port.NodePort), string(port.Protocol), lbSecGroup.ID) if err != nil { glog.Errorf("Error occured creating security group for loadbalancer %s:", loadbalancer.ID) _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } } lbSecGroupRuleCreateOpts := rules.CreateOpts{ Direction: "ingress", PortRangeMax: 4, // ICMP: Code - Values for ICMP "Destination Unreachable: Fragmentation Needed and Don't Fragment was Set" PortRangeMin: 3, // ICMP: Type Protocol: "icmp", RemoteIPPrefix: "0.0.0.0/0", // The Fragmentation packet can come from anywhere along the path back to the sourceRange - we need to all this from all SecGroupID: lbSecGroup.ID, EtherType: "IPv4", } _, err = rules.Create(lbaas.network, lbSecGroupRuleCreateOpts).Extract() if err != nil { // cleanup what was created so far _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } lbSecGroupRuleCreateOpts = rules.CreateOpts{ Direction: "ingress", PortRangeMax: 0, // ICMP: Code - Values for ICMP "Packet Too Big" PortRangeMin: 2, // ICMP: Type Protocol: "icmp", RemoteIPPrefix: "::/0", // The Fragmentation packet can come from anywhere along the path back to the sourceRange - we need to all this from all SecGroupID: lbSecGroup.ID, EtherType: "IPv6", } _, err = rules.Create(lbaas.network, lbSecGroupRuleCreateOpts).Extract() if err != nil { // cleanup what was created so far _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } // Get the port ID port, err := getPortByIP(lbaas.network, loadbalancer.VipAddress) if err != nil { // cleanup what was created so far _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, err } update_opts := neutronports.UpdateOpts{SecurityGroups: []string{lbSecGroup.ID}} res := neutronports.Update(lbaas.network, port.ID, update_opts) if res.Err != nil { glog.Errorf("Error occured updating port: %s", port.ID) // cleanup what was created so far _ = lbaas.EnsureLoadBalancerDeleted(clusterName, apiService) return nil, res.Err } } return status, nil }
func (lbaas *LbaasV2) EnsureLoadBalancerDeleted(clusterName string, service *v1.Service) error { loadBalancerName := cloudprovider.GetLoadBalancerName(service) glog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v)", clusterName, loadBalancerName) loadbalancer, err := getLoadbalancerByName(lbaas.network, loadBalancerName) if err != nil && err != ErrNotFound { return err } if loadbalancer == nil { return nil } if lbaas.opts.FloatingNetworkId != "" && loadbalancer != nil { portID, err := getPortIDByIP(lbaas.network, loadbalancer.VipAddress) if err != nil { return err } floatingIP, err := getFloatingIPByPortID(lbaas.network, portID) if err != nil && err != ErrNotFound { return err } if floatingIP != nil { err = floatingips.Delete(lbaas.network, floatingIP.ID).ExtractErr() if err != nil && !isNotFound(err) { return err } } } // get all listeners associated with this loadbalancer var listenerIDs []string err = listeners.List(lbaas.network, listeners.ListOpts{LoadbalancerID: loadbalancer.ID}).EachPage(func(page pagination.Page) (bool, error) { listenerList, err := listeners.ExtractListeners(page) if err != nil { return false, err } for _, listener := range listenerList { listenerIDs = append(listenerIDs, listener.ID) } return true, nil }) if err != nil { return err } // get all pools (and health monitors) associated with this loadbalancer var poolIDs []string var monitorIDs []string err = v2pools.List(lbaas.network, v2pools.ListOpts{LoadbalancerID: loadbalancer.ID}).EachPage(func(page pagination.Page) (bool, error) { poolsList, err := v2pools.ExtractPools(page) if err != nil { return false, err } for _, pool := range poolsList { poolIDs = append(poolIDs, pool.ID) monitorIDs = append(monitorIDs, pool.MonitorID) } return true, nil }) if err != nil { return err } // get all members associated with each poolIDs var memberIDs []string for _, poolID := range poolIDs { err := v2pools.ListAssociateMembers(lbaas.network, poolID, v2pools.MemberListOpts{}).EachPage(func(page pagination.Page) (bool, error) { membersList, err := v2pools.ExtractMembers(page) if err != nil { return false, err } for _, member := range membersList { memberIDs = append(memberIDs, member.ID) } return true, nil }) if err != nil { return err } } // delete all monitors for _, monitorID := range monitorIDs { err := v2monitors.Delete(lbaas.network, monitorID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete all members and pools for _, poolID := range poolIDs { // delete all members for this pool for _, memberID := range memberIDs { err := v2pools.DeleteMember(lbaas.network, poolID, memberID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete pool err := v2pools.Delete(lbaas.network, poolID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete all listeners for _, listenerID := range listenerIDs { err := listeners.Delete(lbaas.network, listenerID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete loadbalancer err = loadbalancers.Delete(lbaas.network, loadbalancer.ID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerDeleted(lbaas.network, loadbalancer.ID) // Delete the Security Group if lbaas.opts.ManageSecurityGroups { // Generate Name lbSecGroupName := getSecurityGroupName(clusterName, service) lbSecGroupID, err := groups.IDFromName(lbaas.network, lbSecGroupName) if err != nil { glog.V(1).Infof("Error occurred finding security group: %s: %v", lbSecGroupName, err) return nil } lbSecGroup := groups.Delete(lbaas.network, lbSecGroupID) if lbSecGroup.Err != nil && !isNotFound(lbSecGroup.Err) { return lbSecGroup.Err } // Delete the rules in the Node Security Group opts := rules.ListOpts{ SecGroupID: lbaas.opts.NodeSecurityGroupID, RemoteGroupID: lbSecGroupID, } secGroupRules, err := getSecurityGroupRules(lbaas.network, opts) if err != nil && !isNotFound(err) { glog.Errorf("Error finding rules for remote group id %s in security group id %s", lbSecGroupID, lbaas.opts.NodeSecurityGroupID) return err } for _, rule := range secGroupRules { res := rules.Delete(lbaas.network, rule.ID) if res.Err != nil && !isNotFound(res.Err) { glog.V(1).Infof("Error occurred deleting security group rule: %s: %v", rule.ID, res.Err) } } } return nil }
func deletePool(t *testing.T, poolID string) { res := pools.Delete(base.Client, poolID) th.AssertNoErr(t, res.Err) t.Logf("Deleted Pool, ID %s", poolID) }
func (lbaas *LbaasV2) EnsureLoadBalancerDeleted(service *api.Service) error { loadBalancerName := cloudprovider.GetLoadBalancerName(service) glog.V(4).Infof("EnsureLoadBalancerDeleted(%v)", loadBalancerName) loadbalancer, err := getLoadbalancerByName(lbaas.network, loadBalancerName) if err != nil && err != ErrNotFound { return err } if loadbalancer == nil { return nil } if lbaas.opts.FloatingNetworkId != "" && loadbalancer != nil { portID, err := getPortIDByIP(lbaas.network, loadbalancer.VipAddress) if err != nil { return err } floatingIP, err := getFloatingIPByPortID(lbaas.network, portID) if err != nil && err != ErrNotFound { return err } if floatingIP != nil { err = floatingips.Delete(lbaas.network, floatingIP.ID).ExtractErr() if err != nil && !isNotFound(err) { return err } } } // get all listeners associated with this loadbalancer var listenerIDs []string err = listeners.List(lbaas.network, listeners.ListOpts{LoadbalancerID: loadbalancer.ID}).EachPage(func(page pagination.Page) (bool, error) { listenerList, err := listeners.ExtractListeners(page) if err != nil { return false, err } for _, listener := range listenerList { listenerIDs = append(listenerIDs, listener.ID) } return true, nil }) if err != nil { return err } // get all pools associated with this loadbalancer var poolIDs []string err = v2_pools.List(lbaas.network, v2_pools.ListOpts{LoadbalancerID: loadbalancer.ID}).EachPage(func(page pagination.Page) (bool, error) { poolsList, err := v2_pools.ExtractPools(page) if err != nil { return false, err } for _, pool := range poolsList { poolIDs = append(poolIDs, pool.ID) } return true, nil }) if err != nil { return err } // get all members associated with each poolIDs var memberIDs []string for _, poolID := range poolIDs { err := v2_pools.ListAssociateMembers(lbaas.network, poolID, v2_pools.MemberListOpts{}).EachPage(func(page pagination.Page) (bool, error) { membersList, err := v2_pools.ExtractMembers(page) if err != nil { return false, err } for _, member := range membersList { memberIDs = append(memberIDs, member.ID) } return true, nil }) if err != nil { return err } } // get all monitors associated with each poolIDs var monitorIDs []string for _, poolID := range poolIDs { err = v2_monitors.List(lbaas.network, v2_monitors.ListOpts{PoolID: poolID}).EachPage(func(page pagination.Page) (bool, error) { monitorsList, err := v2_monitors.ExtractMonitors(page) if err != nil { return false, err } for _, monitor := range monitorsList { monitorIDs = append(monitorIDs, monitor.ID) } return true, nil }) if err != nil { return err } } // delete all monitors for _, monitorID := range monitorIDs { err := v2_monitors.Delete(lbaas.network, monitorID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete all members and pools for _, poolID := range poolIDs { // delete all members for this pool for _, memberID := range memberIDs { err := v2_pools.DeleteMember(lbaas.network, poolID, memberID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete pool err := v2_pools.Delete(lbaas.network, poolID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete all listeners for _, listenerID := range listenerIDs { err := listeners.Delete(lbaas.network, listenerID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete loadbalancer err = loadbalancers.Delete(lbaas.network, loadbalancer.ID).ExtractErr() if err != nil && !isNotFound(err) { return err } waitLoadbalancerDeleted(lbaas.network, loadbalancer.ID) return nil }
func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *api.Service, nodeNames []string) (*api.LoadBalancerStatus, error) { glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v, %v)", clusterName, apiService.Namespace, apiService.Name, apiService.Spec.LoadBalancerIP, apiService.Spec.Ports, nodeNames, apiService.Annotations) ports := apiService.Spec.Ports if len(ports) == 0 { return nil, fmt.Errorf("no ports provided to openstack load balancer") } // Check for TCP protocol on each port // TODO: Convert all error messages to use an event recorder for _, port := range ports { if port.Protocol != api.ProtocolTCP { return nil, fmt.Errorf("Only TCP LoadBalancer is supported for openstack load balancers") } } affinity := api.ServiceAffinityNone //apiService.Spec.SessionAffinity var persistence *v2_pools.SessionPersistence switch affinity { case api.ServiceAffinityNone: persistence = nil case api.ServiceAffinityClientIP: persistence = &v2_pools.SessionPersistence{Type: "SOURCE_IP"} default: return nil, fmt.Errorf("unsupported load balancer affinity: %v", affinity) } sourceRanges, err := service.GetLoadBalancerSourceRanges(apiService) if err != nil { return nil, err } if !service.IsAllowAll(sourceRanges) { return nil, fmt.Errorf("Source range restrictions are not supported for openstack load balancers") } name := cloudprovider.GetLoadBalancerName(apiService) loadbalancer, err := getLoadbalancerByName(lbaas.network, name) if err != nil { if err != ErrNotFound { return nil, fmt.Errorf("Error getting loadbalancer %s: %v", name, err) } glog.V(2).Infof("Creating loadbalancer %s", name) loadbalancer, err = lbaas.createLoadBalancer(apiService, name) if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating loadbalancer %s: %v", name, err) } } else { glog.V(2).Infof("LoadBalancer %s already exists", name) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) lbmethod := v2_pools.LBMethod(lbaas.opts.LBMethod) if lbmethod == "" { lbmethod = v2_pools.LBMethodRoundRobin } oldListeners, err := getListenersByLoadBalancerID(lbaas.network, loadbalancer.ID) if err != nil { return nil, fmt.Errorf("Error getting LB %s listeners: %v", name, err) } for portIndex, port := range ports { listener := getListenerForPort(oldListeners, port) if listener == nil { glog.V(4).Infof("Creating listener for port %d", int(port.Port)) listener, err = listeners.Create(lbaas.network, listeners.CreateOpts{ Name: fmt.Sprintf("listener_%s_%d", name, portIndex), Protocol: listeners.Protocol(port.Protocol), ProtocolPort: int(port.Port), LoadbalancerID: loadbalancer.ID, }).Extract() if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating LB listener: %v", err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } glog.V(4).Infof("Listener for %s port %d: %s", string(port.Protocol), int(port.Port), listener.ID) // After all ports have been processed, remaining listeners are removed as obsolete. // Pop valid listeners. oldListeners = popListener(oldListeners, listener.ID) pool, err := getPoolByListenerID(lbaas.network, loadbalancer.ID, listener.ID) if err != nil && err != ErrNotFound { // Unknown error, retry later return nil, fmt.Errorf("Error getting pool for listener %s: %v", listener.ID, err) } if pool == nil { glog.V(4).Infof("Creating pool for listener %s", listener.ID) pool, err = v2_pools.Create(lbaas.network, v2_pools.CreateOpts{ Name: fmt.Sprintf("pool_%s_%d", name, portIndex), Protocol: v2_pools.Protocol(port.Protocol), LBMethod: lbmethod, ListenerID: listener.ID, Persistence: persistence, }).Extract() if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating pool for listener %s: %v", listener.ID, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } glog.V(4).Infof("Pool for listener %s: %s", listener.ID, pool.ID) members, err := getMembersByPoolID(lbaas.network, pool.ID) if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error getting pool members %s: %v", pool.ID, err) } for _, nodeName := range nodeNames { addr, err := getAddressByName(lbaas.compute, types.NodeName(nodeName)) if err != nil { if err == ErrNotFound { // Node failure, do not create member glog.Warningf("Failed to create LB pool member for node %s: %v", nodeName, err) continue } else { return nil, fmt.Errorf("Error getting address for node %s: %v", nodeName, err) } } if !memberExists(members, addr) { glog.V(4).Infof("Creating member for pool %s", pool.ID) _, err := v2_pools.CreateAssociateMember(lbaas.network, pool.ID, v2_pools.MemberCreateOpts{ ProtocolPort: int(port.NodePort), Address: addr, SubnetID: lbaas.opts.SubnetId, }).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB pool member for node: %s, %v", nodeName, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // After all members have been processed, remaining members are deleted as obsolete. members = popMember(members, addr) glog.V(4).Infof("Ensured pool %s has member for %s at %s", pool.ID, nodeName, addr) } // Delete obsolete members for this pool for _, member := range members { glog.V(4).Infof("Deleting obsolete member %s for pool %s address %s", member.ID, pool.ID, member.Address) err := v2_pools.DeleteMember(lbaas.network, pool.ID, member.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete member %s for pool %s address %s: %v", member.ID, pool.ID, member.Address, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } monitorID := pool.MonitorID if monitorID == "" && lbaas.opts.CreateMonitor { glog.V(4).Infof("Creating monitor for pool %s", pool.ID) monitor, err := v2_monitors.Create(lbaas.network, v2_monitors.CreateOpts{ PoolID: pool.ID, Type: string(port.Protocol), Delay: int(lbaas.opts.MonitorDelay.Duration.Seconds()), Timeout: int(lbaas.opts.MonitorTimeout.Duration.Seconds()), MaxRetries: int(lbaas.opts.MonitorMaxRetries), }).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB pool healthmonitor: %v", err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) monitorID = monitor.ID } glog.V(4).Infof("Monitor for pool %s: %s", pool.ID, monitorID) } // All remaining listeners are obsolete, delete for _, listener := range oldListeners { glog.V(4).Infof("Deleting obsolete listener %s:", listener.ID) // get pool for listener pool, err := getPoolByListenerID(lbaas.network, loadbalancer.ID, listener.ID) if err != nil && err != ErrNotFound { return nil, fmt.Errorf("Error getting pool for obsolete listener %s: %v", listener.ID, err) } if pool != nil { // get and delete monitor monitorID := pool.MonitorID if monitorID != "" { glog.V(4).Infof("Deleting obsolete monitor %s for pool %s", monitorID, pool.ID) err = v2_monitors.Delete(lbaas.network, monitorID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete monitor %s for pool %s: %v", monitorID, pool.ID, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // get and delete pool members members, err := getMembersByPoolID(lbaas.network, pool.ID) if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error getting members for pool %s: %v", pool.ID, err) } if members != nil { for _, member := range members { glog.V(4).Infof("Deleting obsolete member %s for pool %s address %s", member.ID, pool.ID, member.Address) err := v2_pools.DeleteMember(lbaas.network, pool.ID, member.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete member %s for pool %s address %s: %v", member.ID, pool.ID, member.Address, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } } glog.V(4).Infof("Deleting obsolete pool %s for listener %s", pool.ID, listener.ID) // delete pool err = v2_pools.Delete(lbaas.network, pool.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleting obsolete pool %s for listener %s: %v", pool.ID, listener.ID, err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) } // delete listener err = listeners.Delete(lbaas.network, listener.ID).ExtractErr() if err != nil && !isNotFound(err) { return nil, fmt.Errorf("Error deleteting obsolete listener: %v", err) } waitLoadbalancerActiveProvisioningStatus(lbaas.network, loadbalancer.ID) glog.V(2).Infof("Deleted obsolete listener: %s", listener.ID) } status := &api.LoadBalancerStatus{} status.Ingress = []api.LoadBalancerIngress{{IP: loadbalancer.VipAddress}} portID, err := getPortIDByIP(lbaas.network, loadbalancer.VipAddress) if err != nil { return nil, fmt.Errorf("Error getting port for LB vip %s: %v", loadbalancer.VipAddress, err) } floatIP, err := getFloatingIPByPortID(lbaas.network, portID) if err != nil && err != ErrNotFound { return nil, fmt.Errorf("Error getting floating ip for port %s: %v", portID, err) } if floatIP == nil && lbaas.opts.FloatingNetworkId != "" { glog.V(4).Infof("Creating floating ip for loadbalancer %s port %s", loadbalancer.ID, portID) floatIPOpts := floatingips.CreateOpts{ FloatingNetworkID: lbaas.opts.FloatingNetworkId, PortID: portID, } floatIP, err = floatingips.Create(lbaas.network, floatIPOpts).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB floatingip %+v: %v", floatIPOpts, err) } } status.Ingress = append(status.Ingress, api.LoadBalancerIngress{IP: floatIP.FloatingIP}) return status, nil }