github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/container/kvm/libvirt.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package kvm
     5  
     6  // This file contains wrappers around the following executables:
     7  //   uvt-simplestreams-libvirt
     8  //   uvt-kvm
     9  //   virsh
    10  // Those executables are found in the following packages:
    11  //   uvtool-libvirt
    12  //   libvirt-bin
    13  //
    14  // These executables provide Juju's interface to dealing with kvm containers.
    15  // The define how we start, stop and list running containers on the host
    16  
    17  import (
    18  	"fmt"
    19  	"path/filepath"
    20  	"regexp"
    21  	"strings"
    22  
    23  	"github.com/juju/errors"
    24  	"github.com/juju/utils"
    25  
    26  	"github.com/juju/juju/network"
    27  )
    28  
    29  var (
    30  	// The regular expression for breaking up the results of 'virsh list'
    31  	// (?m) - specify that this is a multiline regex
    32  	// first part is the opaque identifier we don't care about
    33  	// then the hostname, and lastly the status.
    34  	machineListPattern = regexp.MustCompile(`(?m)^\s+\d+\s+(?P<hostname>[-\w]+)\s+(?P<status>.+)\s*$`)
    35  )
    36  
    37  // run the command and return the combined output.
    38  func run(command string, args ...string) (output string, err error) {
    39  	logger.Tracef("%s %v", command, args)
    40  	output, err = utils.RunCommand(command, args...)
    41  	logger.Tracef("output: %v", output)
    42  	return output, err
    43  }
    44  
    45  // SyncImages updates the local cached images by reading the simplestreams
    46  // data and downloading the cloud images to the uvtool pool (used by libvirt).
    47  func SyncImages(series, arch, source string) error {
    48  
    49  	args := []string{
    50  		"sync",
    51  		fmt.Sprintf("arch=%s", arch),
    52  		fmt.Sprintf("release=%s", series),
    53  	}
    54  
    55  	if source != "" {
    56  		args = append(args, fmt.Sprintf("--source=%s", source))
    57  	}
    58  
    59  	_, err := run("uvt-simplestreams-libvirt", args...)
    60  	return err
    61  }
    62  
    63  type CreateMachineParams struct {
    64  	Hostname      string
    65  	Series        string
    66  	Arch          string
    67  	UserDataFile  string
    68  	NetworkBridge string
    69  	Memory        uint64
    70  	CpuCores      uint64
    71  	RootDisk      uint64
    72  	Interfaces    []network.InterfaceInfo
    73  }
    74  
    75  // CreateMachine creates a virtual machine and starts it.
    76  func CreateMachine(params CreateMachineParams) error {
    77  	if params.Hostname == "" {
    78  		return fmt.Errorf("Hostname is required")
    79  	}
    80  	args := []string{
    81  		"create",
    82  		"--log-console-output", // do wonder where this goes...
    83  	}
    84  	if params.UserDataFile != "" {
    85  		args = append(args, "--user-data", params.UserDataFile)
    86  	}
    87  	if params.Memory != 0 {
    88  		args = append(args, "--memory", fmt.Sprint(params.Memory))
    89  	}
    90  	if params.CpuCores != 0 {
    91  		args = append(args, "--cpu", fmt.Sprint(params.CpuCores))
    92  	}
    93  	if params.RootDisk != 0 {
    94  		args = append(args, "--disk", fmt.Sprint(params.RootDisk))
    95  	}
    96  	if params.NetworkBridge != "" {
    97  		if len(params.Interfaces) != 0 {
    98  			templateDir := filepath.Dir(params.UserDataFile)
    99  
   100  			templatePath := filepath.Join(templateDir, "kvm-template.xml")
   101  			err := WriteTemplate(templatePath, params)
   102  			if err != nil {
   103  				return errors.Trace(err)
   104  			}
   105  
   106  			args = append(args, "--template", templatePath)
   107  		} else {
   108  			args = append(args, "--bridge", params.NetworkBridge)
   109  		}
   110  	}
   111  
   112  	args = append(args, params.Hostname)
   113  	if params.Series != "" {
   114  		args = append(args, fmt.Sprintf("release=%s", params.Series))
   115  	}
   116  	if params.Arch != "" {
   117  		args = append(args, fmt.Sprintf("arch=%s", params.Arch))
   118  	}
   119  	output, err := run("uvt-kvm", args...)
   120  	logger.Debugf("is this the logged output?:\n%s", output)
   121  	return err
   122  }
   123  
   124  // DestroyMachine destroys the virtual machine identified by hostname.
   125  func DestroyMachine(hostname string) error {
   126  	_, err := run("uvt-kvm", "destroy", hostname)
   127  	return err
   128  }
   129  
   130  // AutostartMachine indicates that the virtual machines should automatically
   131  // restart when the host restarts.
   132  func AutostartMachine(hostname string) error {
   133  	_, err := run("virsh", "autostart", hostname)
   134  	return err
   135  }
   136  
   137  // ListMachines returns a map of machine name to state, where state is one of:
   138  // running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended.
   139  func ListMachines() (map[string]string, error) {
   140  	output, err := run("virsh", "-q", "list", "--all")
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	// Split the output into lines.
   145  	// Regex matching is the easiest way to match the lines.
   146  	//   id hostname status
   147  	// separated by whitespace, with whitespace at the start too.
   148  	result := make(map[string]string)
   149  	for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) {
   150  		hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s)
   151  		parts := strings.SplitN(string(hostnameAndStatus), " ", 2)
   152  		result[parts[0]] = parts[1]
   153  	}
   154  	return result, nil
   155  }