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 }