Fix golint for openstack and cinder packages

This commit is contained in:
Davanum Srinivas 2018-02-03 18:44:57 -05:00
parent c6e581ff42
commit 73b46ff7db
16 changed files with 360 additions and 331 deletions

View File

@ -88,7 +88,6 @@ pkg/cloudprovider/providers/aws
pkg/cloudprovider/providers/fake pkg/cloudprovider/providers/fake
pkg/cloudprovider/providers/gce pkg/cloudprovider/providers/gce
pkg/cloudprovider/providers/gce/cloud pkg/cloudprovider/providers/gce/cloud
pkg/cloudprovider/providers/openstack
pkg/cloudprovider/providers/ovirt pkg/cloudprovider/providers/ovirt
pkg/cloudprovider/providers/photon pkg/cloudprovider/providers/photon
pkg/cloudprovider/providers/vsphere pkg/cloudprovider/providers/vsphere
@ -391,7 +390,6 @@ pkg/volume/aws_ebs
pkg/volume/azure_dd pkg/volume/azure_dd
pkg/volume/azure_file pkg/volume/azure_file
pkg/volume/cephfs pkg/volume/cephfs
pkg/volume/cinder
pkg/volume/configmap pkg/volume/configmap
pkg/volume/empty_dir pkg/volume/empty_dir
pkg/volume/fc pkg/volume/fc

View File

@ -33,12 +33,12 @@ import (
) )
const ( const (
// metadataUrlTemplate allows building an OpenStack Metadata service URL. // metadataURLTemplate allows building an OpenStack Metadata service URL.
// It's a hardcoded IPv4 link-local address as documented in "OpenStack Cloud // It's a hardcoded IPv4 link-local address as documented in "OpenStack Cloud
// Administrator Guide", chapter Compute - Networking with nova-network. // Administrator Guide", chapter Compute - Networking with nova-network.
//https://docs.openstack.org/nova/latest/admin/networking-nova.html#metadata-service //https://docs.openstack.org/nova/latest/admin/networking-nova.html#metadata-service
defaultMetadataVersion = "2012-08-10" defaultMetadataVersion = "2012-08-10"
metadataUrlTemplate = "http://169.254.169.254/openstack/%s/meta_data.json" metadataURLTemplate = "http://169.254.169.254/openstack/%s/meta_data.json"
// metadataID is used as an identifier on the metadata search order configuration. // metadataID is used as an identifier on the metadata search order configuration.
metadataID = "metadataService" metadataID = "metadataService"
@ -53,10 +53,10 @@ const (
configDriveID = "configDrive" configDriveID = "configDrive"
) )
// ErrBadMetadata is used to indicate a problem parsing data from metadata server
var ErrBadMetadata = errors.New("invalid OpenStack metadata, got empty uuid") var ErrBadMetadata = errors.New("invalid OpenStack metadata, got empty uuid")
// There are multiple device types. To keep it simple, we're using a single structure // DeviceMetadata is a single/simplified data structure for all kinds of device metadata types.
// for all device metadata types.
type DeviceMetadata struct { type DeviceMetadata struct {
Type string `json:"type"` Type string `json:"type"`
Bus string `json:"bus,omitempty"` Bus string `json:"bus,omitempty"`
@ -65,10 +65,11 @@ type DeviceMetadata struct {
// .. and other fields. // .. and other fields.
} }
// Assumes the "2012-08-10" meta_data.json format. // Metadata has the information fetched from OpenStack metadata service or
//https://docs.openstack.org/nova/latest/user/config-drive.html // config drives. Assumes the "2012-08-10" meta_data.json format.
// See http://docs.openstack.org/user-guide/cli_config_drive.html
type Metadata struct { type Metadata struct {
Uuid string `json:"uuid"` UUID string `json:"uuid"`
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
AvailabilityZone string `json:"availability_zone"` AvailabilityZone string `json:"availability_zone"`
Devices []DeviceMetadata `json:"devices,omitempty"` Devices []DeviceMetadata `json:"devices,omitempty"`
@ -84,15 +85,15 @@ func parseMetadata(r io.Reader) (*Metadata, error) {
return nil, err return nil, err
} }
if metadata.Uuid == "" { if metadata.UUID == "" {
return nil, ErrBadMetadata return nil, ErrBadMetadata
} }
return &metadata, nil return &metadata, nil
} }
func getMetadataUrl(metadataVersion string) string { func getMetadataURL(metadataVersion string) string {
return fmt.Sprintf(metadataUrlTemplate, metadataVersion) return fmt.Sprintf(metadataURLTemplate, metadataVersion)
} }
func getConfigDrivePath(metadataVersion string) string { func getConfigDrivePath(metadataVersion string) string {
@ -147,16 +148,16 @@ func getMetadataFromConfigDrive(metadataVersion string) (*Metadata, error) {
func getMetadataFromMetadataService(metadataVersion string) (*Metadata, error) { func getMetadataFromMetadataService(metadataVersion string) (*Metadata, error) {
// Try to get JSON from metadata server. // Try to get JSON from metadata server.
metadataUrl := getMetadataUrl(metadataVersion) metadataURL := getMetadataURL(metadataVersion)
glog.V(4).Infof("Attempting to fetch metadata from %s", metadataUrl) glog.V(4).Infof("Attempting to fetch metadata from %s", metadataURL)
resp, err := http.Get(metadataUrl) resp, err := http.Get(metadataURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("error fetching %s: %v", metadataUrl, err) return nil, fmt.Errorf("error fetching %s: %v", metadataURL, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status) err = fmt.Errorf("unexpected status code when reading metadata from %s: %s", metadataURL, resp.Status)
return nil, err return nil, err
} }

View File

@ -22,7 +22,7 @@ import (
) )
var FakeMetadata = Metadata{ var FakeMetadata = Metadata{
Uuid: "83679162-1378-4288-a2d4-70e13ec132aa", UUID: "83679162-1378-4288-a2d4-70e13ec132aa",
Hostname: "test", Hostname: "test",
AvailabilityZone: "nova", AvailabilityZone: "nova",
} }
@ -85,8 +85,8 @@ func TestParseMetadata(t *testing.T) {
t.Errorf("incorrect hostname: %s", md.Hostname) t.Errorf("incorrect hostname: %s", md.Hostname)
} }
if md.Uuid != "83679162-1378-4288-a2d4-70e13ec132aa" { if md.UUID != "83679162-1378-4288-a2d4-70e13ec132aa" {
t.Errorf("incorrect uuid: %s", md.Uuid) t.Errorf("incorrect uuid: %s", md.UUID)
} }
if md.AvailabilityZone != "nova" { if md.AvailabilityZone != "nova" {

View File

@ -49,20 +49,27 @@ import (
) )
const ( const (
// ProviderName is the name of the openstack provider
ProviderName = "openstack" ProviderName = "openstack"
AvailabilityZone = "availability_zone" availabilityZone = "availability_zone"
defaultTimeOut = 60 * time.Second defaultTimeOut = 60 * time.Second
) )
// ErrNotFound is used to inform that the object is missing
var ErrNotFound = errors.New("failed to find object") var ErrNotFound = errors.New("failed to find object")
// ErrMultipleResults is used when we unexpectedly get back multiple results
var ErrMultipleResults = errors.New("multiple results where only one expected") var ErrMultipleResults = errors.New("multiple results where only one expected")
// ErrNoAddressFound is used when we cannot find an ip address for the host
var ErrNoAddressFound = errors.New("no address found for host") var ErrNoAddressFound = errors.New("no address found for host")
// encoding.TextUnmarshaler interface for time.Duration // MyDuration is the encoding.TextUnmarshaler interface for time.Duration
type MyDuration struct { type MyDuration struct {
time.Duration time.Duration
} }
// UnmarshalText is used to convert from text to Duration
func (d *MyDuration) UnmarshalText(text []byte) error { func (d *MyDuration) UnmarshalText(text []byte) error {
res, err := time.ParseDuration(string(text)) res, err := time.ParseDuration(string(text))
if err != nil { if err != nil {
@ -72,6 +79,7 @@ func (d *MyDuration) UnmarshalText(text []byte) error {
return nil return nil
} }
// LoadBalancer is used for creating and maintaining load balancers
type LoadBalancer struct { type LoadBalancer struct {
network *gophercloud.ServiceClient network *gophercloud.ServiceClient
compute *gophercloud.ServiceClient compute *gophercloud.ServiceClient
@ -79,11 +87,12 @@ type LoadBalancer struct {
opts LoadBalancerOpts opts LoadBalancerOpts
} }
// LoadBalancerOpts have the options to talk to Neutron LBaaSV2 or Octavia
type LoadBalancerOpts struct { type LoadBalancerOpts struct {
LBVersion string `gcfg:"lb-version"` // overrides autodetection. Only support v2. LBVersion string `gcfg:"lb-version"` // overrides autodetection. Only support v2.
UseOctavia bool `gcfg:"use-octavia"` // uses Octavia V2 service catalog endpoint UseOctavia bool `gcfg:"use-octavia"` // uses Octavia V2 service catalog endpoint
SubnetId string `gcfg:"subnet-id"` // overrides autodetection. SubnetID string `gcfg:"subnet-id"` // overrides autodetection.
FloatingNetworkId string `gcfg:"floating-network-id"` // If specified, will create floating ip for loadbalancer, or do not create floating ip. FloatingNetworkID string `gcfg:"floating-network-id"` // If specified, will create floating ip for loadbalancer, or do not create floating ip.
LBMethod string `gcfg:"lb-method"` // default to ROUND_ROBIN. LBMethod string `gcfg:"lb-method"` // default to ROUND_ROBIN.
LBProvider string `gcfg:"lb-provider"` LBProvider string `gcfg:"lb-provider"`
CreateMonitor bool `gcfg:"create-monitor"` CreateMonitor bool `gcfg:"create-monitor"`
@ -94,16 +103,19 @@ type LoadBalancerOpts struct {
NodeSecurityGroupIDs []string // Do not specify, get it automatically when enable manage-security-groups. TODO(FengyunPan): move it into cache NodeSecurityGroupIDs []string // Do not specify, get it automatically when enable manage-security-groups. TODO(FengyunPan): move it into cache
} }
// BlockStorageOpts is used to talk to Cinder service
type BlockStorageOpts struct { type BlockStorageOpts struct {
BSVersion string `gcfg:"bs-version"` // overrides autodetection. v1 or v2. Defaults to auto BSVersion string `gcfg:"bs-version"` // overrides autodetection. v1 or v2. Defaults to auto
TrustDevicePath bool `gcfg:"trust-device-path"` // See Issue #33128 TrustDevicePath bool `gcfg:"trust-device-path"` // See Issue #33128
IgnoreVolumeAZ bool `gcfg:"ignore-volume-az"` IgnoreVolumeAZ bool `gcfg:"ignore-volume-az"`
} }
// RouterOpts is used for Neutron routes
type RouterOpts struct { type RouterOpts struct {
RouterId string `gcfg:"router-id"` // required RouterID string `gcfg:"router-id"` // required
} }
// MetadataOpts is used for configuring how to talk to metadata service or config drive
type MetadataOpts struct { type MetadataOpts struct {
SearchOrder string `gcfg:"search-order"` SearchOrder string `gcfg:"search-order"`
RequestTimeout MyDuration `gcfg:"request-timeout"` RequestTimeout MyDuration `gcfg:"request-timeout"`
@ -121,16 +133,17 @@ type OpenStack struct {
localInstanceID string localInstanceID string
} }
// Config is used to read and store information from the cloud configuration file
type Config struct { type Config struct {
Global struct { Global struct {
AuthUrl string `gcfg:"auth-url"` AuthURL string `gcfg:"auth-url"`
Username string Username string
UserId string `gcfg:"user-id"` UserID string `gcfg:"user-id"`
Password string Password string
TenantId string `gcfg:"tenant-id"` TenantID string `gcfg:"tenant-id"`
TenantName string `gcfg:"tenant-name"` TenantName string `gcfg:"tenant-name"`
TrustId string `gcfg:"trust-id"` TrustID string `gcfg:"trust-id"`
DomainId string `gcfg:"domain-id"` DomainID string `gcfg:"domain-id"`
DomainName string `gcfg:"domain-name"` DomainName string `gcfg:"domain-name"`
Region string Region string
CAFile string `gcfg:"ca-file"` CAFile string `gcfg:"ca-file"`
@ -142,7 +155,7 @@ type Config struct {
} }
func init() { func init() {
RegisterMetrics() registerMetrics()
cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
cfg, err := readConfig(config) cfg, err := readConfig(config)
@ -155,13 +168,13 @@ func init() {
func (cfg Config) toAuthOptions() gophercloud.AuthOptions { func (cfg Config) toAuthOptions() gophercloud.AuthOptions {
return gophercloud.AuthOptions{ return gophercloud.AuthOptions{
IdentityEndpoint: cfg.Global.AuthUrl, IdentityEndpoint: cfg.Global.AuthURL,
Username: cfg.Global.Username, Username: cfg.Global.Username,
UserID: cfg.Global.UserId, UserID: cfg.Global.UserID,
Password: cfg.Global.Password, Password: cfg.Global.Password,
TenantID: cfg.Global.TenantId, TenantID: cfg.Global.TenantID,
TenantName: cfg.Global.TenantName, TenantName: cfg.Global.TenantName,
DomainID: cfg.Global.DomainId, DomainID: cfg.Global.DomainID,
DomainName: cfg.Global.DomainName, DomainName: cfg.Global.DomainName,
// Persistent service, so we need to be able to renew tokens. // Persistent service, so we need to be able to renew tokens.
@ -171,11 +184,11 @@ func (cfg Config) toAuthOptions() gophercloud.AuthOptions {
func (cfg Config) toAuth3Options() tokens3.AuthOptions { func (cfg Config) toAuth3Options() tokens3.AuthOptions {
return tokens3.AuthOptions{ return tokens3.AuthOptions{
IdentityEndpoint: cfg.Global.AuthUrl, IdentityEndpoint: cfg.Global.AuthURL,
Username: cfg.Global.Username, Username: cfg.Global.Username,
UserID: cfg.Global.UserId, UserID: cfg.Global.UserID,
Password: cfg.Global.Password, Password: cfg.Global.Password,
DomainID: cfg.Global.DomainId, DomainID: cfg.Global.DomainID,
DomainName: cfg.Global.DomainName, DomainName: cfg.Global.DomainName,
AllowReauth: true, AllowReauth: true,
} }
@ -184,36 +197,38 @@ func (cfg Config) toAuth3Options() tokens3.AuthOptions {
// configFromEnv allows setting up credentials etc using the // configFromEnv allows setting up credentials etc using the
// standard OS_* OpenStack client environment variables. // standard OS_* OpenStack client environment variables.
func configFromEnv() (cfg Config, ok bool) { func configFromEnv() (cfg Config, ok bool) {
cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL") cfg.Global.AuthURL = os.Getenv("OS_AUTH_URL")
cfg.Global.Username = os.Getenv("OS_USERNAME") cfg.Global.Username = os.Getenv("OS_USERNAME")
cfg.Global.Password = os.Getenv("OS_PASSWORD") cfg.Global.Password = os.Getenv("OS_PASSWORD")
cfg.Global.Region = os.Getenv("OS_REGION_NAME") cfg.Global.Region = os.Getenv("OS_REGION_NAME")
cfg.Global.UserId = os.Getenv("OS_USER_ID") cfg.Global.UserID = os.Getenv("OS_USER_ID")
cfg.Global.TrustId = os.Getenv("OS_TRUST_ID") cfg.Global.TrustID = os.Getenv("OS_TRUST_ID")
cfg.Global.TenantId = os.Getenv("OS_TENANT_ID") cfg.Global.TenantID = os.Getenv("OS_TENANT_ID")
if cfg.Global.TenantId == "" { if cfg.Global.TenantID == "" {
cfg.Global.TenantId = os.Getenv("OS_PROJECT_ID") cfg.Global.TenantID = os.Getenv("OS_PROJECT_ID")
} }
cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME") cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME")
if cfg.Global.TenantName == "" { if cfg.Global.TenantName == "" {
cfg.Global.TenantName = os.Getenv("OS_PROJECT_NAME") cfg.Global.TenantName = os.Getenv("OS_PROJECT_NAME")
} }
cfg.Global.DomainId = os.Getenv("OS_DOMAIN_ID") cfg.Global.DomainID = os.Getenv("OS_DOMAIN_ID")
if cfg.Global.DomainId == "" { if cfg.Global.DomainID == "" {
cfg.Global.DomainId = os.Getenv("OS_USER_DOMAIN_ID") cfg.Global.DomainID = os.Getenv("OS_USER_DOMAIN_ID")
} }
cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME") cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME")
if cfg.Global.DomainName == "" { if cfg.Global.DomainName == "" {
cfg.Global.DomainName = os.Getenv("OS_USER_DOMAIN_NAME") cfg.Global.DomainName = os.Getenv("OS_USER_DOMAIN_NAME")
} }
ok = cfg.Global.AuthUrl != "" && ok = cfg.Global.AuthURL != "" &&
cfg.Global.Username != "" && cfg.Global.Username != "" &&
cfg.Global.Password != "" && cfg.Global.Password != "" &&
(cfg.Global.TenantId != "" || cfg.Global.TenantName != "" || (cfg.Global.TenantID != "" || cfg.Global.TenantName != "" ||
cfg.Global.DomainId != "" || cfg.Global.DomainName != "" || cfg.Global.Region != "" || cfg.Global.UserId != "" || cfg.Global.TrustId != "") cfg.Global.DomainID != "" || cfg.Global.DomainName != "" ||
cfg.Global.Region != "" || cfg.Global.UserID != "" ||
cfg.Global.TrustID != "")
cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID) cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID)
cfg.BlockStorage.BSVersion = "auto" cfg.BlockStorage.BSVersion = "auto"
@ -238,13 +253,13 @@ func readConfig(config io.Reader) (Config, error) {
return cfg, err return cfg, err
} }
// Tiny helper for conditional unwind logic // caller is a tiny helper for conditional unwind logic
type Caller bool type caller bool
func NewCaller() Caller { return Caller(true) } func newCaller() caller { return caller(true) }
func (c *Caller) Disarm() { *c = false } func (c *caller) disarm() { *c = false }
func (c *Caller) Call(f func()) { func (c *caller) call(f func()) {
if *c { if *c {
f() f()
} }
@ -269,7 +284,7 @@ func readInstanceID(searchOrder string) (string, error) {
return "", err return "", err
} }
return md.Uuid, nil return md.UUID, nil
} }
// check opts for OpenStack // check opts for OpenStack
@ -290,16 +305,11 @@ func checkOpenStackOpts(openstackOpts *OpenStack) error {
return fmt.Errorf("monitor-max-retries not set in cloud provider config") return fmt.Errorf("monitor-max-retries not set in cloud provider config")
} }
} }
return checkMetadataSearchOrder(openstackOpts.metadataOpts.SearchOrder)
if err := checkMetadataSearchOrder(openstackOpts.metadataOpts.SearchOrder); err != nil {
return err
}
return nil
} }
func newOpenStack(cfg Config) (*OpenStack, error) { func newOpenStack(cfg Config) (*OpenStack, error) {
provider, err := openstack.NewClient(cfg.Global.AuthUrl) provider, err := openstack.NewClient(cfg.Global.AuthURL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -313,10 +323,10 @@ func newOpenStack(cfg Config) (*OpenStack, error) {
provider.HTTPClient.Transport = netutil.SetOldTransportDefaults(&http.Transport{TLSClientConfig: config}) provider.HTTPClient.Transport = netutil.SetOldTransportDefaults(&http.Transport{TLSClientConfig: config})
} }
if cfg.Global.TrustId != "" { if cfg.Global.TrustID != "" {
opts := cfg.toAuth3Options() opts := cfg.toAuth3Options()
authOptsExt := trusts.AuthOptsExt{ authOptsExt := trusts.AuthOptsExt{
TrustID: cfg.Global.TrustId, TrustID: cfg.Global.TrustID,
AuthOptionsBuilder: &opts, AuthOptionsBuilder: &opts,
} }
err = openstack.AuthenticateV3(provider, authOptsExt, gophercloud.EndpointOpts{}) err = openstack.AuthenticateV3(provider, authOptsExt, gophercloud.EndpointOpts{})
@ -360,7 +370,7 @@ func mapNodeNameToServerName(nodeName types.NodeName) string {
return string(nodeName) return string(nodeName)
} }
// getNodeNameByID maps instanceid to types.NodeName // GetNodeNameByID maps instanceid to types.NodeName
func (os *OpenStack) GetNodeNameByID(instanceID string) (types.NodeName, error) { func (os *OpenStack) GetNodeNameByID(instanceID string) (types.NodeName, error) {
client, err := os.NewComputeV2() client, err := os.NewComputeV2()
var nodeName types.NodeName var nodeName types.NodeName
@ -441,7 +451,7 @@ func nodeAddresses(srv *servers.Server) ([]v1.NodeAddress, error) {
addrs := []v1.NodeAddress{} addrs := []v1.NodeAddress{}
type Address struct { type Address struct {
IpType string `mapstructure:"OS-EXT-IPS:type"` IPType string `mapstructure:"OS-EXT-IPS:type"`
Addr string Addr string
} }
@ -454,7 +464,7 @@ func nodeAddresses(srv *servers.Server) ([]v1.NodeAddress, error) {
for network, addrList := range addresses { for network, addrList := range addresses {
for _, props := range addrList { for _, props := range addrList {
var addressType v1.NodeAddressType var addressType v1.NodeAddressType
if props.IpType == "floating" || network == "public" { if props.IPType == "floating" || network == "public" {
addressType = v1.NodeExternalIP addressType = v1.NodeExternalIP
} else { } else {
addressType = v1.NodeInternalIP addressType = v1.NodeInternalIP
@ -537,6 +547,7 @@ func getAttachedInterfacesByID(client *gophercloud.ServiceClient, serviceID stri
return interfaces, nil return interfaces, nil
} }
// Clusters is a no-op
func (os *OpenStack) Clusters() (cloudprovider.Clusters, bool) { func (os *OpenStack) Clusters() (cloudprovider.Clusters, bool) {
return nil, false return nil, false
} }
@ -551,6 +562,7 @@ func (os *OpenStack) HasClusterID() bool {
return true return true
} }
// LoadBalancer initializes a LbaasV2 object
func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) { func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
glog.V(4).Info("openstack.LoadBalancer() called") glog.V(4).Info("openstack.LoadBalancer() called")
@ -587,11 +599,13 @@ func isNotFound(err error) bool {
return ok && e.Actual == http.StatusNotFound return ok && e.Actual == http.StatusNotFound
} }
// Zones indicates that we support zones
func (os *OpenStack) Zones() (cloudprovider.Zones, bool) { func (os *OpenStack) Zones() (cloudprovider.Zones, bool) {
glog.V(1).Info("Claiming to support Zones") glog.V(1).Info("Claiming to support Zones")
return os, true return os, true
} }
// GetZone returns the current zone
func (os *OpenStack) GetZone() (cloudprovider.Zone, error) { func (os *OpenStack) GetZone() (cloudprovider.Zone, error) {
md, err := getMetadata(os.metadataOpts.SearchOrder) md, err := getMetadata(os.metadataOpts.SearchOrder)
if err != nil { if err != nil {
@ -626,7 +640,7 @@ func (os *OpenStack) GetZoneByProviderID(providerID string) (cloudprovider.Zone,
} }
zone := cloudprovider.Zone{ zone := cloudprovider.Zone{
FailureDomain: srv.Metadata[AvailabilityZone], FailureDomain: srv.Metadata[availabilityZone],
Region: os.region, Region: os.region,
} }
glog.V(4).Infof("The instance %s in zone %v", srv.Name, zone) glog.V(4).Infof("The instance %s in zone %v", srv.Name, zone)
@ -651,13 +665,14 @@ func (os *OpenStack) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Z
} }
zone := cloudprovider.Zone{ zone := cloudprovider.Zone{
FailureDomain: srv.Metadata[AvailabilityZone], FailureDomain: srv.Metadata[availabilityZone],
Region: os.region, Region: os.region,
} }
glog.V(4).Infof("The instance %s in zone %v", srv.Name, zone) glog.V(4).Infof("The instance %s in zone %v", srv.Name, zone)
return zone, nil return zone, nil
} }
// Routes initializes routes support
func (os *OpenStack) Routes() (cloudprovider.Routes, bool) { func (os *OpenStack) Routes() (cloudprovider.Routes, bool) {
glog.V(4).Info("openstack.Routes() called") glog.V(4).Info("openstack.Routes() called")
@ -742,12 +757,12 @@ func (os *OpenStack) volumeService(forceVersion string) (volumeService, error) {
return &VolumesV1{sClient, os.bsOpts}, nil return &VolumesV1{sClient, os.bsOpts}, nil
} }
err_txt := "BlockStorage API version autodetection failed. " + errTxt := "BlockStorage API version autodetection failed. " +
"Please set it explicitly in cloud.conf in section [BlockStorage] with key `bs-version`" "Please set it explicitly in cloud.conf in section [BlockStorage] with key `bs-version`"
return nil, errors.New(err_txt) return nil, errors.New(errTxt)
default: default:
err_txt := fmt.Sprintf("Config error: unrecognised bs-version \"%v\"", os.bsOpts.BSVersion) errTxt := fmt.Sprintf("Config error: unrecognised bs-version \"%v\"", os.bsOpts.BSVersion)
return nil, errors.New(err_txt) return nil, errors.New(errTxt)
} }
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack"
) )
// NewNetworkV2 creates a ServiceClient that may be used with the neutron v2 API
func (os *OpenStack) NewNetworkV2() (*gophercloud.ServiceClient, error) { func (os *OpenStack) NewNetworkV2() (*gophercloud.ServiceClient, error) {
network, err := openstack.NewNetworkV2(os.provider, gophercloud.EndpointOpts{ network, err := openstack.NewNetworkV2(os.provider, gophercloud.EndpointOpts{
Region: os.region, Region: os.region,
@ -33,6 +34,7 @@ func (os *OpenStack) NewNetworkV2() (*gophercloud.ServiceClient, error) {
return network, nil return network, nil
} }
// NewComputeV2 creates a ServiceClient that may be used with the nova v2 API
func (os *OpenStack) NewComputeV2() (*gophercloud.ServiceClient, error) { func (os *OpenStack) NewComputeV2() (*gophercloud.ServiceClient, error) {
compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
Region: os.region, Region: os.region,
@ -43,6 +45,7 @@ func (os *OpenStack) NewComputeV2() (*gophercloud.ServiceClient, error) {
return compute, nil return compute, nil
} }
// NewBlockStorageV1 creates a ServiceClient that may be used with the Cinder v1 API
func (os *OpenStack) NewBlockStorageV1() (*gophercloud.ServiceClient, error) { func (os *OpenStack) NewBlockStorageV1() (*gophercloud.ServiceClient, error) {
storage, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ storage, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{
Region: os.region, Region: os.region,
@ -53,6 +56,7 @@ func (os *OpenStack) NewBlockStorageV1() (*gophercloud.ServiceClient, error) {
return storage, nil return storage, nil
} }
// NewBlockStorageV2 creates a ServiceClient that may be used with the Cinder v2 API
func (os *OpenStack) NewBlockStorageV2() (*gophercloud.ServiceClient, error) { func (os *OpenStack) NewBlockStorageV2() (*gophercloud.ServiceClient, error) {
storage, err := openstack.NewBlockStorageV2(os.provider, gophercloud.EndpointOpts{ storage, err := openstack.NewBlockStorageV2(os.provider, gophercloud.EndpointOpts{
Region: os.region, Region: os.region,
@ -63,6 +67,7 @@ func (os *OpenStack) NewBlockStorageV2() (*gophercloud.ServiceClient, error) {
return storage, nil return storage, nil
} }
// NewBlockStorageV3 creates a ServiceClient that may be used with the Cinder v3 API
func (os *OpenStack) NewBlockStorageV3() (*gophercloud.ServiceClient, error) { func (os *OpenStack) NewBlockStorageV3() (*gophercloud.ServiceClient, error) {
storage, err := openstack.NewBlockStorageV3(os.provider, gophercloud.EndpointOpts{ storage, err := openstack.NewBlockStorageV3(os.provider, gophercloud.EndpointOpts{
Region: os.region, Region: os.region,
@ -73,6 +78,7 @@ func (os *OpenStack) NewBlockStorageV3() (*gophercloud.ServiceClient, error) {
return storage, nil return storage, nil
} }
// NewLoadBalancerV2 creates a ServiceClient that may be used with the Neutron LBaaS v2 API
func (os *OpenStack) NewLoadBalancerV2() (*gophercloud.ServiceClient, error) { func (os *OpenStack) NewLoadBalancerV2() (*gophercloud.ServiceClient, error) {
var lb *gophercloud.ServiceClient var lb *gophercloud.ServiceClient
var err error var err error

View File

@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
) )
// Instances encapsulates an implementation of Instances for OpenStack.
type Instances struct { type Instances struct {
compute *gophercloud.ServiceClient compute *gophercloud.ServiceClient
opts MetadataOpts opts MetadataOpts
@ -51,7 +52,7 @@ func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
}, true }, true
} }
// Implementation of Instances.CurrentNodeName // CurrentNodeName implements Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname. // Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) { func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
md, err := getMetadata(i.opts.SearchOrder) md, err := getMetadata(i.opts.SearchOrder)
@ -61,10 +62,12 @@ func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
return types.NodeName(md.Hostname), nil return types.NodeName(md.Hostname), nil
} }
// AddSSHKeyToAllInstances is not implemented for OpenStack
func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error { func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
return cloudprovider.NotImplemented return cloudprovider.NotImplemented
} }
// NodeAddresses implements Instances.NodeAddresses
func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) { func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
glog.V(4).Infof("NodeAddresses(%v) called", name) glog.V(4).Infof("NodeAddresses(%v) called", name)
@ -212,9 +215,9 @@ func srvInstanceType(srv *servers.Server) (string, error) {
// See cloudprovider.GetInstanceProviderID and Instances.InstanceID. // See cloudprovider.GetInstanceProviderID and Instances.InstanceID.
func instanceIDFromProviderID(providerID string) (instanceID string, err error) { func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
// If Instances.InstanceID or cloudprovider.GetInstanceProviderID is changed, the regexp should be changed too. // If Instances.InstanceID or cloudprovider.GetInstanceProviderID is changed, the regexp should be changed too.
var providerIdRegexp = regexp.MustCompile(`^` + ProviderName + `:///([^/]+)$`) var providerIDRegexp = regexp.MustCompile(`^` + ProviderName + `:///([^/]+)$`)
matches := providerIdRegexp.FindStringSubmatch(providerID) matches := providerIDRegexp.FindStringSubmatch(providerID)
if len(matches) != 2 { if len(matches) != 2 {
return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"openstack:///InstanceID\"", providerID) return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"openstack:///InstanceID\"", providerID)
} }

View File

@ -68,7 +68,7 @@ const (
activeStatus = "ACTIVE" activeStatus = "ACTIVE"
errorStatus = "ERROR" errorStatus = "ERROR"
ServiceAnnotationLoadBalancerFloatingNetworkId = "loadbalancer.openstack.org/floating-network-id" ServiceAnnotationLoadBalancerFloatingNetworkID = "loadbalancer.openstack.org/floating-network-id"
// ServiceAnnotationLoadBalancerInternal is the annotation used on the service // ServiceAnnotationLoadBalancerInternal is the annotation used on the service
// to indicate that we want an internal loadbalancer service. // to indicate that we want an internal loadbalancer service.
@ -76,7 +76,7 @@ const (
ServiceAnnotationLoadBalancerInternal = "service.beta.kubernetes.io/openstack-internal-load-balancer" ServiceAnnotationLoadBalancerInternal = "service.beta.kubernetes.io/openstack-internal-load-balancer"
) )
// LoadBalancer implementation for LBaaS v2 // LbaasV2 is a LoadBalancer implementation for Neutron LBaaS v2 API
type LbaasV2 struct { type LbaasV2 struct {
LoadBalancer LoadBalancer
} }
@ -368,12 +368,10 @@ func waitLoadbalancerDeleted(client *gophercloud.ServiceClient, loadbalancerID s
if err != nil { if err != nil {
if err == ErrNotFound { if err == ErrNotFound {
return true, nil return true, nil
} else {
return false, err
} }
} else { return false, err
return false, nil
} }
return false, nil
}) })
if err == wait.ErrWaitTimeout { if err == wait.ErrWaitTimeout {
@ -442,7 +440,7 @@ func (lbaas *LbaasV2) createLoadBalancer(service *v1.Service, name string, inter
createOpts := loadbalancers.CreateOpts{ createOpts := loadbalancers.CreateOpts{
Name: name, Name: name,
Description: fmt.Sprintf("Kubernetes external service %s", name), Description: fmt.Sprintf("Kubernetes external service %s", name),
VipSubnetID: lbaas.opts.SubnetId, VipSubnetID: lbaas.opts.SubnetID,
Provider: lbaas.opts.LBProvider, Provider: lbaas.opts.LBProvider,
} }
@ -458,6 +456,7 @@ func (lbaas *LbaasV2) createLoadBalancer(service *v1.Service, name string, inter
return loadbalancer, nil return loadbalancer, nil
} }
// GetLoadBalancer returns whether the specified load balancer exists and its status
func (lbaas *LbaasV2) GetLoadBalancer(clusterName string, service *v1.Service) (*v1.LoadBalancerStatus, bool, error) { func (lbaas *LbaasV2) GetLoadBalancer(clusterName string, service *v1.Service) (*v1.LoadBalancerStatus, bool, error) {
loadBalancerName := cloudprovider.GetLoadBalancerName(service) loadBalancerName := cloudprovider.GetLoadBalancerName(service)
loadbalancer, err := getLoadbalancerByName(lbaas.lb, loadBalancerName) loadbalancer, err := getLoadbalancerByName(lbaas.lb, loadBalancerName)
@ -485,7 +484,7 @@ func (lbaas *LbaasV2) GetLoadBalancer(clusterName string, service *v1.Service) (
} }
// The LB needs to be configured with instance addresses on the same // The LB needs to be configured with instance addresses on the same
// subnet as the LB (aka opts.SubnetId). Currently we're just // subnet as the LB (aka opts.SubnetID). Currently we're just
// guessing that the node's InternalIP is the right address - and that // guessing that the node's InternalIP is the right address - and that
// should be sufficient for all "normal" cases. // should be sufficient for all "normal" cases.
func nodeAddressForLB(node *v1.Node) (string, error) { func nodeAddressForLB(node *v1.Node) (string, error) {
@ -584,8 +583,8 @@ func isSecurityGroupNotFound(err error) bool {
return false return false
} }
// getFloatingNetworkIdForLB returns a floating-network-id for cluster. // getFloatingNetworkIDForLB returns a floating-network-id for cluster.
func getFloatingNetworkIdForLB(client *gophercloud.ServiceClient) (string, error) { func getFloatingNetworkIDForLB(client *gophercloud.ServiceClient) (string, error) {
var floatingNetworkIds []string var floatingNetworkIds []string
type NetworkWithExternalExt struct { type NetworkWithExternalExt struct {
@ -635,6 +634,7 @@ func getFloatingNetworkIdForLB(client *gophercloud.ServiceClient) (string, error
// a list of regions (from config) and query/create loadbalancers in // a list of regions (from config) and query/create loadbalancers in
// each region. // each region.
// EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one.
func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) { func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v, %v)", clusterName, apiService.Namespace, apiService.Name, apiService.Spec.LoadBalancerIP, apiService.Spec.Ports, nodes, apiService.Annotations) glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v, %v)", clusterName, apiService.Namespace, apiService.Name, apiService.Spec.LoadBalancerIP, apiService.Spec.Ports, nodes, apiService.Annotations)
@ -642,16 +642,16 @@ func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Serv
return nil, fmt.Errorf("there are no available nodes for LoadBalancer service %s/%s", apiService.Namespace, apiService.Name) return nil, fmt.Errorf("there are no available nodes for LoadBalancer service %s/%s", apiService.Namespace, apiService.Name)
} }
if len(lbaas.opts.SubnetId) == 0 { if len(lbaas.opts.SubnetID) == 0 {
// Get SubnetId automatically. // Get SubnetID automatically.
// The LB needs to be configured with instance addresses on the same subnet, so get SubnetId by one node. // The LB needs to be configured with instance addresses on the same subnet, so get SubnetID by one node.
subnetID, err := getSubnetIDForLB(lbaas.compute, *nodes[0]) subnetID, err := getSubnetIDForLB(lbaas.compute, *nodes[0])
if err != nil { if err != nil {
glog.Warningf("Failed to find subnet-id for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err) glog.Warningf("Failed to find subnet-id for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
return nil, fmt.Errorf("no subnet-id for service %s/%s : subnet-id not set in cloud provider config, "+ return nil, fmt.Errorf("no subnet-id for service %s/%s : subnet-id not set in cloud provider config, "+
"and failed to find subnet-id from OpenStack: %v", apiService.Namespace, apiService.Name, err) "and failed to find subnet-id from OpenStack: %v", apiService.Namespace, apiService.Name, err)
} }
lbaas.opts.SubnetId = subnetID lbaas.opts.SubnetID = subnetID
} }
ports := apiService.Spec.Ports ports := apiService.Spec.Ports
@ -659,10 +659,10 @@ func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Serv
return nil, fmt.Errorf("no ports provided to openstack load balancer") return nil, fmt.Errorf("no ports provided to openstack load balancer")
} }
floatingPool := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerFloatingNetworkId, lbaas.opts.FloatingNetworkId) floatingPool := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerFloatingNetworkID, lbaas.opts.FloatingNetworkID)
if len(floatingPool) == 0 { if len(floatingPool) == 0 {
var err error var err error
floatingPool, err = getFloatingNetworkIdForLB(lbaas.network) floatingPool, err = getFloatingNetworkIDForLB(lbaas.network)
if err != nil { if err != nil {
glog.Warningf("Failed to find floating-network-id for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err) glog.Warningf("Failed to find floating-network-id for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
} }
@ -816,7 +816,7 @@ func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Serv
_, err := v2pools.CreateMember(lbaas.lb, pool.ID, v2pools.CreateMemberOpts{ _, err := v2pools.CreateMember(lbaas.lb, pool.ID, v2pools.CreateMemberOpts{
ProtocolPort: int(port.NodePort), ProtocolPort: int(port.NodePort),
Address: addr, Address: addr,
SubnetID: lbaas.opts.SubnetId, SubnetID: lbaas.opts.SubnetID,
}).Extract() }).Extract()
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating LB pool member for node: %s, %v", node.Name, err) return nil, fmt.Errorf("error creating LB pool member for node: %s, %v", node.Name, err)
@ -1111,8 +1111,8 @@ func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *v1.Ser
// update loadbalancer vip port // update loadbalancer vip port
if !found { if !found {
port.SecurityGroups = append(port.SecurityGroups, lbSecGroup.ID) port.SecurityGroups = append(port.SecurityGroups, lbSecGroup.ID)
update_opts := neutronports.UpdateOpts{SecurityGroups: &port.SecurityGroups} updateOpts := neutronports.UpdateOpts{SecurityGroups: &port.SecurityGroups}
res := neutronports.Update(lbaas.network, portID, update_opts) res := neutronports.Update(lbaas.network, portID, updateOpts)
if res.Err != nil { if res.Err != nil {
msg := fmt.Sprintf("Error occured updating port %s for loadbalancer service %s/%s: %v", portID, apiService.Namespace, apiService.Name, res.Err) msg := fmt.Sprintf("Error occured updating port %s for loadbalancer service %s/%s: %v", portID, apiService.Namespace, apiService.Name, res.Err)
return fmt.Errorf(msg) return fmt.Errorf(msg)
@ -1152,20 +1152,21 @@ func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *v1.Ser
return nil return nil
} }
// UpdateLoadBalancer updates hosts under the specified load balancer.
func (lbaas *LbaasV2) UpdateLoadBalancer(clusterName string, service *v1.Service, nodes []*v1.Node) error { func (lbaas *LbaasV2) UpdateLoadBalancer(clusterName string, service *v1.Service, nodes []*v1.Node) error {
loadBalancerName := cloudprovider.GetLoadBalancerName(service) loadBalancerName := cloudprovider.GetLoadBalancerName(service)
glog.V(4).Infof("UpdateLoadBalancer(%v, %v, %v)", clusterName, loadBalancerName, nodes) glog.V(4).Infof("UpdateLoadBalancer(%v, %v, %v)", clusterName, loadBalancerName, nodes)
if len(lbaas.opts.SubnetId) == 0 && len(nodes) > 0 { if len(lbaas.opts.SubnetID) == 0 && len(nodes) > 0 {
// Get SubnetId automatically. // Get SubnetID automatically.
// The LB needs to be configured with instance addresses on the same subnet, so get SubnetId by one node. // The LB needs to be configured with instance addresses on the same subnet, so get SubnetID by one node.
subnetID, err := getSubnetIDForLB(lbaas.compute, *nodes[0]) subnetID, err := getSubnetIDForLB(lbaas.compute, *nodes[0])
if err != nil { if err != nil {
glog.Warningf("Failed to find subnet-id for loadbalancer service %s/%s: %v", service.Namespace, service.Name, err) glog.Warningf("Failed to find subnet-id for loadbalancer service %s/%s: %v", service.Namespace, service.Name, err)
return fmt.Errorf("no subnet-id for service %s/%s : subnet-id not set in cloud provider config, "+ return fmt.Errorf("no subnet-id for service %s/%s : subnet-id not set in cloud provider config, "+
"and failed to find subnet-id from OpenStack: %v", service.Namespace, service.Name, err) "and failed to find subnet-id from OpenStack: %v", service.Namespace, service.Name, err)
} }
lbaas.opts.SubnetId = subnetID lbaas.opts.SubnetID = subnetID
} }
ports := service.Spec.Ports ports := service.Spec.Ports
@ -1254,7 +1255,7 @@ func (lbaas *LbaasV2) UpdateLoadBalancer(clusterName string, service *v1.Service
_, err := v2pools.CreateMember(lbaas.lb, pool.ID, v2pools.CreateMemberOpts{ _, err := v2pools.CreateMember(lbaas.lb, pool.ID, v2pools.CreateMemberOpts{
Address: addr, Address: addr,
ProtocolPort: int(port.NodePort), ProtocolPort: int(port.NodePort),
SubnetID: lbaas.opts.SubnetId, SubnetID: lbaas.opts.SubnetID,
}).Extract() }).Extract()
if err != nil { if err != nil {
return err return err
@ -1372,6 +1373,7 @@ func (lbaas *LbaasV2) updateSecurityGroup(clusterName string, apiService *v1.Ser
return nil return nil
} }
// EnsureLoadBalancerDeleted deletes the specified load balancer
func (lbaas *LbaasV2) EnsureLoadBalancerDeleted(clusterName string, service *v1.Service) error { func (lbaas *LbaasV2) EnsureLoadBalancerDeleted(clusterName string, service *v1.Service) error {
loadBalancerName := cloudprovider.GetLoadBalancerName(service) loadBalancerName := cloudprovider.GetLoadBalancerName(service)
glog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v)", clusterName, loadBalancerName) glog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v)", clusterName, loadBalancerName)
@ -1512,9 +1514,8 @@ func (lbaas *LbaasV2) EnsureSecurityGroupDeleted(clusterName string, service *v1
if isSecurityGroupNotFound(err) { if isSecurityGroupNotFound(err) {
// It is OK when the security group has been deleted by others. // It is OK when the security group has been deleted by others.
return nil return nil
} else {
return fmt.Errorf("Error occurred finding security group: %s: %v", lbSecGroupName, err)
} }
return fmt.Errorf("Error occurred finding security group: %s: %v", lbSecGroupName, err)
} }
lbSecGroup := groups.Delete(lbaas.network, lbSecGroupID) lbSecGroup := groups.Delete(lbaas.network, lbSecGroupID)

View File

@ -19,32 +19,32 @@ package openstack
import "github.com/prometheus/client_golang/prometheus" import "github.com/prometheus/client_golang/prometheus"
const ( const (
OpenstackSubsystem = "openstack" openstackSubsystem = "openstack"
OpenstackOperationKey = "cloudprovider_openstack_api_request_duration_seconds" openstackOperationKey = "cloudprovider_openstack_api_request_duration_seconds"
OpenstackOperationErrorKey = "cloudprovider_openstack_api_request_errors" openstackOperationErrorKey = "cloudprovider_openstack_api_request_errors"
) )
var ( var (
OpenstackOperationsLatency = prometheus.NewHistogramVec( openstackOperationsLatency = prometheus.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Subsystem: OpenstackSubsystem, Subsystem: openstackSubsystem,
Name: OpenstackOperationKey, Name: openstackOperationKey,
Help: "Latency of openstack api call", Help: "Latency of openstack api call",
}, },
[]string{"request"}, []string{"request"},
) )
OpenstackApiRequestErrors = prometheus.NewCounterVec( openstackAPIRequestErrors = prometheus.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Subsystem: OpenstackSubsystem, Subsystem: openstackSubsystem,
Name: OpenstackOperationErrorKey, Name: openstackOperationErrorKey,
Help: "Cumulative number of openstack Api call errors", Help: "Cumulative number of openstack Api call errors",
}, },
[]string{"request"}, []string{"request"},
) )
) )
func RegisterMetrics() { func registerMetrics() {
prometheus.MustRegister(OpenstackOperationsLatency) prometheus.MustRegister(openstackOperationsLatency)
prometheus.MustRegister(OpenstackApiRequestErrors) prometheus.MustRegister(openstackAPIRequestErrors)
} }

View File

@ -29,17 +29,19 @@ import (
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
) )
var ErrNoRouterId = errors.New("router-id not set in cloud provider config") var errNoRouterID = errors.New("router-id not set in cloud provider config")
// Routes implements the cloudprovider.Routes for OpenStack clouds
type Routes struct { type Routes struct {
compute *gophercloud.ServiceClient compute *gophercloud.ServiceClient
network *gophercloud.ServiceClient network *gophercloud.ServiceClient
opts RouterOpts opts RouterOpts
} }
// NewRoutes creates a new instance of Routes
func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, opts RouterOpts) (cloudprovider.Routes, error) { func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, opts RouterOpts) (cloudprovider.Routes, error) {
if opts.RouterId == "" { if opts.RouterID == "" {
return nil, ErrNoRouterId return nil, errNoRouterID
} }
return &Routes{ return &Routes{
@ -49,6 +51,7 @@ func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceC
}, nil }, nil
} }
// ListRoutes lists all managed routes that belong to the specified clusterName
func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) { func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
glog.V(4).Infof("ListRoutes(%v)", clusterName) glog.V(4).Infof("ListRoutes(%v)", clusterName)
@ -70,7 +73,7 @@ func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error)
return nil, err return nil, err
} }
router, err := routers.Get(r.network, r.opts.RouterId).Extract() router, err := routers.Get(r.network, r.opts.RouterID).Extract()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -136,10 +139,11 @@ func updateAllowedAddressPairs(network *gophercloud.ServiceClient, port *neutron
return unwinder, nil return unwinder, nil
} }
// CreateRoute creates the described managed route
func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error { func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
glog.V(4).Infof("CreateRoute(%v, %v, %v)", clusterName, nameHint, route) glog.V(4).Infof("CreateRoute(%v, %v, %v)", clusterName, nameHint, route)
onFailure := NewCaller() onFailure := newCaller()
addr, err := getAddressByName(r.compute, route.TargetNode) addr, err := getAddressByName(r.compute, route.TargetNode)
if err != nil { if err != nil {
@ -148,7 +152,7 @@ func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudpr
glog.V(4).Infof("Using nexthop %v for node %v", addr, route.TargetNode) glog.V(4).Infof("Using nexthop %v for node %v", addr, route.TargetNode)
router, err := routers.Get(r.network, r.opts.RouterId).Extract() router, err := routers.Get(r.network, r.opts.RouterID).Extract()
if err != nil { if err != nil {
return err return err
} }
@ -171,7 +175,7 @@ func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudpr
if err != nil { if err != nil {
return err return err
} }
defer onFailure.Call(unwind) defer onFailure.call(unwind)
// get the port of addr on target node. // get the port of addr on target node.
portID, err := getPortIDByIP(r.compute, route.TargetNode, addr) portID, err := getPortIDByIP(r.compute, route.TargetNode, addr)
@ -200,25 +204,26 @@ func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudpr
if err != nil { if err != nil {
return err return err
} }
defer onFailure.Call(unwind) defer onFailure.call(unwind)
} }
glog.V(4).Infof("Route created: %v", route) glog.V(4).Infof("Route created: %v", route)
onFailure.Disarm() onFailure.disarm()
return nil return nil
} }
// DeleteRoute deletes the specified managed route
func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) error { func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
glog.V(4).Infof("DeleteRoute(%v, %v)", clusterName, route) glog.V(4).Infof("DeleteRoute(%v, %v)", clusterName, route)
onFailure := NewCaller() onFailure := newCaller()
addr, err := getAddressByName(r.compute, route.TargetNode) addr, err := getAddressByName(r.compute, route.TargetNode)
if err != nil { if err != nil {
return err return err
} }
router, err := routers.Get(r.network, r.opts.RouterId).Extract() router, err := routers.Get(r.network, r.opts.RouterID).Extract()
if err != nil { if err != nil {
return err return err
} }
@ -245,7 +250,7 @@ func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) err
if err != nil { if err != nil {
return err return err
} }
defer onFailure.Call(unwind) defer onFailure.call(unwind)
// get the port of addr on target node. // get the port of addr on target node.
portID, err := getPortIDByIP(r.compute, route.TargetNode, addr) portID, err := getPortIDByIP(r.compute, route.TargetNode, addr)
@ -257,9 +262,9 @@ func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) err
return err return err
} }
addr_pairs := port.AllowedAddressPairs addrPairs := port.AllowedAddressPairs
index = -1 index = -1
for i, item := range addr_pairs { for i, item := range addrPairs {
if item.IPAddress == route.DestinationCIDR { if item.IPAddress == route.DestinationCIDR {
index = i index = i
break break
@ -268,18 +273,18 @@ func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) err
if index != -1 { if index != -1 {
// Delete element `index` // Delete element `index`
addr_pairs[index] = addr_pairs[len(addr_pairs)-1] addrPairs[index] = addrPairs[len(addrPairs)-1]
addr_pairs = addr_pairs[:len(addr_pairs)-1] addrPairs = addrPairs[:len(addrPairs)-1]
unwind, err := updateAllowedAddressPairs(r.network, port, addr_pairs) unwind, err := updateAllowedAddressPairs(r.network, port, addrPairs)
if err != nil { if err != nil {
return err return err
} }
defer onFailure.Call(unwind) defer onFailure.call(unwind)
} }
glog.V(4).Infof("Route deleted: %v", route) glog.V(4).Infof("Route deleted: %v", route)
onFailure.Disarm() onFailure.disarm()
return nil return nil
} }

View File

@ -40,7 +40,7 @@ func TestRoutes(t *testing.T) {
} }
// Pick the first router and server to try a test with // Pick the first router and server to try a test with
os.routeOpts.RouterId = getRouters(os)[0].ID os.routeOpts.RouterID = getRouters(os)[0].ID
servername := getServers(os)[0].Name servername := getServers(os)[0].Name
r, ok := os.Routes() r, ok := os.Routes()

View File

@ -37,9 +37,7 @@ import (
) )
const ( const (
volumeAvailableStatus = "available" testClusterName = "testCluster"
volumeInUseStatus = "in-use"
testClusterName = "testCluster"
volumeStatusTimeoutSeconds = 30 volumeStatusTimeoutSeconds = 30
// volumeStatus* is configuration of exponential backoff for // volumeStatus* is configuration of exponential backoff for
@ -68,9 +66,8 @@ func WaitForVolumeStatus(t *testing.T, os *OpenStack, volumeName string, status
status, status,
volumeStatusTimeoutSeconds) volumeStatusTimeoutSeconds)
return true, nil return true, nil
} else {
return false, nil
} }
return false, nil
}) })
if err == wait.ErrWaitTimeout { if err == wait.ErrWaitTimeout {
t.Logf("Volume (%s) status did not change to %s after %v seconds\n", t.Logf("Volume (%s) status did not change to %s after %v seconds\n",
@ -116,12 +113,12 @@ func TestReadConfig(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Should succeed when a valid config is provided: %s", err) t.Fatalf("Should succeed when a valid config is provided: %s", err)
} }
if cfg.Global.AuthUrl != "http://auth.url" { if cfg.Global.AuthURL != "http://auth.url" {
t.Errorf("incorrect authurl: %s", cfg.Global.AuthUrl) t.Errorf("incorrect authurl: %s", cfg.Global.AuthURL)
} }
if cfg.Global.UserId != "user" { if cfg.Global.UserID != "user" {
t.Errorf("incorrect userid: %s", cfg.Global.UserId) t.Errorf("incorrect userid: %s", cfg.Global.UserID)
} }
if cfg.Global.Password != "mypass" { if cfg.Global.Password != "mypass" {
@ -163,10 +160,10 @@ func TestToAuthOptions(t *testing.T) {
cfg := Config{} cfg := Config{}
cfg.Global.Username = "user" cfg.Global.Username = "user"
cfg.Global.Password = "pass" cfg.Global.Password = "pass"
cfg.Global.DomainId = "2a73b8f597c04551a0fdc8e95544be8a" cfg.Global.DomainID = "2a73b8f597c04551a0fdc8e95544be8a"
cfg.Global.DomainName = "local" cfg.Global.DomainName = "local"
cfg.Global.AuthUrl = "http://auth.url" cfg.Global.AuthURL = "http://auth.url"
cfg.Global.UserId = "user" cfg.Global.UserID = "user"
ao := cfg.toAuthOptions() ao := cfg.toAuthOptions()
@ -179,20 +176,20 @@ func TestToAuthOptions(t *testing.T) {
if ao.Password != cfg.Global.Password { if ao.Password != cfg.Global.Password {
t.Errorf("Password %s != %s", ao.Password, cfg.Global.Password) t.Errorf("Password %s != %s", ao.Password, cfg.Global.Password)
} }
if ao.DomainID != cfg.Global.DomainId { if ao.DomainID != cfg.Global.DomainID {
t.Errorf("DomainID %s != %s", ao.DomainID, cfg.Global.DomainId) t.Errorf("DomainID %s != %s", ao.DomainID, cfg.Global.DomainID)
} }
if ao.IdentityEndpoint != cfg.Global.AuthUrl { if ao.IdentityEndpoint != cfg.Global.AuthURL {
t.Errorf("IdentityEndpoint %s != %s", ao.IdentityEndpoint, cfg.Global.AuthUrl) t.Errorf("IdentityEndpoint %s != %s", ao.IdentityEndpoint, cfg.Global.AuthURL)
} }
if ao.UserID != cfg.Global.UserId { if ao.UserID != cfg.Global.UserID {
t.Errorf("UserID %s != %s", ao.UserID, cfg.Global.UserId) t.Errorf("UserID %s != %s", ao.UserID, cfg.Global.UserID)
} }
if ao.DomainName != cfg.Global.DomainName { if ao.DomainName != cfg.Global.DomainName {
t.Errorf("DomainName %s != %s", ao.DomainName, cfg.Global.DomainName) t.Errorf("DomainName %s != %s", ao.DomainName, cfg.Global.DomainName)
} }
if ao.TenantID != cfg.Global.TenantId { if ao.TenantID != cfg.Global.TenantID {
t.Errorf("TenantID %s != %s", ao.TenantID, cfg.Global.TenantId) t.Errorf("TenantID %s != %s", ao.TenantID, cfg.Global.TenantID)
} }
} }
@ -210,8 +207,8 @@ func TestCheckOpenStackOpts(t *testing.T) {
provider: nil, provider: nil,
lbOpts: LoadBalancerOpts{ lbOpts: LoadBalancerOpts{
LBVersion: "v2", LBVersion: "v2",
SubnetId: "6261548e-ffde-4bc7-bd22-59c83578c5ef", SubnetID: "6261548e-ffde-4bc7-bd22-59c83578c5ef",
FloatingNetworkId: "38b8b5f9-64dc-4424-bf86-679595714786", FloatingNetworkID: "38b8b5f9-64dc-4424-bf86-679595714786",
LBMethod: "ROUND_ROBIN", LBMethod: "ROUND_ROBIN",
LBProvider: "haproxy", LBProvider: "haproxy",
CreateMonitor: true, CreateMonitor: true,
@ -232,7 +229,7 @@ func TestCheckOpenStackOpts(t *testing.T) {
provider: nil, provider: nil,
lbOpts: LoadBalancerOpts{ lbOpts: LoadBalancerOpts{
LBVersion: "v2", LBVersion: "v2",
FloatingNetworkId: "38b8b5f9-64dc-4424-bf86-679595714786", FloatingNetworkID: "38b8b5f9-64dc-4424-bf86-679595714786",
LBMethod: "ROUND_ROBIN", LBMethod: "ROUND_ROBIN",
CreateMonitor: true, CreateMonitor: true,
MonitorDelay: delay, MonitorDelay: delay,
@ -252,8 +249,8 @@ func TestCheckOpenStackOpts(t *testing.T) {
provider: nil, provider: nil,
lbOpts: LoadBalancerOpts{ lbOpts: LoadBalancerOpts{
LBVersion: "v2", LBVersion: "v2",
SubnetId: "6261548e-ffde-4bc7-bd22-59c83578c5ef", SubnetID: "6261548e-ffde-4bc7-bd22-59c83578c5ef",
FloatingNetworkId: "38b8b5f9-64dc-4424-bf86-679595714786", FloatingNetworkID: "38b8b5f9-64dc-4424-bf86-679595714786",
LBMethod: "ROUND_ROBIN", LBMethod: "ROUND_ROBIN",
CreateMonitor: true, CreateMonitor: true,
MonitorTimeout: timeout, MonitorTimeout: timeout,
@ -303,8 +300,8 @@ func TestCheckOpenStackOpts(t *testing.T) {
provider: nil, provider: nil,
lbOpts: LoadBalancerOpts{ lbOpts: LoadBalancerOpts{
LBVersion: "v2", LBVersion: "v2",
SubnetId: "6261548e-ffde-4bc7-bd22-59c83578c5ef", SubnetID: "6261548e-ffde-4bc7-bd22-59c83578c5ef",
FloatingNetworkId: "38b8b5f9-64dc-4424-bf86-679595714786", FloatingNetworkID: "38b8b5f9-64dc-4424-bf86-679595714786",
LBMethod: "ROUND_ROBIN", LBMethod: "ROUND_ROBIN",
CreateMonitor: true, CreateMonitor: true,
MonitorDelay: delay, MonitorDelay: delay,
@ -323,8 +320,8 @@ func TestCheckOpenStackOpts(t *testing.T) {
provider: nil, provider: nil,
lbOpts: LoadBalancerOpts{ lbOpts: LoadBalancerOpts{
LBVersion: "v2", LBVersion: "v2",
SubnetId: "6261548e-ffde-4bc7-bd22-59c83578c5ef", SubnetID: "6261548e-ffde-4bc7-bd22-59c83578c5ef",
FloatingNetworkId: "38b8b5f9-64dc-4424-bf86-679595714786", FloatingNetworkID: "38b8b5f9-64dc-4424-bf86-679595714786",
LBMethod: "ROUND_ROBIN", LBMethod: "ROUND_ROBIN",
CreateMonitor: true, CreateMonitor: true,
MonitorDelay: delay, MonitorDelay: delay,
@ -356,39 +353,39 @@ func TestCaller(t *testing.T) {
called := false called := false
myFunc := func() { called = true } myFunc := func() { called = true }
c := NewCaller() c := newCaller()
c.Call(myFunc) c.call(myFunc)
if !called { if !called {
t.Errorf("Caller failed to call function in default case") t.Errorf("caller failed to call function in default case")
} }
c.Disarm() c.disarm()
called = false called = false
c.Call(myFunc) c.call(myFunc)
if called { if called {
t.Error("Caller still called function when disarmed") t.Error("caller still called function when disarmed")
} }
// Confirm the "usual" deferred Caller pattern works as expected // Confirm the "usual" deferred caller pattern works as expected
called = false called = false
success_case := func() { successCase := func() {
c := NewCaller() c := newCaller()
defer c.Call(func() { called = true }) defer c.call(func() { called = true })
c.Disarm() c.disarm()
} }
if success_case(); called { if successCase(); called {
t.Error("Deferred success case still invoked unwind") t.Error("Deferred success case still invoked unwind")
} }
called = false called = false
failure_case := func() { failureCase := func() {
c := NewCaller() c := newCaller()
defer c.Call(func() { called = true }) defer c.call(func() { called = true })
} }
if failure_case(); !called { if failureCase(); !called {
t.Error("Deferred failure case failed to invoke unwind") t.Error("Deferred failure case failed to invoke unwind")
} }
} }
@ -563,15 +560,15 @@ func TestVolumes(t *testing.T) {
if err != nil { if err != nil {
t.Logf("Cannot find instance id: %v - perhaps you are running this test outside a VM launched by OpenStack", err) t.Logf("Cannot find instance id: %v - perhaps you are running this test outside a VM launched by OpenStack", err)
} else { } else {
diskId, err := os.AttachDisk(id, vol) diskID, err := os.AttachDisk(id, vol)
if err != nil { if err != nil {
t.Fatalf("Cannot AttachDisk Cinder volume %s: %v", vol, err) t.Fatalf("Cannot AttachDisk Cinder volume %s: %v", vol, err)
} }
t.Logf("Volume (%s) attached, disk ID: %s\n", vol, diskId) t.Logf("Volume (%s) attached, disk ID: %s\n", vol, diskID)
WaitForVolumeStatus(t, os, vol, volumeInUseStatus) WaitForVolumeStatus(t, os, vol, volumeInUseStatus)
devicePath := os.GetDevicePath(diskId) devicePath := os.GetDevicePath(diskID)
if diskPathRegexp.FindString(devicePath) == "" { if diskPathRegexp.FindString(devicePath) == "" {
t.Fatalf("GetDevicePath returned and unexpected path for Cinder volume %s, returned %s", vol, devicePath) t.Fatalf("GetDevicePath returned and unexpected path for Cinder volume %s, returned %s", vol, devicePath)
} }
@ -654,10 +651,10 @@ func TestToAuth3Options(t *testing.T) {
cfg := Config{} cfg := Config{}
cfg.Global.Username = "user" cfg.Global.Username = "user"
cfg.Global.Password = "pass" cfg.Global.Password = "pass"
cfg.Global.DomainId = "2a73b8f597c04551a0fdc8e95544be8a" cfg.Global.DomainID = "2a73b8f597c04551a0fdc8e95544be8a"
cfg.Global.DomainName = "local" cfg.Global.DomainName = "local"
cfg.Global.AuthUrl = "http://auth.url" cfg.Global.AuthURL = "http://auth.url"
cfg.Global.UserId = "user" cfg.Global.UserID = "user"
ao := cfg.toAuth3Options() ao := cfg.toAuth3Options()
@ -670,14 +667,14 @@ func TestToAuth3Options(t *testing.T) {
if ao.Password != cfg.Global.Password { if ao.Password != cfg.Global.Password {
t.Errorf("Password %s != %s", ao.Password, cfg.Global.Password) t.Errorf("Password %s != %s", ao.Password, cfg.Global.Password)
} }
if ao.DomainID != cfg.Global.DomainId { if ao.DomainID != cfg.Global.DomainID {
t.Errorf("DomainID %s != %s", ao.DomainID, cfg.Global.DomainId) t.Errorf("DomainID %s != %s", ao.DomainID, cfg.Global.DomainID)
} }
if ao.IdentityEndpoint != cfg.Global.AuthUrl { if ao.IdentityEndpoint != cfg.Global.AuthURL {
t.Errorf("IdentityEndpoint %s != %s", ao.IdentityEndpoint, cfg.Global.AuthUrl) t.Errorf("IdentityEndpoint %s != %s", ao.IdentityEndpoint, cfg.Global.AuthURL)
} }
if ao.UserID != cfg.Global.UserId { if ao.UserID != cfg.Global.UserID {
t.Errorf("UserID %s != %s", ao.UserID, cfg.Global.UserId) t.Errorf("UserID %s != %s", ao.UserID, cfg.Global.UserID)
} }
if ao.DomainName != cfg.Global.DomainName { if ao.DomainName != cfg.Global.DomainName {
t.Errorf("DomainName %s != %s", ao.DomainName, cfg.Global.DomainName) t.Errorf("DomainName %s != %s", ao.DomainName, cfg.Global.DomainName)

View File

@ -45,36 +45,37 @@ import (
) )
type volumeService interface { type volumeService interface {
createVolume(opts VolumeCreateOpts) (string, string, error) createVolume(opts volumeCreateOpts) (string, string, error)
getVolume(volumeID string) (Volume, error) getVolume(volumeID string) (Volume, error)
deleteVolume(volumeName string) error deleteVolume(volumeName string) error
expandVolume(volumeID string, newSize int) error expandVolume(volumeID string, newSize int) error
} }
// Volumes implementation for v1 // VolumesV1 is a Volumes implementation for cinder v1
type VolumesV1 struct { type VolumesV1 struct {
blockstorage *gophercloud.ServiceClient blockstorage *gophercloud.ServiceClient
opts BlockStorageOpts opts BlockStorageOpts
} }
// Volumes implementation for v2 // VolumesV2 is a Volumes implementation for cinder v2
type VolumesV2 struct { type VolumesV2 struct {
blockstorage *gophercloud.ServiceClient blockstorage *gophercloud.ServiceClient
opts BlockStorageOpts opts BlockStorageOpts
} }
// Volumes implementation for v3 // VolumesV3 is a Volumes implementation for cinder v3
type VolumesV3 struct { type VolumesV3 struct {
blockstorage *gophercloud.ServiceClient blockstorage *gophercloud.ServiceClient
opts BlockStorageOpts opts BlockStorageOpts
} }
// Volume stores information about a single volume
type Volume struct { type Volume struct {
// ID of the instance, to which this volume is attached. "" if not attached // ID of the instance, to which this volume is attached. "" if not attached
AttachedServerId string AttachedServerID string
// Device file path // Device file path
AttachedDevice string AttachedDevice string
// AvailabilityZone is which availability zone the volume is in // availabilityZone is which availability zone the volume is in
AvailabilityZone string AvailabilityZone string
// Unique identifier for the volume. // Unique identifier for the volume.
ID string ID string
@ -86,7 +87,7 @@ type Volume struct {
Size int Size int
} }
type VolumeCreateOpts struct { type volumeCreateOpts struct {
Size int Size int
Availability string Availability string
Name string Name string
@ -98,21 +99,21 @@ type VolumeCreateOpts struct {
var _ cloudprovider.PVLabeler = (*OpenStack)(nil) var _ cloudprovider.PVLabeler = (*OpenStack)(nil)
const ( const (
VolumeAvailableStatus = "available" volumeAvailableStatus = "available"
VolumeInUseStatus = "in-use" volumeInUseStatus = "in-use"
VolumeDeletedStatus = "deleted" volumeDeletedStatus = "deleted"
VolumeErrorStatus = "error" volumeErrorStatus = "error"
// On some environments, we need to query the metadata service in order // On some environments, we need to query the metadata service in order
// to locate disks. We'll use the Newton version, which includes device // to locate disks. We'll use the Newton version, which includes device
// metadata. // metadata.
NewtonMetadataVersion = "2016-06-30" newtonMetadataVersion = "2016-06-30"
) )
func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, error) { func (volumes *VolumesV1) createVolume(opts volumeCreateOpts) (string, string, error) {
startTime := time.Now() startTime := time.Now()
create_opts := volumes_v1.CreateOpts{ createOpts := volumes_v1.CreateOpts{
Name: opts.Name, Name: opts.Name,
Size: opts.Size, Size: opts.Size,
VolumeType: opts.VolumeType, VolumeType: opts.VolumeType,
@ -120,7 +121,7 @@ func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, e
Metadata: opts.Metadata, Metadata: opts.Metadata,
} }
vol, err := volumes_v1.Create(volumes.blockstorage, create_opts).Extract() vol, err := volumes_v1.Create(volumes.blockstorage, createOpts).Extract()
timeTaken := time.Since(startTime).Seconds() timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("create_v1_volume", timeTaken, err) recordOpenstackOperationMetric("create_v1_volume", timeTaken, err)
if err != nil { if err != nil {
@ -129,10 +130,10 @@ func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, e
return vol.ID, vol.AvailabilityZone, nil return vol.ID, vol.AvailabilityZone, nil
} }
func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, error) { func (volumes *VolumesV2) createVolume(opts volumeCreateOpts) (string, string, error) {
startTime := time.Now() startTime := time.Now()
create_opts := volumes_v2.CreateOpts{ createOpts := volumes_v2.CreateOpts{
Name: opts.Name, Name: opts.Name,
Size: opts.Size, Size: opts.Size,
VolumeType: opts.VolumeType, VolumeType: opts.VolumeType,
@ -140,7 +141,7 @@ func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, e
Metadata: opts.Metadata, Metadata: opts.Metadata,
} }
vol, err := volumes_v2.Create(volumes.blockstorage, create_opts).Extract() vol, err := volumes_v2.Create(volumes.blockstorage, createOpts).Extract()
timeTaken := time.Since(startTime).Seconds() timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("create_v2_volume", timeTaken, err) recordOpenstackOperationMetric("create_v2_volume", timeTaken, err)
if err != nil { if err != nil {
@ -149,10 +150,10 @@ func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, e
return vol.ID, vol.AvailabilityZone, nil return vol.ID, vol.AvailabilityZone, nil
} }
func (volumes *VolumesV3) createVolume(opts VolumeCreateOpts) (string, string, error) { func (volumes *VolumesV3) createVolume(opts volumeCreateOpts) (string, string, error) {
startTime := time.Now() startTime := time.Now()
create_opts := volumes_v3.CreateOpts{ createOpts := volumes_v3.CreateOpts{
Name: opts.Name, Name: opts.Name,
Size: opts.Size, Size: opts.Size,
VolumeType: opts.VolumeType, VolumeType: opts.VolumeType,
@ -160,7 +161,7 @@ func (volumes *VolumesV3) createVolume(opts VolumeCreateOpts) (string, string, e
Metadata: opts.Metadata, Metadata: opts.Metadata,
} }
vol, err := volumes_v3.Create(volumes.blockstorage, create_opts).Extract() vol, err := volumes_v3.Create(volumes.blockstorage, createOpts).Extract()
timeTaken := time.Since(startTime).Seconds() timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("create_v3_volume", timeTaken, err) recordOpenstackOperationMetric("create_v3_volume", timeTaken, err)
if err != nil { if err != nil {
@ -187,7 +188,7 @@ func (volumes *VolumesV1) getVolume(volumeID string) (Volume, error) {
} }
if len(volumeV1.Attachments) > 0 && volumeV1.Attachments[0]["server_id"] != nil { if len(volumeV1.Attachments) > 0 && volumeV1.Attachments[0]["server_id"] != nil {
volume.AttachedServerId = volumeV1.Attachments[0]["server_id"].(string) volume.AttachedServerID = volumeV1.Attachments[0]["server_id"].(string)
volume.AttachedDevice = volumeV1.Attachments[0]["device"].(string) volume.AttachedDevice = volumeV1.Attachments[0]["device"].(string)
} }
@ -212,7 +213,7 @@ func (volumes *VolumesV2) getVolume(volumeID string) (Volume, error) {
} }
if len(volumeV2.Attachments) > 0 { if len(volumeV2.Attachments) > 0 {
volume.AttachedServerId = volumeV2.Attachments[0].ServerID volume.AttachedServerID = volumeV2.Attachments[0].ServerID
volume.AttachedDevice = volumeV2.Attachments[0].Device volume.AttachedDevice = volumeV2.Attachments[0].Device
} }
@ -236,7 +237,7 @@ func (volumes *VolumesV3) getVolume(volumeID string) (Volume, error) {
} }
if len(volumeV3.Attachments) > 0 { if len(volumeV3.Attachments) > 0 {
volume.AttachedServerId = volumeV3.Attachments[0].ServerID volume.AttachedServerID = volumeV3.Attachments[0].ServerID
volume.AttachedDevice = volumeV3.Attachments[0].Device volume.AttachedDevice = volumeV3.Attachments[0].Device
} }
@ -269,10 +270,10 @@ func (volumes *VolumesV3) deleteVolume(volumeID string) error {
func (volumes *VolumesV1) expandVolume(volumeID string, newSize int) error { func (volumes *VolumesV1) expandVolume(volumeID string, newSize int) error {
startTime := time.Now() startTime := time.Now()
create_opts := volumeexpand.ExtendSizeOpts{ createOpts := volumeexpand.ExtendSizeOpts{
NewSize: newSize, NewSize: newSize,
} }
err := volumeexpand.ExtendSize(volumes.blockstorage, volumeID, create_opts).ExtractErr() err := volumeexpand.ExtendSize(volumes.blockstorage, volumeID, createOpts).ExtractErr()
timeTaken := time.Since(startTime).Seconds() timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("expand_volume", timeTaken, err) recordOpenstackOperationMetric("expand_volume", timeTaken, err)
return err return err
@ -280,10 +281,10 @@ func (volumes *VolumesV1) expandVolume(volumeID string, newSize int) error {
func (volumes *VolumesV2) expandVolume(volumeID string, newSize int) error { func (volumes *VolumesV2) expandVolume(volumeID string, newSize int) error {
startTime := time.Now() startTime := time.Now()
create_opts := volumeexpand.ExtendSizeOpts{ createOpts := volumeexpand.ExtendSizeOpts{
NewSize: newSize, NewSize: newSize,
} }
err := volumeexpand.ExtendSize(volumes.blockstorage, volumeID, create_opts).ExtractErr() err := volumeexpand.ExtendSize(volumes.blockstorage, volumeID, createOpts).ExtractErr()
timeTaken := time.Since(startTime).Seconds() timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("expand_volume", timeTaken, err) recordOpenstackOperationMetric("expand_volume", timeTaken, err)
return err return err
@ -291,25 +292,26 @@ func (volumes *VolumesV2) expandVolume(volumeID string, newSize int) error {
func (volumes *VolumesV3) expandVolume(volumeID string, newSize int) error { func (volumes *VolumesV3) expandVolume(volumeID string, newSize int) error {
startTime := time.Now() startTime := time.Now()
create_opts := volumeexpand.ExtendSizeOpts{ createOpts := volumeexpand.ExtendSizeOpts{
NewSize: newSize, NewSize: newSize,
} }
err := volumeexpand.ExtendSize(volumes.blockstorage, volumeID, create_opts).ExtractErr() err := volumeexpand.ExtendSize(volumes.blockstorage, volumeID, createOpts).ExtractErr()
timeTaken := time.Since(startTime).Seconds() timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("expand_volume", timeTaken, err) recordOpenstackOperationMetric("expand_volume", timeTaken, err)
return err return err
} }
// OperationPending checks if there is an operation pending on a volume
func (os *OpenStack) OperationPending(diskName string) (bool, string, error) { func (os *OpenStack) OperationPending(diskName string) (bool, string, error) {
volume, err := os.getVolume(diskName) volume, err := os.getVolume(diskName)
if err != nil { if err != nil {
return false, "", err return false, "", err
} }
volumeStatus := volume.Status volumeStatus := volume.Status
if volumeStatus == VolumeErrorStatus { if volumeStatus == volumeErrorStatus {
return false, volumeStatus, nil return false, volumeStatus, nil
} }
if volumeStatus == VolumeAvailableStatus || volumeStatus == VolumeInUseStatus || volumeStatus == VolumeDeletedStatus { if volumeStatus == volumeAvailableStatus || volumeStatus == volumeInUseStatus || volumeStatus == volumeDeletedStatus {
return false, volume.Status, nil return false, volume.Status, nil
} }
return true, volumeStatus, nil return true, volumeStatus, nil
@ -327,13 +329,13 @@ func (os *OpenStack) AttachDisk(instanceID, volumeID string) (string, error) {
return "", err return "", err
} }
if volume.AttachedServerId != "" { if volume.AttachedServerID != "" {
if instanceID == volume.AttachedServerId { if instanceID == volume.AttachedServerID {
glog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID) glog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID)
return volume.ID, nil return volume.ID, nil
} }
nodeName, err := os.GetNodeNameByID(volume.AttachedServerId) nodeName, err := os.GetNodeNameByID(volume.AttachedServerID)
attachErr := fmt.Sprintf("disk %s path %s is attached to a different instance (%s)", volumeID, volume.AttachedDevice, volume.AttachedServerId) attachErr := fmt.Sprintf("disk %s path %s is attached to a different instance (%s)", volumeID, volume.AttachedDevice, volume.AttachedServerID)
if err != nil { if err != nil {
glog.Error(attachErr) glog.Error(attachErr)
return "", errors.New(attachErr) return "", errors.New(attachErr)
@ -365,34 +367,34 @@ func (os *OpenStack) DetachDisk(instanceID, volumeID string) error {
if err != nil { if err != nil {
return err return err
} }
if volume.Status == VolumeAvailableStatus { if volume.Status == volumeAvailableStatus {
// "available" is fine since that means the volume is detached from instance already. // "available" is fine since that means the volume is detached from instance already.
glog.V(2).Infof("volume: %s has been detached from compute: %s ", volume.ID, instanceID) glog.V(2).Infof("volume: %s has been detached from compute: %s ", volume.ID, instanceID)
return nil return nil
} }
if volume.Status != VolumeInUseStatus { if volume.Status != volumeInUseStatus {
return fmt.Errorf("can not detach volume %s, its status is %s", volume.Name, volume.Status) return fmt.Errorf("can not detach volume %s, its status is %s", volume.Name, volume.Status)
} }
cClient, err := os.NewComputeV2() cClient, err := os.NewComputeV2()
if err != nil { if err != nil {
return err return err
} }
if volume.AttachedServerId != instanceID { if volume.AttachedServerID != instanceID {
return fmt.Errorf("disk: %s has no attachments or is not attached to compute: %s", volume.Name, instanceID) return fmt.Errorf("disk: %s has no attachments or is not attached to compute: %s", volume.Name, instanceID)
} else {
startTime := time.Now()
// This is a blocking call and effects kubelet's performance directly.
// We should consider kicking it out into a separate routine, if it is bad.
err = volumeattach.Delete(cClient, instanceID, volume.ID).ExtractErr()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("detach_disk", timeTaken, err)
if err != nil {
return fmt.Errorf("failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err)
}
glog.V(2).Infof("Successfully detached volume: %s from compute: %s", volume.ID, instanceID)
} }
startTime := time.Now()
// This is a blocking call and effects kubelet's performance directly.
// We should consider kicking it out into a separate routine, if it is bad.
err = volumeattach.Delete(cClient, instanceID, volume.ID).ExtractErr()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("detach_disk", timeTaken, err)
if err != nil {
return fmt.Errorf("failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err)
}
glog.V(2).Infof("Successfully detached volume: %s from compute: %s", volume.ID, instanceID)
return nil return nil
} }
@ -402,7 +404,7 @@ func (os *OpenStack) ExpandVolume(volumeID string, oldSize resource.Quantity, ne
if err != nil { if err != nil {
return oldSize, err return oldSize, err
} }
if volume.Status != VolumeAvailableStatus { if volume.Status != volumeAvailableStatus {
// cinder volume can not be expanded if its status is not available // cinder volume can not be expanded if its status is not available
return oldSize, fmt.Errorf("volume status is not available") return oldSize, fmt.Errorf("volume status is not available")
} }
@ -445,7 +447,7 @@ func (os *OpenStack) CreateVolume(name string, size int, vtype, availability str
return "", "", os.bsOpts.IgnoreVolumeAZ, fmt.Errorf("unable to initialize cinder client for region: %s, err: %v", os.region, err) return "", "", os.bsOpts.IgnoreVolumeAZ, fmt.Errorf("unable to initialize cinder client for region: %s, err: %v", os.region, err)
} }
opts := VolumeCreateOpts{ opts := volumeCreateOpts{
Name: name, Name: name,
Size: size, Size: size,
VolumeType: vtype, VolumeType: vtype,
@ -465,8 +467,8 @@ func (os *OpenStack) CreateVolume(name string, size int, vtype, availability str
return volumeID, volumeAZ, os.bsOpts.IgnoreVolumeAZ, nil return volumeID, volumeAZ, os.bsOpts.IgnoreVolumeAZ, nil
} }
// GetDevicePath returns the path of an attached block storage volume, specified by its id. // GetDevicePathBySerialID returns the path of an attached block storage volume, specified by its id.
func (os *OpenStack) GetDevicePathBySerialId(volumeID string) string { func (os *OpenStack) GetDevicePathBySerialID(volumeID string) string {
// Build a list of candidate device paths. // Build a list of candidate device paths.
// Certain Nova drivers will set the disk serial ID, including the Cinder volume id. // Certain Nova drivers will set the disk serial ID, including the Cinder volume id.
candidateDeviceNodes := []string{ candidateDeviceNodes := []string{
@ -493,7 +495,7 @@ func (os *OpenStack) GetDevicePathBySerialId(volumeID string) string {
return "" return ""
} }
func (os *OpenStack) GetDevicePathFromInstanceMetadata(volumeID string) string { func (os *OpenStack) getDevicePathFromInstanceMetadata(volumeID string) string {
// Nova Hyper-V hosts cannot override disk SCSI IDs. In order to locate // Nova Hyper-V hosts cannot override disk SCSI IDs. In order to locate
// volumes, we're querying the metadata service. Note that the Hyper-V // volumes, we're querying the metadata service. Note that the Hyper-V
// driver will include device metadata for untagged volumes as well. // driver will include device metadata for untagged volumes as well.
@ -501,7 +503,7 @@ func (os *OpenStack) GetDevicePathFromInstanceMetadata(volumeID string) string {
// We're avoiding using cached metadata (or the configdrive), // We're avoiding using cached metadata (or the configdrive),
// relying on the metadata service. // relying on the metadata service.
instanceMetadata, err := getMetadataFromMetadataService( instanceMetadata, err := getMetadataFromMetadataService(
NewtonMetadataVersion) newtonMetadataVersion)
if err != nil { if err != nil {
glog.V(4).Infof( glog.V(4).Infof(
@ -544,10 +546,10 @@ func (os *OpenStack) GetDevicePathFromInstanceMetadata(volumeID string) string {
// GetDevicePath returns the path of an attached block storage volume, specified by its id. // GetDevicePath returns the path of an attached block storage volume, specified by its id.
func (os *OpenStack) GetDevicePath(volumeID string) string { func (os *OpenStack) GetDevicePath(volumeID string) string {
devicePath := os.GetDevicePathBySerialId(volumeID) devicePath := os.GetDevicePathBySerialID(volumeID)
if devicePath == "" { if devicePath == "" {
devicePath = os.GetDevicePathFromInstanceMetadata(volumeID) devicePath = os.getDevicePathFromInstanceMetadata(volumeID)
} }
if devicePath == "" { if devicePath == "" {
@ -557,6 +559,7 @@ func (os *OpenStack) GetDevicePath(volumeID string) string {
return devicePath return devicePath
} }
// DeleteVolume deletes a volume given volume name.
func (os *OpenStack) DeleteVolume(volumeID string) error { func (os *OpenStack) DeleteVolume(volumeID string) error {
used, err := os.diskIsUsed(volumeID) used, err := os.diskIsUsed(volumeID)
if err != nil { if err != nil {
@ -585,17 +588,16 @@ func (os *OpenStack) GetAttachmentDiskPath(instanceID, volumeID string) (string,
if err != nil { if err != nil {
return "", err return "", err
} }
if volume.Status != VolumeInUseStatus { if volume.Status != volumeInUseStatus {
return "", fmt.Errorf("can not get device path of volume %s, its status is %s ", volume.Name, volume.Status) return "", fmt.Errorf("can not get device path of volume %s, its status is %s ", volume.Name, volume.Status)
} }
if volume.AttachedServerId != "" { if volume.AttachedServerID != "" {
if instanceID == volume.AttachedServerId { if instanceID == volume.AttachedServerID {
// Attachment[0]["device"] points to the device path // Attachment[0]["device"] points to the device path
// see http://developer.openstack.org/api-ref-blockstorage-v1.html // see http://developer.openstack.org/api-ref-blockstorage-v1.html
return volume.AttachedDevice, nil return volume.AttachedDevice, nil
} else {
return "", fmt.Errorf("disk %q is attached to a different compute: %q, should be detached before proceeding", volumeID, volume.AttachedServerId)
} }
return "", fmt.Errorf("disk %q is attached to a different compute: %q, should be detached before proceeding", volumeID, volume.AttachedServerID)
} }
return "", fmt.Errorf("volume %s has no ServerId", volumeID) return "", fmt.Errorf("volume %s has no ServerId", volumeID)
} }
@ -610,7 +612,7 @@ func (os *OpenStack) DiskIsAttached(instanceID, volumeID string) (bool, error) {
return false, err return false, err
} }
return instanceID == volume.AttachedServerId, nil return instanceID == volume.AttachedServerID, nil
} }
// DiskIsAttachedByName queries if a volume is attached to a compute instance by name // DiskIsAttachedByName queries if a volume is attached to a compute instance by name
@ -624,9 +626,8 @@ func (os *OpenStack) DiskIsAttachedByName(nodeName types.NodeName, volumeID stri
if err == ErrNotFound { if err == ErrNotFound {
// instance not found anymore in cloudprovider, assume that cinder is detached // instance not found anymore in cloudprovider, assume that cinder is detached
return false, "", nil return false, "", nil
} else {
return false, "", err
} }
return false, "", err
} }
instanceID := "/" + srv.ID instanceID := "/" + srv.ID
if ind := strings.LastIndex(instanceID, "/"); ind >= 0 { if ind := strings.LastIndex(instanceID, "/"); ind >= 0 {
@ -665,9 +666,8 @@ func (os *OpenStack) DisksAreAttachedByName(nodeName types.NodeName, volumeIDs [
attached[volumeID] = false attached[volumeID] = false
} }
return attached, nil return attached, nil
} else {
return attached, err
} }
return attached, err
} }
instanceID := "/" + srv.ID instanceID := "/" + srv.ID
if ind := strings.LastIndex(instanceID, "/"); ind >= 0 { if ind := strings.LastIndex(instanceID, "/"); ind >= 0 {
@ -682,7 +682,7 @@ func (os *OpenStack) diskIsUsed(volumeID string) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return volume.AttachedServerId != "", nil return volume.AttachedServerID != "", nil
} }
// ShouldTrustDevicePath queries if we should trust the cinder provide deviceName, See issue #33128 // ShouldTrustDevicePath queries if we should trust the cinder provide deviceName, See issue #33128
@ -715,8 +715,8 @@ func (os *OpenStack) GetLabelsForVolume(pv *v1.PersistentVolume) (map[string]str
// recordOpenstackOperationMetric records openstack operation metrics // recordOpenstackOperationMetric records openstack operation metrics
func recordOpenstackOperationMetric(operation string, timeTaken float64, err error) { func recordOpenstackOperationMetric(operation string, timeTaken float64, err error) {
if err != nil { if err != nil {
OpenstackApiRequestErrors.With(prometheus.Labels{"request": operation}).Inc() openstackAPIRequestErrors.With(prometheus.Labels{"request": operation}).Inc()
} else { } else {
OpenstackOperationsLatency.With(prometheus.Labels{"request": operation}).Observe(timeTaken) openstackOperationsLatency.With(prometheus.Labels{"request": operation}).Observe(timeTaken)
} }
} }

View File

@ -35,7 +35,7 @@ import (
type cinderDiskAttacher struct { type cinderDiskAttacher struct {
host volume.VolumeHost host volume.VolumeHost
cinderProvider CinderProvider cinderProvider BlockStorageProvider
} }
var _ volume.Attacher = &cinderDiskAttacher{} var _ volume.Attacher = &cinderDiskAttacher{}
@ -215,7 +215,7 @@ func (attacher *cinderDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath
volumeID := volumeSource.VolumeID volumeID := volumeSource.VolumeID
if devicePath == "" { if devicePath == "" {
return "", fmt.Errorf("WaitForAttach failed for Cinder disk %q: devicePath is empty.", volumeID) return "", fmt.Errorf("WaitForAttach failed for Cinder disk %q: devicePath is empty", volumeID)
} }
ticker := time.NewTicker(probeVolumeInitDelay) ticker := time.NewTicker(probeVolumeInitDelay)
@ -237,16 +237,15 @@ func (attacher *cinderDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath
if exists && err == nil { if exists && err == nil {
glog.Infof("Successfully found attached Cinder disk %q at %v.", volumeID, devicePath) glog.Infof("Successfully found attached Cinder disk %q at %v.", volumeID, devicePath)
return devicePath, nil return devicePath, nil
} else {
// Log an error, and continue checking periodically
glog.Errorf("Error: could not find attached Cinder disk %q (path: %q): %v", volumeID, devicePath, err)
// Using exponential backoff instead of linear
ticker.Stop()
duration = time.Duration(float64(duration) * probeVolumeFactor)
ticker = time.NewTicker(duration)
} }
// Log an error, and continue checking periodically
glog.Errorf("Error: could not find attached Cinder disk %q (path: %q): %v", volumeID, devicePath, err)
// Using exponential backoff instead of linear
ticker.Stop()
duration = time.Duration(float64(duration) * probeVolumeFactor)
ticker = time.NewTicker(duration)
case <-timer.C: case <-timer.C:
return "", fmt.Errorf("Could not find attached Cinder disk %q. Timeout waiting for mount paths to be created.", volumeID) return "", fmt.Errorf("could not find attached Cinder disk %q. Timeout waiting for mount paths to be created", volumeID)
} }
} }
} }
@ -299,7 +298,7 @@ func (attacher *cinderDiskAttacher) MountDevice(spec *volume.Spec, devicePath st
type cinderDiskDetacher struct { type cinderDiskDetacher struct {
mounter mount.Interface mounter mount.Interface
cinderProvider CinderProvider cinderProvider BlockStorageProvider
} }
var _ volume.Detacher = &cinderDiskDetacher{} var _ volume.Detacher = &cinderDiskDetacher{}

View File

@ -453,18 +453,18 @@ func (testcase *testcase) AttachDisk(instanceID, volumeID string) (string, error
if expected.volumeID == "" && expected.instanceID == "" { if expected.volumeID == "" && expected.instanceID == "" {
// testcase.attach looks uninitialized, test did not expect to call // testcase.attach looks uninitialized, test did not expect to call
// AttachDisk // AttachDisk
testcase.t.Errorf("Unexpected AttachDisk call!") testcase.t.Errorf("unexpected AttachDisk call")
return "", errors.New("Unexpected AttachDisk call!") return "", errors.New("unexpected AttachDisk call")
} }
if expected.volumeID != volumeID { if expected.volumeID != volumeID {
testcase.t.Errorf("Unexpected AttachDisk call: expected volumeID %s, got %s", expected.volumeID, volumeID) testcase.t.Errorf("unexpected AttachDisk call: expected volumeID %s, got %s", expected.volumeID, volumeID)
return "", errors.New("Unexpected AttachDisk call: wrong volumeID") return "", errors.New("unexpected AttachDisk call: wrong volumeID")
} }
if expected.instanceID != instanceID { if expected.instanceID != instanceID {
testcase.t.Errorf("Unexpected AttachDisk call: expected instanceID %s, got %s", expected.instanceID, instanceID) testcase.t.Errorf("unexpected AttachDisk call: expected instanceID %s, got %s", expected.instanceID, instanceID)
return "", errors.New("Unexpected AttachDisk call: wrong instanceID") return "", errors.New("unexpected AttachDisk call: wrong instanceID")
} }
glog.V(4).Infof("AttachDisk call: %s, %s, returning %q, %v", volumeID, instanceID, expected.retDeviceName, expected.ret) glog.V(4).Infof("AttachDisk call: %s, %s, returning %q, %v", volumeID, instanceID, expected.retDeviceName, expected.ret)
@ -479,18 +479,18 @@ func (testcase *testcase) DetachDisk(instanceID, volumeID string) error {
if expected.devicePath == "" && expected.instanceID == "" { if expected.devicePath == "" && expected.instanceID == "" {
// testcase.detach looks uninitialized, test did not expect to call // testcase.detach looks uninitialized, test did not expect to call
// DetachDisk // DetachDisk
testcase.t.Errorf("Unexpected DetachDisk call!") testcase.t.Errorf("unexpected DetachDisk call")
return errors.New("Unexpected DetachDisk call!") return errors.New("unexpected DetachDisk call")
} }
if expected.devicePath != volumeID { if expected.devicePath != volumeID {
testcase.t.Errorf("Unexpected DetachDisk call: expected volumeID %s, got %s", expected.devicePath, volumeID) testcase.t.Errorf("unexpected DetachDisk call: expected volumeID %s, got %s", expected.devicePath, volumeID)
return errors.New("Unexpected DetachDisk call: wrong volumeID") return errors.New("unexpected DetachDisk call: wrong volumeID")
} }
if expected.instanceID != instanceID { if expected.instanceID != instanceID {
testcase.t.Errorf("Unexpected DetachDisk call: expected instanceID %s, got %s", expected.instanceID, instanceID) testcase.t.Errorf("unexpected DetachDisk call: expected instanceID %s, got %s", expected.instanceID, instanceID)
return errors.New("Unexpected DetachDisk call: wrong instanceID") return errors.New("unexpected DetachDisk call: wrong instanceID")
} }
glog.V(4).Infof("DetachDisk call: %s, %s, returning %v", volumeID, instanceID, expected.ret) glog.V(4).Infof("DetachDisk call: %s, %s, returning %v", volumeID, instanceID, expected.ret)
@ -527,18 +527,18 @@ func (testcase *testcase) DiskIsAttached(instanceID, volumeID string) (bool, err
if expected.volumeID == "" && expected.instanceID == "" { if expected.volumeID == "" && expected.instanceID == "" {
// testcase.diskIsAttached looks uninitialized, test did not expect to // testcase.diskIsAttached looks uninitialized, test did not expect to
// call DiskIsAttached // call DiskIsAttached
testcase.t.Errorf("Unexpected DiskIsAttached call!") testcase.t.Errorf("unexpected DiskIsAttached call")
return false, errors.New("Unexpected DiskIsAttached call!") return false, errors.New("unexpected DiskIsAttached call")
} }
if expected.volumeID != volumeID { if expected.volumeID != volumeID {
testcase.t.Errorf("Unexpected DiskIsAttached call: expected volumeID %s, got %s", expected.volumeID, volumeID) testcase.t.Errorf("unexpected DiskIsAttached call: expected volumeID %s, got %s", expected.volumeID, volumeID)
return false, errors.New("Unexpected DiskIsAttached call: wrong volumeID") return false, errors.New("unexpected DiskIsAttached call: wrong volumeID")
} }
if expected.instanceID != instanceID { if expected.instanceID != instanceID {
testcase.t.Errorf("Unexpected DiskIsAttached call: expected instanceID %s, got %s", expected.instanceID, instanceID) testcase.t.Errorf("unexpected DiskIsAttached call: expected instanceID %s, got %s", expected.instanceID, instanceID)
return false, errors.New("Unexpected DiskIsAttached call: wrong instanceID") return false, errors.New("unexpected DiskIsAttached call: wrong instanceID")
} }
glog.V(4).Infof("DiskIsAttached call: %s, %s, returning %v, %v", volumeID, instanceID, expected.isAttached, expected.ret) glog.V(4).Infof("DiskIsAttached call: %s, %s, returning %v, %v", volumeID, instanceID, expected.isAttached, expected.ret)
@ -551,18 +551,18 @@ func (testcase *testcase) GetAttachmentDiskPath(instanceID, volumeID string) (st
if expected.volumeID == "" && expected.instanceID == "" { if expected.volumeID == "" && expected.instanceID == "" {
// testcase.diskPath looks uninitialized, test did not expect to // testcase.diskPath looks uninitialized, test did not expect to
// call GetAttachmentDiskPath // call GetAttachmentDiskPath
testcase.t.Errorf("Unexpected GetAttachmentDiskPath call!") testcase.t.Errorf("unexpected GetAttachmentDiskPath call")
return "", errors.New("Unexpected GetAttachmentDiskPath call!") return "", errors.New("unexpected GetAttachmentDiskPath call")
} }
if expected.volumeID != volumeID { if expected.volumeID != volumeID {
testcase.t.Errorf("Unexpected GetAttachmentDiskPath call: expected volumeID %s, got %s", expected.volumeID, volumeID) testcase.t.Errorf("unexpected GetAttachmentDiskPath call: expected volumeID %s, got %s", expected.volumeID, volumeID)
return "", errors.New("Unexpected GetAttachmentDiskPath call: wrong volumeID") return "", errors.New("unexpected GetAttachmentDiskPath call: wrong volumeID")
} }
if expected.instanceID != instanceID { if expected.instanceID != instanceID {
testcase.t.Errorf("Unexpected GetAttachmentDiskPath call: expected instanceID %s, got %s", expected.instanceID, instanceID) testcase.t.Errorf("unexpected GetAttachmentDiskPath call: expected instanceID %s, got %s", expected.instanceID, instanceID)
return "", errors.New("Unexpected GetAttachmentDiskPath call: wrong instanceID") return "", errors.New("unexpected GetAttachmentDiskPath call: wrong instanceID")
} }
glog.V(4).Infof("GetAttachmentDiskPath call: %s, %s, returning %v, %v", volumeID, instanceID, expected.retPath, expected.ret) glog.V(4).Infof("GetAttachmentDiskPath call: %s, %s, returning %v, %v", volumeID, instanceID, expected.retPath, expected.ret)
@ -588,25 +588,25 @@ func (testcase *testcase) DiskIsAttachedByName(nodeName types.NodeName, volumeID
} }
if expected.nodeName != nodeName { if expected.nodeName != nodeName {
testcase.t.Errorf("Unexpected DiskIsAttachedByName call: expected nodename %s, got %s", expected.nodeName, nodeName) testcase.t.Errorf("unexpected DiskIsAttachedByName call: expected nodename %s, got %s", expected.nodeName, nodeName)
return false, instanceID, errors.New("Unexpected DiskIsAttachedByName call: wrong nodename") return false, instanceID, errors.New("unexpected DiskIsAttachedByName call: wrong nodename")
} }
if expected.volumeID == "" && expected.instanceID == "" { if expected.volumeID == "" && expected.instanceID == "" {
// testcase.diskIsAttached looks uninitialized, test did not expect to // testcase.diskIsAttached looks uninitialized, test did not expect to
// call DiskIsAttached // call DiskIsAttached
testcase.t.Errorf("Unexpected DiskIsAttachedByName call!") testcase.t.Errorf("unexpected DiskIsAttachedByName call")
return false, instanceID, errors.New("Unexpected DiskIsAttachedByName call!") return false, instanceID, errors.New("unexpected DiskIsAttachedByName call")
} }
if expected.volumeID != volumeID { if expected.volumeID != volumeID {
testcase.t.Errorf("Unexpected DiskIsAttachedByName call: expected volumeID %s, got %s", expected.volumeID, volumeID) testcase.t.Errorf("unexpected DiskIsAttachedByName call: expected volumeID %s, got %s", expected.volumeID, volumeID)
return false, instanceID, errors.New("Unexpected DiskIsAttachedByName call: wrong volumeID") return false, instanceID, errors.New("unexpected DiskIsAttachedByName call: wrong volumeID")
} }
if expected.instanceID != instanceID { if expected.instanceID != instanceID {
testcase.t.Errorf("Unexpected DiskIsAttachedByName call: expected instanceID %s, got %s", expected.instanceID, instanceID) testcase.t.Errorf("unexpected DiskIsAttachedByName call: expected instanceID %s, got %s", expected.instanceID, instanceID)
return false, instanceID, errors.New("Unexpected DiskIsAttachedByName call: wrong instanceID") return false, instanceID, errors.New("unexpected DiskIsAttachedByName call: wrong instanceID")
} }
glog.V(4).Infof("DiskIsAttachedByName call: %s, %s, returning %v, %v", volumeID, nodeName, expected.isAttached, expected.instanceID, expected.ret) glog.V(4).Infof("DiskIsAttachedByName call: %s, %s, returning %v, %v", volumeID, nodeName, expected.isAttached, expected.instanceID, expected.ret)

View File

@ -38,15 +38,17 @@ import (
) )
const ( const (
// DefaultCloudConfigPath is the default path for cloud configuration
DefaultCloudConfigPath = "/etc/kubernetes/cloud-config" DefaultCloudConfigPath = "/etc/kubernetes/cloud-config"
) )
// This is the primary entrypoint for volume plugins. // ProbeVolumePlugins is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin { func ProbeVolumePlugins() []volume.VolumePlugin {
return []volume.VolumePlugin{&cinderPlugin{}} return []volume.VolumePlugin{&cinderPlugin{}}
} }
type CinderProvider interface { // BlockStorageProvider is the interface for accessing cinder functionality.
type BlockStorageProvider interface {
AttachDisk(instanceID, volumeID string) (string, error) AttachDisk(instanceID, volumeID string) (string, error)
DetachDisk(instanceID, volumeID string) error DetachDisk(instanceID, volumeID string) error
DeleteVolume(volumeID string) error DeleteVolume(volumeID string) error
@ -120,7 +122,7 @@ func (plugin *cinderPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
} }
func (plugin *cinderPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { func (plugin *cinderPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
return plugin.newMounterInternal(spec, pod.UID, &CinderDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName())) return plugin.newMounterInternal(spec, pod.UID, &DiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
} }
func (plugin *cinderPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager cdManager, mounter mount.Interface) (volume.Mounter, error) { func (plugin *cinderPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager cdManager, mounter mount.Interface) (volume.Mounter, error) {
@ -147,7 +149,7 @@ func (plugin *cinderPlugin) newMounterInternal(spec *volume.Spec, podUID types.U
} }
func (plugin *cinderPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { func (plugin *cinderPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
return plugin.newUnmounterInternal(volName, podUID, &CinderDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName())) return plugin.newUnmounterInternal(volName, podUID, &DiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
} }
func (plugin *cinderPlugin) newUnmounterInternal(volName string, podUID types.UID, manager cdManager, mounter mount.Interface) (volume.Unmounter, error) { func (plugin *cinderPlugin) newUnmounterInternal(volName string, podUID types.UID, manager cdManager, mounter mount.Interface) (volume.Unmounter, error) {
@ -162,7 +164,7 @@ func (plugin *cinderPlugin) newUnmounterInternal(volName string, podUID types.UI
} }
func (plugin *cinderPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) { func (plugin *cinderPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
return plugin.newDeleterInternal(spec, &CinderDiskUtil{}) return plugin.newDeleterInternal(spec, &DiskUtil{})
} }
func (plugin *cinderPlugin) newDeleterInternal(spec *volume.Spec, manager cdManager) (volume.Deleter, error) { func (plugin *cinderPlugin) newDeleterInternal(spec *volume.Spec, manager cdManager) (volume.Deleter, error) {
@ -179,7 +181,7 @@ func (plugin *cinderPlugin) newDeleterInternal(spec *volume.Spec, manager cdMana
} }
func (plugin *cinderPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) { func (plugin *cinderPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
return plugin.newProvisionerInternal(options, &CinderDiskUtil{}) return plugin.newProvisionerInternal(options, &DiskUtil{})
} }
func (plugin *cinderPlugin) newProvisionerInternal(options volume.VolumeOptions, manager cdManager) (volume.Provisioner, error) { func (plugin *cinderPlugin) newProvisionerInternal(options volume.VolumeOptions, manager cdManager) (volume.Provisioner, error) {
@ -192,23 +194,22 @@ func (plugin *cinderPlugin) newProvisionerInternal(options volume.VolumeOptions,
}, nil }, nil
} }
func (plugin *cinderPlugin) getCloudProvider() (CinderProvider, error) { func (plugin *cinderPlugin) getCloudProvider() (BlockStorageProvider, error) {
cloud := plugin.host.GetCloudProvider() cloud := plugin.host.GetCloudProvider()
if cloud == nil { if cloud == nil {
if _, err := os.Stat(DefaultCloudConfigPath); err == nil { if _, err := os.Stat(DefaultCloudConfigPath); err == nil {
var config *os.File var config *os.File
config, err = os.Open(DefaultCloudConfigPath) config, err = os.Open(DefaultCloudConfigPath)
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("unable to load OpenStack configuration from default path : %v", err)) return nil, fmt.Errorf("unable to load OpenStack configuration from default path : %v", err)
} else { }
defer config.Close() defer config.Close()
cloud, err = cloudprovider.GetCloudProvider(openstack.ProviderName, config) cloud, err = cloudprovider.GetCloudProvider(openstack.ProviderName, config)
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("unable to create OpenStack cloud provider from default path : %v", err)) return nil, fmt.Errorf("unable to create OpenStack cloud provider from default path : %v", err)
}
} }
} else { } else {
return nil, errors.New(fmt.Sprintf("OpenStack cloud provider was not initialized properly : %v", err)) return nil, fmt.Errorf("OpenStack cloud provider was not initialized properly : %v", err)
} }
} }

View File

@ -35,11 +35,12 @@ import (
"k8s.io/utils/exec" "k8s.io/utils/exec"
) )
type CinderDiskUtil struct{} // DiskUtil has utility/helper methods
type DiskUtil struct{}
// Attaches a disk specified by a volume.CinderPersistenDisk to the current kubelet. // AttachDisk attaches a disk specified by a volume.CinderPersistenDisk to the current kubelet.
// Mounts the disk to its global path. // Mounts the disk to its global path.
func (util *CinderDiskUtil) AttachDisk(b *cinderVolumeMounter, globalPDPath string) error { func (util *DiskUtil) AttachDisk(b *cinderVolumeMounter, globalPDPath string) error {
options := []string{} options := []string{}
if b.readOnly { if b.readOnly {
options = append(options, "ro") options = append(options, "ro")
@ -98,8 +99,8 @@ func (util *CinderDiskUtil) AttachDisk(b *cinderVolumeMounter, globalPDPath stri
return nil return nil
} }
// Unmounts the device and detaches the disk from the kubelet's host machine. // DetachDisk unmounts the device and detaches the disk from the kubelet's host machine.
func (util *CinderDiskUtil) DetachDisk(cd *cinderVolumeUnmounter) error { func (util *DiskUtil) DetachDisk(cd *cinderVolumeUnmounter) error {
globalPDPath := makeGlobalPDName(cd.plugin.host, cd.pdName) globalPDPath := makeGlobalPDName(cd.plugin.host, cd.pdName)
if err := cd.mounter.Unmount(globalPDPath); err != nil { if err := cd.mounter.Unmount(globalPDPath); err != nil {
return err return err
@ -124,7 +125,8 @@ func (util *CinderDiskUtil) DetachDisk(cd *cinderVolumeUnmounter) error {
return nil return nil
} }
func (util *CinderDiskUtil) DeleteVolume(cd *cinderVolumeDeleter) error { // DeleteVolume uses the cloud entrypoint to delete specified volume
func (util *DiskUtil) DeleteVolume(cd *cinderVolumeDeleter) error {
cloud, err := cd.plugin.getCloudProvider() cloud, err := cd.plugin.getCloudProvider()
if err != nil { if err != nil {
return err return err
@ -158,7 +160,8 @@ func getZonesFromNodes(kubeClient clientset.Interface) (sets.String, error) {
return zones, nil return zones, nil
} }
func (util *CinderDiskUtil) CreateVolume(c *cinderVolumeProvisioner) (volumeID string, volumeSizeGB int, volumeLabels map[string]string, fstype string, err error) { // CreateVolume uses the cloud provider entrypoint for creating a volume
func (util *DiskUtil) CreateVolume(c *cinderVolumeProvisioner) (volumeID string, volumeSizeGB int, volumeLabels map[string]string, fstype string, err error) {
cloud, err := c.plugin.getCloudProvider() cloud, err := c.plugin.getCloudProvider()
if err != nil { if err != nil {
return "", 0, nil, "", err return "", 0, nil, "", err
@ -247,10 +250,10 @@ func probeAttachedVolume() error {
} }
func scsiHostRescan() { func scsiHostRescan() {
scsi_path := "/sys/class/scsi_host/" scsiPath := "/sys/class/scsi_host/"
if dirs, err := ioutil.ReadDir(scsi_path); err == nil { if dirs, err := ioutil.ReadDir(scsiPath); err == nil {
for _, f := range dirs { for _, f := range dirs {
name := scsi_path + f.Name() + "/scan" name := scsiPath + f.Name() + "/scan"
data := []byte("- - -") data := []byte("- - -")
ioutil.WriteFile(name, data, 0666) ioutil.WriteFile(name, data, 0666)
} }