...

Source file src/github.com/shirou/gopsutil/mem/mem_solaris.go

Documentation: github.com/shirou/gopsutil/mem

     1  // +build solaris
     2  
     3  package mem
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os/exec"
     9  	"regexp"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/shirou/gopsutil/internal/common"
    14  )
    15  
    16  // VirtualMemory for Solaris is a minimal implementation which only returns
    17  // what Nomad needs. It does take into account global vs zone, however.
    18  func VirtualMemory() (*VirtualMemoryStat, error) {
    19  	return VirtualMemoryWithContext(context.Background())
    20  }
    21  
    22  func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
    23  	result := &VirtualMemoryStat{}
    24  
    25  	zoneName, err := zoneName()
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  
    30  	if zoneName == "global" {
    31  		cap, err := globalZoneMemoryCapacity()
    32  		if err != nil {
    33  			return nil, err
    34  		}
    35  		result.Total = cap
    36  	} else {
    37  		cap, err := nonGlobalZoneMemoryCapacity()
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  		result.Total = cap
    42  	}
    43  
    44  	return result, nil
    45  }
    46  
    47  func SwapMemory() (*SwapMemoryStat, error) {
    48  	return SwapMemoryWithContext(context.Background())
    49  }
    50  
    51  func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
    52  	return nil, common.ErrNotImplementedError
    53  }
    54  
    55  func zoneName() (string, error) {
    56  	zonename, err := exec.LookPath("zonename")
    57  	if err != nil {
    58  		return "", err
    59  	}
    60  
    61  	ctx := context.Background()
    62  	out, err := invoke.CommandWithContext(ctx, zonename)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	return strings.TrimSpace(string(out)), nil
    68  }
    69  
    70  var globalZoneMemoryCapacityMatch = regexp.MustCompile(`[Mm]emory size: (\d+) Megabytes`)
    71  
    72  func globalZoneMemoryCapacity() (uint64, error) {
    73  	prtconf, err := exec.LookPath("prtconf")
    74  	if err != nil {
    75  		return 0, err
    76  	}
    77  
    78  	ctx := context.Background()
    79  	out, err := invoke.CommandWithContext(ctx, prtconf)
    80  	if err != nil {
    81  		return 0, err
    82  	}
    83  
    84  	match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1)
    85  	if len(match) != 1 {
    86  		return 0, fmt.Errorf("memory size not contained in output of %q", prtconf)
    87  	}
    88  
    89  	totalMB, err := strconv.ParseUint(match[0][1], 10, 64)
    90  	if err != nil {
    91  		return 0, err
    92  	}
    93  
    94  	return totalMB * 1024 * 1024, nil
    95  }
    96  
    97  var kstatMatch = regexp.MustCompile(`(\S+)\s+(\S*)`)
    98  
    99  func nonGlobalZoneMemoryCapacity() (uint64, error) {
   100  	kstat, err := exec.LookPath("kstat")
   101  	if err != nil {
   102  		return 0, err
   103  	}
   104  
   105  	ctx := context.Background()
   106  	out, err := invoke.CommandWithContext(ctx, kstat, "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap")
   107  	if err != nil {
   108  		return 0, err
   109  	}
   110  
   111  	kstats := kstatMatch.FindAllStringSubmatch(string(out), -1)
   112  	if len(kstats) != 1 {
   113  		return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats))
   114  	}
   115  
   116  	memSizeBytes, err := strconv.ParseUint(kstats[0][2], 10, 64)
   117  	if err != nil {
   118  		return 0, err
   119  	}
   120  
   121  	return memSizeBytes, nil
   122  }
   123  
   124  const swapCommand = "swap"
   125  
   126  // The blockSize as reported by `swap -l`. See https://docs.oracle.com/cd/E23824_01/html/821-1459/fsswap-52195.html
   127  const blockSize = 512
   128  
   129  // swapctl column indexes
   130  const (
   131  	nameCol = 0
   132  	// devCol = 1
   133  	// swaploCol = 2
   134  	totalBlocksCol = 3
   135  	freeBlocksCol  = 4
   136  )
   137  
   138  func SwapDevices() ([]*SwapDevice, error) {
   139  	return SwapDevicesWithContext(context.Background())
   140  }
   141  
   142  func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
   143  	swapCommandPath, err := exec.LookPath(swapCommand)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("could not find command %q: %w", swapCommand, err)
   146  	}
   147  	output, err := invoke.CommandWithContext(ctx, swapCommandPath, "-l")
   148  	if err != nil {
   149  		return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
   150  	}
   151  
   152  	return parseSwapsCommandOutput(string(output))
   153  }
   154  
   155  func parseSwapsCommandOutput(output string) ([]*SwapDevice, error) {
   156  	lines := strings.Split(output, "\n")
   157  	if len(lines) == 0 {
   158  		return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
   159  	}
   160  
   161  	// Check header headerFields are as expected.
   162  	headerFields := strings.Fields(lines[0])
   163  	if len(headerFields) < freeBlocksCol {
   164  		return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, lines[0])
   165  	}
   166  	if headerFields[nameCol] != "swapfile" {
   167  		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "swapfile")
   168  	}
   169  	if headerFields[totalBlocksCol] != "blocks" {
   170  		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalBlocksCol], "blocks")
   171  	}
   172  	if headerFields[freeBlocksCol] != "free" {
   173  		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[freeBlocksCol], "free")
   174  	}
   175  
   176  	var swapDevices []*SwapDevice
   177  	for _, line := range lines[1:] {
   178  		if line == "" {
   179  			continue // the terminal line is typically empty
   180  		}
   181  		fields := strings.Fields(line)
   182  		if len(fields) < freeBlocksCol {
   183  			return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
   184  		}
   185  
   186  		totalBlocks, err := strconv.ParseUint(fields[totalBlocksCol], 10, 64)
   187  		if err != nil {
   188  			return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
   189  		}
   190  
   191  		freeBlocks, err := strconv.ParseUint(fields[freeBlocksCol], 10, 64)
   192  		if err != nil {
   193  			return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
   194  		}
   195  
   196  		swapDevices = append(swapDevices, &SwapDevice{
   197  			Name:      fields[nameCol],
   198  			UsedBytes: (totalBlocks - freeBlocks) * blockSize,
   199  			FreeBytes: freeBlocks * blockSize,
   200  		})
   201  	}
   202  
   203  	return swapDevices, nil
   204  }
   205  

View as plain text