diff --git a/pkg/cloudprovider/providers/openstack/openstack.go b/pkg/cloudprovider/providers/openstack/openstack.go index be941b52cf7..574bc1e58ab 100644 --- a/pkg/cloudprovider/providers/openstack/openstack.go +++ b/pkg/cloudprovider/providers/openstack/openstack.go @@ -83,7 +83,7 @@ type LoadBalancer struct { } type LoadBalancerOpts struct { - LBVersion string `gcfg:"lb-version"` // v1 or v2 + LBVersion string `gcfg:"lb-version"` // overrides autodetection. v1 or v2 SubnetId string `gcfg:"subnet-id"` // required FloatingNetworkId string `gcfg:"floating-network-id"` LBMethod string `gcfg:"lb-method"` @@ -526,13 +526,35 @@ func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) { return nil, false } + lbversion := os.lbOpts.LBVersion + if lbversion == "" { + // No version specified, try newest supported by server + netExts, err := networkExtensions(network) + if err != nil { + glog.Warningf("Failed to list neutron extensions: %v", err) + return nil, false + } + + if netExts["lbaasv2"] { + lbversion = "v2" + } else if netExts["lbaas"] { + lbversion = "v1" + } else { + glog.Warningf("Failed to find neutron LBaaS extension (v1 or v2)") + return nil, false + } + glog.V(3).Infof("Using LBaaS extension %v", lbversion) + } + glog.V(1).Info("Claiming to support LoadBalancer") if os.lbOpts.LBVersion == "v2" { return &LbaasV2{LoadBalancer{network, compute, os.lbOpts}}, true - } else { - + } else if lbversion == "v1" { return &LbaasV1{LoadBalancer{network, compute, os.lbOpts}}, true + } else { + glog.Warningf("Config error: unrecognised lb-version \"%v\"", lbversion) + return nil, false } } diff --git a/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go b/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go index 615210b705c..d96e3bdab0b 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go +++ b/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go @@ -20,6 +20,7 @@ import ( "time" "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" @@ -55,6 +56,24 @@ type LbaasV2 struct { LoadBalancer } +func networkExtensions(client *gophercloud.ServiceClient) (map[string]bool, error) { + seen := make(map[string]bool) + + pager := extensions.List(client) + err := pager.EachPage(func(page pagination.Page) (bool, error) { + exts, err := extensions.ExtractExtensions(page) + if err != nil { + return false, err + } + for _, ext := range exts { + seen[ext.Alias] = true + } + return true, nil + }) + + return seen, err +} + func getPortIDByIP(client *gophercloud.ServiceClient, ipAddress string) (string, error) { var portID string diff --git a/pkg/cloudprovider/providers/openstack/openstack_test.go b/pkg/cloudprovider/providers/openstack/openstack_test.go index 5a4b1ce98a1..b9978a10433 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_test.go +++ b/pkg/cloudprovider/providers/openstack/openstack_test.go @@ -205,50 +205,29 @@ func TestLoadBalancer(t *testing.T) { t.Skipf("No config found in environment") } - cfg.LoadBalancer.LBVersion = "v1" + versions := []string{"v1", "v2", ""} - os, err := newOpenStack(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) - } + for _, v := range versions { + t.Logf("Trying LBVersion = '%s'\n", v) + cfg.LoadBalancer.LBVersion = v - lb, ok := os.LoadBalancer() - if !ok { - t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?") - } + os, err := newOpenStack(cfg) + if err != nil { + t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) + } - _, exists, err := lb.GetLoadBalancer(testClusterName, &api.Service{ObjectMeta: api.ObjectMeta{Name: "noexist"}}) - if err != nil { - t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err) - } - if exists { - t.Fatalf("GetLoadBalancer(\"noexist\") returned exists") - } -} + lb, ok := os.LoadBalancer() + if !ok { + t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?") + } -func TestLoadBalancerV2(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - cfg.LoadBalancer.LBVersion = "v2" - - os, err := newOpenStack(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) - } - - lbaas, ok := os.LoadBalancer() - if !ok { - t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?") - } - - _, exists, err := lbaas.GetLoadBalancer(testClusterName, &api.Service{ObjectMeta: api.ObjectMeta{Name: "noexist"}}) - if err != nil { - t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err) - } - if exists { - t.Fatalf("GetLoadBalancer(\"noexist\") returned exists") + _, exists, err := lb.GetLoadBalancer(testClusterName, &api.Service{ObjectMeta: api.ObjectMeta{Name: "noexist"}}) + if err != nil { + t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err) + } + if exists { + t.Fatalf("GetLoadBalancer(\"noexist\") returned exists") + } } }