From e4c354c329ea9e4173c45f8cf391ab203fe0b8fa Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Thu, 28 Jul 2016 17:07:10 +1000 Subject: [PATCH] openstack: Autodetect LBaaS v1 vs v2 This removes the need to manually specify the version in all but unusual cases. For most installs this will effectively flip the default from v1 (deprecated) to v2 so conservative existing installs may want to manually configure "lb-version = v1" before upgrading. --- .../providers/openstack/openstack.go | 28 ++++++++- .../openstack/openstack_loadbalancer.go | 19 ++++++ .../providers/openstack/openstack_test.go | 59 ++++++------------- 3 files changed, 63 insertions(+), 43 deletions(-) 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") + } } }