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