github.com/google/cadvisor@v0.49.1/devicemapper/thin_ls_client.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package devicemapper
    16  
    17  import (
    18  	"bufio"
    19  	"bytes"
    20  	"fmt"
    21  	"os/exec"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"k8s.io/klog/v2"
    26  )
    27  
    28  // thinLsClient knows how to run a thin_ls very specific to CoW usage for
    29  // containers.
    30  type thinLsClient interface {
    31  	// ThinLs runs a thin ls on the given device, which is expected to be a
    32  	// metadata device. The caller must hold the metadata snapshot for the
    33  	// device.
    34  	ThinLs(deviceName string) (map[string]uint64, error)
    35  }
    36  
    37  // newThinLsClient returns a thinLsClient or an error if the thin_ls binary
    38  // couldn't be located.
    39  func newThinLsClient() (thinLsClient, error) {
    40  	thinLsPath, err := ThinLsBinaryPresent()
    41  	if err != nil {
    42  		return nil, fmt.Errorf("error creating thin_ls client: %v", err)
    43  	}
    44  
    45  	return &defaultThinLsClient{thinLsPath}, nil
    46  }
    47  
    48  // defaultThinLsClient is a functional thinLsClient
    49  type defaultThinLsClient struct {
    50  	thinLsPath string
    51  }
    52  
    53  var _ thinLsClient = &defaultThinLsClient{}
    54  
    55  func (c *defaultThinLsClient) ThinLs(deviceName string) (map[string]uint64, error) {
    56  	args := []string{"--no-headers", "-m", "-o", "DEV,EXCLUSIVE_BYTES", deviceName}
    57  	klog.V(4).Infof("running command: thin_ls %v", strings.Join(args, " "))
    58  
    59  	output, err := exec.Command(c.thinLsPath, args...).Output()
    60  	if err != nil {
    61  		return nil, fmt.Errorf("Error running command `thin_ls %v`: %v\noutput:\n\n%v", strings.Join(args, " "), err, string(output))
    62  	}
    63  
    64  	return parseThinLsOutput(output), nil
    65  }
    66  
    67  // parseThinLsOutput parses the output returned by thin_ls to build a map of
    68  // device id -> usage.
    69  func parseThinLsOutput(output []byte) map[string]uint64 {
    70  	cache := map[string]uint64{}
    71  
    72  	// parse output
    73  	scanner := bufio.NewScanner(bytes.NewReader(output))
    74  	for scanner.Scan() {
    75  		output := scanner.Text()
    76  		fields := strings.Fields(output)
    77  		if len(fields) != 2 {
    78  			continue
    79  		}
    80  
    81  		deviceID := fields[0]
    82  		usage, err := strconv.ParseUint(fields[1], 10, 64)
    83  		if err != nil {
    84  			klog.Warningf("unexpected error parsing thin_ls output: %v", err)
    85  			continue
    86  		}
    87  
    88  		cache[deviceID] = usage
    89  	}
    90  
    91  	return cache
    92  
    93  }