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.
This commit is contained in:
Angus Lees 2016-07-28 17:07:10 +10:00
parent 045663d43d
commit e4c354c329
3 changed files with 63 additions and 43 deletions

View File

@ -83,7 +83,7 @@ type LoadBalancer struct {
} }
type LoadBalancerOpts 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 SubnetId string `gcfg:"subnet-id"` // required
FloatingNetworkId string `gcfg:"floating-network-id"` FloatingNetworkId string `gcfg:"floating-network-id"`
LBMethod string `gcfg:"lb-method"` LBMethod string `gcfg:"lb-method"`
@ -526,13 +526,35 @@ func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
return nil, false 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") glog.V(1).Info("Claiming to support LoadBalancer")
if os.lbOpts.LBVersion == "v2" { if os.lbOpts.LBVersion == "v2" {
return &LbaasV2{LoadBalancer{network, compute, os.lbOpts}}, true return &LbaasV2{LoadBalancer{network, compute, os.lbOpts}}, true
} else { } else if lbversion == "v1" {
return &LbaasV1{LoadBalancer{network, compute, os.lbOpts}}, true return &LbaasV1{LoadBalancer{network, compute, os.lbOpts}}, true
} else {
glog.Warningf("Config error: unrecognised lb-version \"%v\"", lbversion)
return nil, false
} }
} }

View File

@ -20,6 +20,7 @@ import (
"time" "time"
"github.com/rackspace/gophercloud" "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/layer3/floatingips"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
@ -55,6 +56,24 @@ type LbaasV2 struct {
LoadBalancer 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) { func getPortIDByIP(client *gophercloud.ServiceClient, ipAddress string) (string, error) {
var portID string var portID string

View File

@ -205,50 +205,29 @@ func TestLoadBalancer(t *testing.T) {
t.Skipf("No config found in environment") t.Skipf("No config found in environment")
} }
cfg.LoadBalancer.LBVersion = "v1" versions := []string{"v1", "v2", ""}
os, err := newOpenStack(cfg) for _, v := range versions {
if err != nil { t.Logf("Trying LBVersion = '%s'\n", v)
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) cfg.LoadBalancer.LBVersion = v
}
lb, ok := os.LoadBalancer() os, err := newOpenStack(cfg)
if !ok { if err != nil {
t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?") t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
} }
_, exists, err := lb.GetLoadBalancer(testClusterName, &api.Service{ObjectMeta: api.ObjectMeta{Name: "noexist"}}) lb, ok := os.LoadBalancer()
if err != nil { if !ok {
t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err) t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?")
} }
if exists {
t.Fatalf("GetLoadBalancer(\"noexist\") returned exists")
}
}
func TestLoadBalancerV2(t *testing.T) { _, exists, err := lb.GetLoadBalancer(testClusterName, &api.Service{ObjectMeta: api.ObjectMeta{Name: "noexist"}})
cfg, ok := configFromEnv() if err != nil {
if !ok { t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err)
t.Skipf("No config found in environment") }
} if exists {
cfg.LoadBalancer.LBVersion = "v2" t.Fatalf("GetLoadBalancer(\"noexist\") returned exists")
}
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")
} }
} }