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 }