mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Implement Instances interface for OpenStack cloud provider
This commit is contained in:
parent
a90d503fce
commit
fffa0527d4
@ -17,15 +17,25 @@ limitations under the License.
|
|||||||
package openstack
|
package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"code.google.com/p/gcfg"
|
"code.google.com/p/gcfg"
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrServerNotFound = errors.New("Server not found")
|
||||||
|
var ErrMultipleServersFound = errors.New("Multiple servers matched query")
|
||||||
|
var ErrFlavorNotFound = errors.New("Flavor not found")
|
||||||
|
|
||||||
// OpenStack is an implementation of cloud provider Interface for OpenStack.
|
// OpenStack is an implementation of cloud provider Interface for OpenStack.
|
||||||
type OpenStack struct {
|
type OpenStack struct {
|
||||||
provider string
|
provider string
|
||||||
@ -91,11 +101,114 @@ func newOpenStack(cfg Config) (*OpenStack, error) {
|
|||||||
return &os, err
|
return &os, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (os *OpenStack) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) {
|
type Instances struct {
|
||||||
return nil, false
|
servers gophercloud.CloudServersProvider
|
||||||
|
flavor_to_resource map[string]*api.NodeResources // keyed by flavor id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Instances returns an implementation of Instances for OpenStack.
|
||||||
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
|
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
|
||||||
|
servers, err := gophercloud.ServersApi(os.access, gophercloud.ApiCriteria{
|
||||||
|
Type: "compute",
|
||||||
|
UrlChoice: gophercloud.PublicURL,
|
||||||
|
Region: os.region,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
flavors, err := servers.ListFlavors()
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
flavor_to_resource := make(map[string]*api.NodeResources, len(flavors))
|
||||||
|
for _, flavor := range flavors {
|
||||||
|
rsrc := api.NodeResources{
|
||||||
|
Capacity: api.ResourceList{
|
||||||
|
"cpu": util.NewIntOrStringFromInt(flavor.VCpus),
|
||||||
|
"memory": util.NewIntOrStringFromString(fmt.Sprintf("%dMiB", flavor.Ram)),
|
||||||
|
"openstack.org/disk": util.NewIntOrStringFromString(fmt.Sprintf("%dGB", flavor.Disk)),
|
||||||
|
"openstack.org/rxTxFactor": util.NewIntOrStringFromInt(int(flavor.RxTxFactor * 1000)),
|
||||||
|
"openstack.org/swap": util.NewIntOrStringFromString(fmt.Sprintf("%dMiB", flavor.Swap)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
flavor_to_resource[flavor.Id] = &rsrc
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Instances{servers, flavor_to_resource}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instances) List(name_filter string) ([]string, error) {
|
||||||
|
filter := url.Values{}
|
||||||
|
filter.Set("name", name_filter)
|
||||||
|
filter.Set("status", "ACTIVE")
|
||||||
|
|
||||||
|
servers, err := i.servers.ListServersByFilter(filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]string, len(servers))
|
||||||
|
for idx, srv := range servers {
|
||||||
|
ret[idx] = srv.Name
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServerByName(api gophercloud.CloudServersProvider, name string) (*gophercloud.Server, error) {
|
||||||
|
filter := url.Values{}
|
||||||
|
filter.Set("name", fmt.Sprintf("^%s$", regexp.QuoteMeta(name)))
|
||||||
|
filter.Set("status", "ACTIVE")
|
||||||
|
|
||||||
|
servers, err := api.ListServersByFilter(filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(servers) == 0 {
|
||||||
|
return nil, ErrServerNotFound
|
||||||
|
} else if len(servers) > 1 {
|
||||||
|
return nil, ErrMultipleServersFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return &servers[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instances) IPAddress(name string) (net.IP, error) {
|
||||||
|
srv, err := getServerByName(i.servers, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var s string
|
||||||
|
if len(srv.Addresses.Private) > 0 {
|
||||||
|
s = srv.Addresses.Private[0].Addr
|
||||||
|
} else if len(srv.Addresses.Public) > 0 {
|
||||||
|
s = srv.Addresses.Public[0].Addr
|
||||||
|
} else if srv.AccessIPv4 != "" {
|
||||||
|
s = srv.AccessIPv4
|
||||||
|
} else {
|
||||||
|
s = srv.AccessIPv6
|
||||||
|
}
|
||||||
|
return net.ParseIP(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) {
|
||||||
|
srv, err := getServerByName(i.servers, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rsrc, ok := i.flavor_to_resource[srv.Flavor.Id]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrFlavorNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsrc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os *OpenStack) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,11 +17,12 @@ limitations under the License.
|
|||||||
package openstack
|
package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewOpenStack(t *testing.T) {
|
func TestReadConfig(t *testing.T) {
|
||||||
_, err := readConfig(nil)
|
_, err := readConfig(nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Should fail when no config is provided: %s", err)
|
t.Errorf("Should fail when no config is provided: %s", err)
|
||||||
@ -54,3 +55,81 @@ func TestToAuthOptions(t *testing.T) {
|
|||||||
t.Errorf("Username %s != %s", ao.Username, cfg.Global.Username)
|
t.Errorf("Username %s != %s", ao.Username, cfg.Global.Username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This allows testing against an existing OpenStack install, using the
|
||||||
|
// standard OS_* OpenStack client environment variables.
|
||||||
|
func configFromEnv() (cfg Config, ok bool) {
|
||||||
|
cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL")
|
||||||
|
// gophercloud wants "provider" to point specifically at tokens URL
|
||||||
|
if !strings.HasSuffix(cfg.Global.AuthUrl, "/tokens") {
|
||||||
|
cfg.Global.AuthUrl += "/tokens"
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Global.TenantId = os.Getenv("OS_TENANT_ID")
|
||||||
|
// Rax/nova _insists_ that we don't specify both tenant ID and name
|
||||||
|
if cfg.Global.TenantId == "" {
|
||||||
|
cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Global.Username = os.Getenv("OS_USERNAME")
|
||||||
|
cfg.Global.Password = os.Getenv("OS_PASSWORD")
|
||||||
|
cfg.Global.ApiKey = os.Getenv("OS_API_KEY")
|
||||||
|
cfg.Global.Region = os.Getenv("OS_REGION_NAME")
|
||||||
|
|
||||||
|
ok = (cfg.Global.AuthUrl != "" &&
|
||||||
|
cfg.Global.Username != "" &&
|
||||||
|
(cfg.Global.Password != "" || cfg.Global.ApiKey != "") &&
|
||||||
|
(cfg.Global.TenantId != "" || cfg.Global.TenantName != ""))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewOpenStack(t *testing.T) {
|
||||||
|
cfg, ok := configFromEnv()
|
||||||
|
if !ok {
|
||||||
|
t.Skipf("No config found in environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := newOpenStack(cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInstances(t *testing.T) {
|
||||||
|
cfg, ok := configFromEnv()
|
||||||
|
if !ok {
|
||||||
|
t.Skipf("No config found in environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
os, err := newOpenStack(cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
i, ok := os.Instances()
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Instances() returned false")
|
||||||
|
}
|
||||||
|
|
||||||
|
srvs, err := i.List(".")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Instances.List() failed: %s", err)
|
||||||
|
}
|
||||||
|
if len(srvs) == 0 {
|
||||||
|
t.Fatalf("Instances.List() returned zero servers")
|
||||||
|
}
|
||||||
|
t.Logf("Found servers (%d): %s\n", len(srvs), srvs)
|
||||||
|
|
||||||
|
ip, err := i.IPAddress(srvs[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Instances.IPAddress(%s) failed: %s", srvs[0], err)
|
||||||
|
}
|
||||||
|
t.Logf("Found IPAddress(%s) = %s\n", srvs[0], ip)
|
||||||
|
|
||||||
|
rsrcs, err := i.GetNodeResources(srvs[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Instances.GetNodeResources(%s) failed: %s", srvs[0], err)
|
||||||
|
}
|
||||||
|
t.Logf("Found GetNodeResources(%s) = %s\n", srvs[0], rsrcs)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user