github.com/cloudfoundry-incubator/stembuild@v0.0.0-20211223202937-5b61d62226c6/iaas_cli/iaas_clients/vcenter_client.go (about)

     1  package iaas_clients
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"os"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/cloudfoundry-incubator/stembuild/iaas_cli"
    13  )
    14  
    15  type VcenterClient struct {
    16  	Url           string
    17  	credentialUrl string
    18  	redactedUrl   string
    19  	caCertFile    string
    20  	Runner        iaas_cli.CliRunner
    21  }
    22  
    23  func NewVcenterClient(username string, password string, u string, caCertFile string, runner iaas_cli.CliRunner) *VcenterClient {
    24  
    25  	encodedUser := url.QueryEscape(username)
    26  	encodedPassword := url.QueryEscape(password)
    27  	urlWithCredentials := fmt.Sprintf("%s:%s@%s", encodedUser, encodedPassword, u)
    28  	urlWithRedactedPassword := fmt.Sprintf("%s:REDACTED@%s", encodedUser, u)
    29  	return &VcenterClient{Url: u, credentialUrl: urlWithCredentials, redactedUrl: urlWithRedactedPassword, caCertFile: caCertFile, Runner: runner}
    30  }
    31  
    32  func (c *VcenterClient) ValidateUrl() error {
    33  	args := []string{"about", "-u", c.Url}
    34  	errMsg := fmt.Sprintf("vcenter_client - unable to validate url: %s", c.Url)
    35  	if c.caCertFile != "" {
    36  		args = append(args, fmt.Sprintf("-tls-ca-certs=%s", c.caCertFile))
    37  		errMsg = fmt.Sprintf("vcenter_client - invalid ca certs or url: %s", c.Url)
    38  	}
    39  	errCode := c.Runner.Run(args)
    40  	if errCode != 0 {
    41  		return errors.New(errMsg)
    42  	}
    43  	return nil
    44  
    45  }
    46  
    47  func (c *VcenterClient) ValidateCredentials() error {
    48  	args := c.buildGovcCommand("about")
    49  	errCode := c.Runner.Run(args)
    50  	if errCode != 0 {
    51  		return errors.New(fmt.Sprintf("vcenter_client - invalid credentials for: %s", c.redactedUrl))
    52  	}
    53  
    54  	return nil
    55  }
    56  
    57  func (c *VcenterClient) FindVM(vmInventoryPath string) error {
    58  	args := c.buildGovcCommand("find", "-maxdepth=0", vmInventoryPath)
    59  	errCode := c.Runner.Run(args)
    60  	if errCode != 0 {
    61  		return errors.New(fmt.Sprintf("vcenter_client - unable to find VM: %s. Ensure your inventory path is formatted properly and includes \"vm\" in its path, example: /my-datacenter/vm/my-folder/my-vm-name", vmInventoryPath))
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  func (c *VcenterClient) ListDevices(vmInventoryPath string) ([]string, error) {
    68  	args := c.buildGovcCommand("device.ls", "-vm", vmInventoryPath)
    69  	o, exitCode, err := c.Runner.RunWithOutput(args)
    70  
    71  	if exitCode != 0 {
    72  		return []string{}, fmt.Errorf("vcenter_client - failed to list devices in vCenter, govc exit code %d", exitCode)
    73  	}
    74  
    75  	if err != nil {
    76  		return []string{}, fmt.Errorf("vcenter_client - failed to parse list of devices. Err: %s", err)
    77  	}
    78  
    79  	entries := strings.Split(o, "\n")
    80  	devices := []string{}
    81  	r, _ := regexp.Compile(`\S+`)
    82  	for _, entry := range entries {
    83  		if entry != "" {
    84  			devices = append(devices, r.FindString(entry))
    85  		}
    86  	}
    87  	return devices, nil
    88  }
    89  func (c *VcenterClient) RemoveDevice(vmInventoryPath string, deviceName string) error {
    90  	args := c.buildGovcCommand("device.remove", "-vm", vmInventoryPath, deviceName)
    91  	errCode := c.Runner.Run(args)
    92  	if errCode != 0 {
    93  		return fmt.Errorf("vcenter_client - %s could not be removed", deviceName)
    94  	}
    95  	return nil
    96  }
    97  
    98  func (c *VcenterClient) EjectCDRom(vmInventoryPath string, deviceName string) error {
    99  
   100  	args := c.buildGovcCommand("device.cdrom.eject", "-vm", vmInventoryPath, "-device", deviceName)
   101  	errCode := c.Runner.Run(args)
   102  	if errCode != 0 {
   103  		return fmt.Errorf("vcenter_client - %s could not be ejected", deviceName)
   104  	}
   105  	return nil
   106  }
   107  
   108  func (c *VcenterClient) ExportVM(vmInventoryPath string, destination string) error {
   109  	_, err := os.Stat(destination)
   110  	if err != nil {
   111  		return errors.New(fmt.Sprintf("vcenter_client - provided destination directory: %s does not exist", destination))
   112  	}
   113  	args := c.buildGovcCommand("export.ovf", "-sha", "1", "-vm", vmInventoryPath, destination)
   114  	errCode := c.Runner.Run(args)
   115  	if errCode != 0 {
   116  		return errors.New(fmt.Sprintf("vcenter_client - %s could not be exported", vmInventoryPath))
   117  	}
   118  	return nil
   119  }
   120  
   121  func (c *VcenterClient) UploadArtifact(vmInventoryPath, artifact, destination, username, password string) error {
   122  	vmCredentials := fmt.Sprintf("%s:%s", username, password)
   123  	args := c.buildGovcCommand("guest.upload", "-f", "-l", vmCredentials, "-vm", vmInventoryPath, artifact, destination)
   124  	errCode := c.Runner.Run(args)
   125  	if errCode != 0 {
   126  		return fmt.Errorf("vcenter_client - %s could not be uploaded", artifact)
   127  	}
   128  	return nil
   129  }
   130  
   131  func (c *VcenterClient) MakeDirectory(vmInventoryPath, path, username, password string) error {
   132  	vmCredentials := fmt.Sprintf("%s:%s", username, password)
   133  
   134  	args := c.buildGovcCommand("guest.mkdir", "-l", vmCredentials, "-vm", vmInventoryPath, "-p", path)
   135  	errCode := c.Runner.Run(args)
   136  	if errCode != 0 {
   137  		return fmt.Errorf("vcenter_client - directory `%s` could not be created", path)
   138  	}
   139  	return nil
   140  }
   141  
   142  func (c *VcenterClient) Start(vmInventoryPath, username, password, command string, args ...string) (string, error) {
   143  	vmCredentials := fmt.Sprintf("%s:%s", username, password)
   144  
   145  	cmdArgs := c.buildGovcCommand(append([]string{"guest.start", "-l", vmCredentials, "-vm", vmInventoryPath, command}, args...)...)
   146  	pid, exitCode, err := c.Runner.RunWithOutput(cmdArgs)
   147  	if err != nil {
   148  		return "", fmt.Errorf("vcenter_client - failed to run '%s': %s", command, err)
   149  	}
   150  	if exitCode != 0 {
   151  		return "", fmt.Errorf("vcenter_client - '%s' returned exit code: %d", command, exitCode)
   152  	}
   153  	// We trim this suffix since govc outputs the pid with an '\n' in the output
   154  	return strings.TrimSuffix(pid, "\n"), nil
   155  }
   156  
   157  type govcPS struct {
   158  	ProcessInfo []struct {
   159  		Name      string
   160  		Pid       int
   161  		Owner     string
   162  		CmdLine   string
   163  		StartTime string
   164  		EndTime   string
   165  		ExitCode  int
   166  	}
   167  }
   168  
   169  func (c *VcenterClient) WaitForExit(vmInventoryPath, username, password, pid string) (int, error) {
   170  	vmCredentials := fmt.Sprintf("%s:%s", username, password)
   171  	args := c.buildGovcCommand("guest.ps", "-l", vmCredentials, "-vm", vmInventoryPath, "-p", pid, "-X", "-json")
   172  	output, exitCode, err := c.Runner.RunWithOutput(args)
   173  	if err != nil {
   174  		return 0, fmt.Errorf("vcenter_client - failed to fetch exit code for PID %s: %s", pid, err)
   175  	}
   176  	if exitCode != 0 {
   177  		return 0, fmt.Errorf("vcenter_client - fetching PID %s returned with exit code: %d", pid, exitCode)
   178  	}
   179  
   180  	ps := govcPS{}
   181  	err = json.Unmarshal([]byte(output), &ps)
   182  	if err != nil {
   183  		return 0, fmt.Errorf("vcenter_client - received bad JSON output for PID %s: %s", pid, output)
   184  	}
   185  	if len(ps.ProcessInfo) != 1 {
   186  		return 0, fmt.Errorf("vcenter_client - couldn't get exit code for PID %s", pid)
   187  	}
   188  
   189  	return ps.ProcessInfo[0].ExitCode, nil
   190  }
   191  
   192  func (c *VcenterClient) buildGovcCommand(args ...string) []string {
   193  	commonArgs := []string{"-u", c.credentialUrl}
   194  	if c.caCertFile != "" {
   195  		commonArgs = append(commonArgs, fmt.Sprintf("-tls-ca-certs=%s", c.caCertFile))
   196  	}
   197  	args = append(args[:1], append(commonArgs, args[1:]...)...)
   198  	return args
   199  }
   200  
   201  func (c *VcenterClient) IsPoweredOff(vmInventoryPath string) (bool, error) {
   202  	args := c.buildGovcCommand("vm.info", vmInventoryPath)
   203  	out, exitCode, err := c.Runner.RunWithOutput(args)
   204  	if exitCode != 0 {
   205  		return false, fmt.Errorf("vcenter_client - failed to get vm info, govc exit code: %d", exitCode)
   206  	}
   207  	if err != nil {
   208  		return false, fmt.Errorf("vcenter_client - failed to determine vm power state: %s", err)
   209  	}
   210  
   211  	if strings.Contains(out, "poweredOff") {
   212  		return true, nil
   213  	}
   214  
   215  	return false, nil
   216  }