diff --git a/pkg/kubelet/volumemanager/cache/desired_state_of_world.go b/pkg/kubelet/volumemanager/cache/desired_state_of_world.go index e56e13b1794..c96b804a377 100644 --- a/pkg/kubelet/volumemanager/cache/desired_state_of_world.go +++ b/pkg/kubelet/volumemanager/cache/desired_state_of_world.go @@ -23,6 +23,7 @@ package cache import ( "fmt" "sync" + "time" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -207,6 +208,8 @@ type podToMount struct { // volume claim, this contains the volume.Spec.Name() of the persistent // volume claim outerVolumeSpecName string + // mountRequestTime stores time at which mount was requested + mountRequestTime time.Time } const ( @@ -281,6 +284,11 @@ func (dsw *desiredStateOfWorld) AddPodToVolume( desiredSizeLimit: sizeLimit, } } + oldPodMount, ok := dsw.volumesToMount[volumeName].podsToMount[podName] + mountRequestTime := time.Now() + if ok && !volumePlugin.RequiresRemount(volumeSpec) { + mountRequestTime = oldPodMount.mountRequestTime + } // Create new podToMount object. If it already exists, it is refreshed with // updated values (this is required for volumes that require remounting on @@ -290,6 +298,7 @@ func (dsw *desiredStateOfWorld) AddPodToVolume( pod: pod, volumeSpec: volumeSpec, outerVolumeSpecName: outerVolumeSpecName, + mountRequestTime: mountRequestTime, } return volumeName, nil } @@ -407,6 +416,7 @@ func (dsw *desiredStateOfWorld) GetVolumesToMount() []VolumeToMount { OuterVolumeSpecName: podObj.outerVolumeSpecName, VolumeGidValue: volumeObj.volumeGidValue, ReportedInUse: volumeObj.reportedInUse, + MountRequestTime: podObj.mountRequestTime, DesiredSizeLimit: volumeObj.desiredSizeLimit}}) } } diff --git a/pkg/volume/util/operationexecutor/operation_executor.go b/pkg/volume/util/operationexecutor/operation_executor.go index d7ec7989efe..3ee26c524a1 100644 --- a/pkg/volume/util/operationexecutor/operation_executor.go +++ b/pkg/volume/util/operationexecutor/operation_executor.go @@ -388,6 +388,9 @@ type VolumeToMount struct { // DesiredSizeLimit indicates the desired upper bound on the size of the volume // (if so implemented) DesiredSizeLimit *resource.Quantity + + // time at which volume was requested to be mounted + MountRequestTime time.Time } // DeviceMountState represents device mount state in a global path. diff --git a/pkg/volume/util/operationexecutor/operation_generator.go b/pkg/volume/util/operationexecutor/operation_generator.go index 9b7482f6f5d..47603d6ad89 100644 --- a/pkg/volume/util/operationexecutor/operation_generator.go +++ b/pkg/volume/util/operationexecutor/operation_generator.go @@ -778,6 +778,12 @@ func (og *operationGenerator) GenerateMountVolumeFunc( } } + // record total time it takes to mount a volume. This is end to end time that includes waiting for volume to attach, node to be update + // plugin call to succeed + mountRequestTime := volumeToMount.MountRequestTime + totalTimeTaken := time.Since(mountRequestTime).Seconds() + util.RecordOperationLatencyMetric(util.GetFullQualifiedPluginNameForVolume(volumePluginName, volumeToMount.VolumeSpec), "overall_volume_mount", totalTimeTaken) + markVolMountedErr := actualStateOfWorld.MarkVolumeAsMounted(markOpts) if markVolMountedErr != nil { // On failure, return error. Caller will log and retry.