Change:
Is this a BUG REPORT or FEATURE REQUEST?: /kind feature What happened: Currently openstack cloud provider just support for specifying internal LoadBalancerIP. I think that is unreasonable, actually user just use a floating ip to access a LoadBalancer service. So it is necessary to spport for specifying external LoadBalancerIP. What you expected to happen: Support for specifying external LoadBalancerIP on openstack Anything else we need to know?: Currently LoadBalancerIP can be specified in service.Spec.LoadBalancerIP, we can add annotation to distinguish between internal and external.
/sig openstack /assign I'd like to work for it.

Discussion:
errorStatus  = "ERROR" ServiceAnnotationLoadBalancerFloatingNetworkId = "loadbalancer.openstack.org/floating-network-id" <ins> // ServiceAnnotationLoadBalancerInternal is the annotation used on the service // to indicate that we want an internal loadbalancer service. // If the value of ServiceAnnotationLoadBalancerInternal is false, it indicates that we want an external loadbalancer service. Default to true. ServiceAnnotationLoadBalancerInternal = "service.beta.kubernetes.io/openstack-internal-load-balancer" </ins> ) // LoadBalancer implementation for LBaaS v1

return nil } <del> func (lbaas *LbaasV2) createLoadBalancer(service *v1.Service, name string) (*loadbalancers.LoadBalancer, error) { </del> <ins> func (lbaas *LbaasV2) createLoadBalancer(service *v1.Service, name string, internalAnnotation bool) (*loadbalancers.LoadBalancer, error) { </ins> createOpts := loadbalancers.CreateOpts{ Name:        name, Description: fmt.Sprintf("Kubernetes external service %s", name),

} loadBalancerIP := service.Spec.LoadBalancerIP <del> if loadBalancerIP != "" { </del> <ins> if loadBalancerIP != "" && internalAnnotation { </ins> createOpts.VipAddress = loadBalancerIP }

floatingPool := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerFloatingNetworkId, lbaas.opts.FloatingNetworkId) glog.V(4).Infof("EnsureLoadBalancer using floatingPool: %v", floatingPool) <ins> var internalAnnotation bool internal := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerInternal, "true") switch internal { case "true": glog.V(4).Infof("Ensure an internal loadbalancer service.") internalAnnotation = true case "false": if len(floatingPool) != 0 { glog.V(4).Infof("Ensure an external loadbalancer service.") internalAnnotation = false } else { return nil, fmt.Errorf("floating-network-id or loadbalancer.openstack.org/floating-network-id should be specified when service.beta.kubernetes.io/openstack-internal-load-balancer is false") } default: return nil, fmt.Errorf("unknow service.beta.kubernetes.io/openstack-internal-load-balancer annotation: %v, specify "true" or "false".", internal) } </ins> // Check for TCP protocol on each port // TODO: Convert all error messages to use an event recorder for _, port := range ports {

return nil, fmt.Errorf("Error getting loadbalancer %s: %v", name, err) } glog.V(2).Infof("Creating loadbalancer %s", name) <del> loadbalancer, err = lbaas.createLoadBalancer(apiService, name) </del> <ins> loadbalancer, err = lbaas.createLoadBalancer(apiService, name, internalAnnotation) </ins> if err != nil { // Unknown error, retry later return nil, fmt.Errorf("Error creating loadbalancer %s: %v", name, err)

if err != nil && err != ErrNotFound { return nil, fmt.Errorf("Error getting floating ip for port %s: %v", portID, err) } <del> if floatIP == nil && floatingPool != "" { </del> <ins> if floatIP == nil && floatingPool != "" && !internalAnnotation { </ins> glog.V(4).Infof("Creating floating ip for loadbalancer %s port %s", loadbalancer.ID, portID) floatIPOpts := floatingips.CreateOpts{ FloatingNetworkID: floatingPool, PortID:            portID, } <ins> loadBalancerIP := apiService.Spec.LoadBalancerIP if loadBalancerIP != "" { floatIPOpts.FloatingIP = loadBalancerIP } </ins> floatIP, err = floatingips.Create(lbaas.network, floatIPOpts).Extract() if err != nil { return nil, fmt.Errorf("Error creating LB floatingip %+v: %v", floatIPOpts, err)

lb.opts.SubnetId = subnetID } <ins> floatingPool := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerFloatingNetworkId, lb.opts.FloatingNetworkId) glog.V(4).Infof("EnsureLoadBalancer using floatingPool: %v", floatingPool) var internalAnnotation bool internal := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerInternal, "true") switch internal { case "true": glog.V(4).Infof("Ensure an internal loadbalancer service.") internalAnnotation = true case "false": if len(floatingPool) != 0 { glog.V(4).Infof("Ensure an external loadbalancer service.") internalAnnotation = false } else { return nil, fmt.Errorf("floating-network-id or loadbalancer.openstack.org/floating-network-id should be specified when service.beta.kubernetes.io/openstack-internal-load-balancer is false") } default: return nil, fmt.Errorf("unknow service.beta.kubernetes.io/openstack-internal-load-balancer annotation: %v, specify "true" or "false".", internal) } </ins> ports := apiService.Spec.Ports if len(ports) > 1 { return nil, fmt.Errorf("multiple ports are not supported in openstack v1 load balancers")

} loadBalancerIP := apiService.Spec.LoadBalancerIP <del> if loadBalancerIP != "" { </del> <ins> if loadBalancerIP != "" && internalAnnotation { </ins> createOpts.Address = loadBalancerIP }

status.Ingress = []v1.LoadBalancerIngress{{IP: vip.Address}} <del> if lb.opts.FloatingNetworkId != "" { </del> <ins> if floatingPool != "" && !internalAnnotation { </ins> floatIPOpts := floatingips.CreateOpts{ <del> FloatingNetworkID: lb.opts.FloatingNetworkId, </del> <ins> FloatingNetworkID: floatingPool, </ins> PortID:            vip.PortID, } <ins> loadBalancerIP := apiService.Spec.LoadBalancerIP if loadBalancerIP != "" { floatIPOpts.FloatingIP = loadBalancerIP } </ins> floatIP, err := floatingips.Create(lb.network, floatIPOpts).Extract() if err != nil { return nil, fmt.Errorf("Error creating floatingip for openstack load balancer %s: %v", name, err)

return err } <del> if lb.opts.FloatingNetworkId != "" && vip != nil { </del> <ins> if vip != nil && vip.PortID != "" { </ins> floatingIP, err := getFloatingIPByPortID(lb.network, vip.PortID) if err != nil && !isNotFound(err) { return err