Add metrics support for a few network based volumes.

Signed-off-by: Vishnu Kannan <vishnuk@google.com>
pull/6/head
Vishnu Kannan 2016-05-18 18:13:04 -07:00
parent efc5bbc9e8
commit baa8ac4d6b
10 changed files with 163 additions and 122 deletions

View File

@ -30,7 +30,7 @@ import (
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/mount"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
kstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
)
@ -52,6 +52,10 @@ const (
awsElasticBlockStorePluginName = "kubernetes.io/aws-ebs"
)
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(awsElasticBlockStorePluginName), volName)
}
func (plugin *awsElasticBlockStorePlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
@ -99,13 +103,14 @@ func (plugin *awsElasticBlockStorePlugin) newMounterInternal(spec *volume.Spec,
return &awsElasticBlockStoreMounter{
awsElasticBlockStore: &awsElasticBlockStore{
podUID: podUID,
volName: spec.Name(),
volumeID: volumeID,
partition: partition,
manager: manager,
mounter: mounter,
plugin: plugin,
podUID: podUID,
volName: spec.Name(),
volumeID: volumeID,
partition: partition,
manager: manager,
mounter: mounter,
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, spec.Name(), plugin.host)),
},
fsType: fsType,
readOnly: readOnly,
@ -119,11 +124,12 @@ func (plugin *awsElasticBlockStorePlugin) NewUnmounter(volName string, podUID ty
func (plugin *awsElasticBlockStorePlugin) newUnmounterInternal(volName string, podUID types.UID, manager ebsManager, mounter mount.Interface) (volume.Unmounter, error) {
return &awsElasticBlockStoreUnmounter{&awsElasticBlockStore{
podUID: podUID,
volName: volName,
manager: manager,
mounter: mounter,
plugin: plugin,
podUID: podUID,
volName: volName,
manager: manager,
mounter: mounter,
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
}}, nil
}
@ -187,7 +193,7 @@ type awsElasticBlockStore struct {
// Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface
plugin *awsElasticBlockStorePlugin
volume.MetricsNil
volume.MetricsProvider
}
func detachDiskLogError(ebs *awsElasticBlockStore) {
@ -313,8 +319,7 @@ func getVolumeIDFromGlobalMount(host volume.VolumeHost, globalPath string) (stri
}
func (ebs *awsElasticBlockStore) GetPath() string {
name := awsElasticBlockStorePluginName
return ebs.plugin.host.GetPodVolumeDir(ebs.podUID, utilstrings.EscapeQualifiedNameForDisk(name), ebs.volName)
return getPath(ebs.podUID, ebs.volName, ebs.plugin.host)
}
type awsElasticBlockStoreUnmounter struct {
@ -392,8 +397,7 @@ type awsElasticBlockStoreDeleter struct {
var _ volume.Deleter = &awsElasticBlockStoreDeleter{}
func (d *awsElasticBlockStoreDeleter) GetPath() string {
name := awsElasticBlockStorePluginName
return d.plugin.host.GetPodVolumeDir(d.podUID, utilstrings.EscapeQualifiedNameForDisk(name), d.volName)
return getPath(d.podUID, d.volName, d.plugin.host)
}
func (d *awsElasticBlockStoreDeleter) Delete() error {

View File

@ -23,7 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/strings"
kstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"github.com/golang/glog"
@ -45,6 +45,10 @@ const (
azureFilePluginName = "kubernetes.io/azure-file"
)
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(azureFilePluginName), volName)
}
func (plugin *azureFilePlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
@ -84,10 +88,11 @@ func (plugin *azureFilePlugin) newMounterInternal(spec *volume.Spec, pod *api.Po
}
return &azureFileMounter{
azureFile: &azureFile{
volName: spec.Name(),
mounter: mounter,
pod: pod,
plugin: plugin,
volName: spec.Name(),
mounter: mounter,
pod: pod,
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
},
util: util,
secretName: source.SecretName,
@ -102,10 +107,11 @@ func (plugin *azureFilePlugin) NewUnmounter(volName string, podUID types.UID) (v
func (plugin *azureFilePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
return &azureFileUnmounter{&azureFile{
volName: volName,
mounter: mounter,
pod: &api.Pod{ObjectMeta: api.ObjectMeta{UID: podUID}},
plugin: plugin,
volName: volName,
mounter: mounter,
pod: &api.Pod{ObjectMeta: api.ObjectMeta{UID: podUID}},
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
}}, nil
}
@ -115,12 +121,11 @@ type azureFile struct {
pod *api.Pod
mounter mount.Interface
plugin *azureFilePlugin
volume.MetricsNil
volume.MetricsProvider
}
func (azureFileVolume *azureFile) GetPath() string {
name := azureFilePluginName
return azureFileVolume.plugin.host.GetPodVolumeDir(azureFileVolume.pod.UID, strings.EscapeQualifiedNameForDisk(name), azureFileVolume.volName)
return getPath(azureFileVolume.pod.UID, azureFileVolume.volName, azureFileVolume.plugin.host)
}
type azureFileMounter struct {

View File

@ -54,6 +54,10 @@ const (
emptyDirPluginName = "kubernetes.io/empty-dir"
)
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(emptyDirPluginName), volName)
}
func (plugin *emptyDirPlugin) Init(host volume.VolumeHost) error {
plugin.host = host
@ -88,7 +92,7 @@ func (plugin *emptyDirPlugin) newMounterInternal(spec *volume.Spec, pod *api.Pod
mountDetector: mountDetector,
plugin: plugin,
rootContext: opts.RootContext,
MetricsProvider: volume.NewMetricsDu(GetPath(pod.UID, spec.Name(), plugin.host)),
MetricsProvider: volume.NewMetricsDu(getPath(pod.UID, spec.Name(), plugin.host)),
}, nil
}
@ -105,7 +109,7 @@ func (plugin *emptyDirPlugin) newUnmounterInternal(volName string, podUID types.
mounter: mounter,
mountDetector: mountDetector,
plugin: plugin,
MetricsProvider: volume.NewMetricsDu(GetPath(podUID, volName, plugin.host)),
MetricsProvider: volume.NewMetricsDu(getPath(podUID, volName, plugin.host)),
}
return ed, nil
}
@ -271,12 +275,7 @@ func (ed *emptyDir) setupDir(dir string) error {
}
func (ed *emptyDir) GetPath() string {
return GetPath(ed.pod.UID, ed.volName, ed.plugin.host)
}
func GetPath(uid types.UID, volName string, host volume.VolumeHost) string {
name := emptyDirPluginName
return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(name), volName)
return getPath(ed.pod.UID, ed.volName, ed.plugin.host)
}
// TearDown simply discards everything in the directory.

View File

@ -49,6 +49,10 @@ const (
gcePersistentDiskPluginName = "kubernetes.io/gce-pd"
)
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(gcePersistentDiskPluginName), volName)
}
func (plugin *gcePersistentDiskPlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
@ -103,13 +107,14 @@ func (plugin *gcePersistentDiskPlugin) newMounterInternal(spec *volume.Spec, pod
return &gcePersistentDiskMounter{
gcePersistentDisk: &gcePersistentDisk{
podUID: podUID,
volName: spec.Name(),
pdName: pdName,
partition: partition,
mounter: mounter,
manager: manager,
plugin: plugin,
podUID: podUID,
volName: spec.Name(),
pdName: pdName,
partition: partition,
mounter: mounter,
manager: manager,
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, spec.Name(), plugin.host)),
},
readOnly: readOnly}, nil
}
@ -121,11 +126,12 @@ func (plugin *gcePersistentDiskPlugin) NewUnmounter(volName string, podUID types
func (plugin *gcePersistentDiskPlugin) newUnmounterInternal(volName string, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Unmounter, error) {
return &gcePersistentDiskUnmounter{&gcePersistentDisk{
podUID: podUID,
volName: volName,
manager: manager,
mounter: mounter,
plugin: plugin,
podUID: podUID,
volName: volName,
manager: manager,
mounter: mounter,
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
}}, nil
}
@ -185,7 +191,7 @@ type gcePersistentDisk struct {
// Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface
plugin *gcePersistentDiskPlugin
volume.MetricsNil
volume.MetricsProvider
}
type gcePersistentDiskMounter struct {
@ -270,9 +276,8 @@ func makeGlobalPDName(host volume.VolumeHost, devName string) string {
return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), "mounts", devName)
}
func (pd *gcePersistentDisk) GetPath() string {
name := gcePersistentDiskPluginName
return pd.plugin.host.GetPodVolumeDir(pd.podUID, strings.EscapeQualifiedNameForDisk(name), pd.volName)
func (b *gcePersistentDiskMounter) GetPath() string {
return getPath(b.podUID, b.volName, b.plugin.host)
}
type gcePersistentDiskUnmounter struct {
@ -281,7 +286,12 @@ type gcePersistentDiskUnmounter struct {
var _ volume.Unmounter = &gcePersistentDiskUnmounter{}
// TearDown unmounts the bind mount
func (c *gcePersistentDiskUnmounter) GetPath() string {
return getPath(c.podUID, c.volName, c.plugin.host)
}
// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (c *gcePersistentDiskUnmounter) TearDown() error {
return c.TearDownAt(c.GetPath())
}
@ -316,8 +326,7 @@ type gcePersistentDiskDeleter struct {
var _ volume.Deleter = &gcePersistentDiskDeleter{}
func (d *gcePersistentDiskDeleter) GetPath() string {
name := gcePersistentDiskPluginName
return d.plugin.host.GetPodVolumeDir(d.podUID, strings.EscapeQualifiedNameForDisk(name), d.volName)
return getPath(d.podUID, d.volName, d.plugin.host)
}
func (d *gcePersistentDiskDeleter) Delete() error {

View File

@ -72,7 +72,7 @@ func (md *metricsDu) runDu(metrics *Metrics) error {
// getFsInfo writes metrics.Capacity and metrics.Available from the filesystem info
func (md *metricsDu) getFsInfo(metrics *Metrics) error {
available, capacity, err := util.FsInfo(md.path)
available, capacity, _, err := util.FsInfo(md.path)
if err != nil {
return fmt.Errorf("Failed to get FsInfo due to error %v", err)
}

View File

@ -0,0 +1,68 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package volume
import (
"errors"
"fmt"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/volume/util"
)
var _ MetricsProvider = &metricsStatFS{}
// metricsStatFS represents a MetricsProvider that calculates the used and available
// Volume space by stat'ing and gathering filesystem info for the Volume path.
type metricsStatFS struct {
// the directory path the volume is mounted to.
path string
}
// NewMetricsStatfs creates a new metricsStatFS with the Volume path.
func NewMetricsStatFS(path string) MetricsProvider {
return &metricsStatFS{path}
}
// See MetricsProvider.GetMetrics
// GetMetrics calculates the volume usage and device free space by executing "du"
// and gathering filesystem info for the Volume path.
func (md *metricsStatFS) GetMetrics() (*Metrics, error) {
metrics := &Metrics{}
if md.path == "" {
return metrics, errors.New("no path defined for disk usage metrics.")
}
err := md.getFsInfo(metrics)
if err != nil {
return metrics, err
}
return metrics, nil
}
// getFsInfo writes metrics.Capacity, metrics.Used and metrics.Available from the filesystem info
func (md *metricsStatFS) getFsInfo(metrics *Metrics) error {
available, capacity, usage, err := util.FsInfo(md.path)
if err != nil {
return fmt.Errorf("Failed to get FsInfo due to error %v", err)
}
metrics.Available = resource.NewQuantity(available, resource.BinarySI)
metrics.Capacity = resource.NewQuantity(capacity, resource.BinarySI)
metrics.Used = resource.NewQuantity(usage, resource.BinarySI)
return nil
}

View File

@ -49,6 +49,10 @@ var wrappedVolumeSpec = volume.Spec{
Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}},
}
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(secretPluginName), volName)
}
func (plugin *secretPlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
@ -70,7 +74,7 @@ func (plugin *secretPlugin) NewMounter(spec *volume.Spec, pod *api.Pod, opts vol
plugin,
plugin.host.GetMounter(),
plugin.host.GetWriter(),
volume.NewCachedMetrics(volume.NewMetricsDu(getPathFromHost(plugin.host, pod.UID, spec.Name()))),
volume.NewCachedMetrics(volume.NewMetricsDu(getPath(pod.UID, spec.Name(), plugin.host))),
},
source: *spec.Volume.Secret,
pod: *pod,
@ -86,7 +90,7 @@ func (plugin *secretPlugin) NewUnmounter(volName string, podUID types.UID) (volu
plugin,
plugin.host.GetMounter(),
plugin.host.GetWriter(),
volume.NewCachedMetrics(volume.NewMetricsDu(getPathFromHost(plugin.host, podUID, volName))),
volume.NewCachedMetrics(volume.NewMetricsDu(getPath(podUID, volName, plugin.host))),
},
}, nil
}
@ -103,11 +107,7 @@ type secretVolume struct {
var _ volume.Volume = &secretVolume{}
func (sv *secretVolume) GetPath() string {
return getPathFromHost(sv.plugin.host, sv.podUID, sv.volName)
}
func getPathFromHost(host volume.VolumeHost, podUID types.UID, volName string) string {
return host.GetPodVolumeDir(podUID, strings.EscapeQualifiedNameForDisk(secretPluginName), volName)
return getPath(sv.podUID, sv.volName, sv.plugin.host)
}
// secretVolumeMounter handles retrieving secrets from the API server

View File

@ -1,4 +1,4 @@
// +build linux
// +build linux darwin
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
@ -27,22 +27,25 @@ import (
"k8s.io/kubernetes/pkg/api/resource"
)
// FSInfo linux returns (available bytes, byte capacity, error) for the filesystem that
// FSInfo linux returns (available bytes, byte capacity, byte usage, error) for the filesystem that
// path resides upon.
func FsInfo(path string) (int64, int64, error) {
func FsInfo(path string) (int64, int64, int64, error) {
statfs := &syscall.Statfs_t{}
err := syscall.Statfs(path, statfs)
if err != nil {
return 0, 0, err
return 0, 0, 0, err
}
// TODO(vishh): Include inodes space
// Available is blocks available * fragment size
available := int64(statfs.Bavail) * int64(statfs.Frsize)
available := int64(statfs.Bavail) * int64(statfs.Bsize)
// Capacity is total block count * fragment size
capacity := int64(statfs.Blocks) * int64(statfs.Frsize)
capacity := int64(statfs.Blocks) * int64(statfs.Bsize)
return available, capacity, nil
// Usage is block being used * fragment size (aka block size).
usage := (int64(statfs.Blocks) - int64(statfs.Bfree)) * int64(statfs.Bsize)
return available, capacity, usage, nil
}
func Du(path string) (*resource.Quantity, error) {

View File

@ -1,47 +0,0 @@
// +build darwin
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"errors"
"fmt"
"os/exec"
"strings"
"k8s.io/kubernetes/pkg/api/resource"
)
// FSInfo linux returns (available bytes, byte capacity, error) for the filesystem that
// path resides upon.
func FsInfo(path string) (int64, int64, error) {
return 0, 0, errors.New("FsInfo not supported for this build.")
}
func Du(path string) (*resource.Quantity, error) {
out, err := exec.Command("nice", "-n", "19", "du", "-s", path).CombinedOutput()
if err != nil {
return nil, fmt.Errorf("failed command 'du' ($ nice -n 19 du -s) on path %s with error %v", path, err)
}
used, err := resource.ParseQuantity(strings.Fields(string(out))[0])
if err != nil {
return nil, fmt.Errorf("failed to parse 'du' output %s due to error %v", out, err)
}
used.Format = resource.BinarySI
return &used, nil
}

View File

@ -26,8 +26,8 @@ import (
)
// FSInfo unsupported returns 0 values for available and capacity and an error.
func FsInfo(path string) (int64, int64, error) {
return 0, 0, errors.New("FsInfo not supported for this build.")
func FsInfo(path string) (int64, int64, int64, error) {
return 0, 0, 0, errors.New("FsInfo not supported for this build.")
}
func Du(path string) (*resource.Quantity, error) {