github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/container/kvm/initialisation.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package kvm 5 6 import ( 7 "os" 8 "runtime" 9 10 "github.com/juju/errors" 11 "github.com/juju/os/series" 12 "github.com/juju/packaging/manager" 13 "github.com/juju/utils/arch" 14 15 "github.com/juju/juju/container" 16 "github.com/juju/juju/juju/paths" 17 ) 18 19 type containerInitialiser struct{} 20 21 // containerInitialiser implements container.Initialiser. 22 var _ container.Initialiser = (*containerInitialiser)(nil) 23 24 // NewContainerInitialiser returns an instance used to perform the steps 25 // required to allow a host machine to run a KVM container. 26 func NewContainerInitialiser() container.Initialiser { 27 return &containerInitialiser{} 28 } 29 30 // Initialise is specified on the container.Initialiser interface. 31 func (ci *containerInitialiser) Initialise() error { 32 if err := ensureDependencies(); err != nil { 33 return errors.Trace(err) 34 } 35 36 // Check if we've done this already. 37 poolInfo, err := poolInfo(run) 38 if err != nil { 39 return errors.Trace(err) 40 } 41 if poolInfo == nil { 42 if err := createPool(paths.DataDir, run, chownToLibvirt); err != nil { 43 return errors.Trace(err) 44 } 45 return nil 46 } 47 logger.Debugf(`pool already initialised "%#v"`, poolInfo) 48 49 return nil 50 } 51 52 // getPackageManager is a helper function which returns the 53 // package manager implementation for the current system. 54 func getPackageManager() (manager.PackageManager, error) { 55 hostSeries, err := series.HostSeries() 56 if err != nil { 57 return nil, errors.Trace(err) 58 } 59 return manager.NewPackageManager(hostSeries) 60 } 61 62 func ensureDependencies() error { 63 pacman, err := getPackageManager() 64 if err != nil { 65 return err 66 } 67 68 for _, pack := range getRequiredPackages(runtime.GOARCH) { 69 if err := pacman.Install(pack); err != nil { 70 return err 71 } 72 } 73 74 return nil 75 } 76 77 func getRequiredPackages(a string) []string { 78 var requiredPackages = []string{ 79 // `qemu-kvm` must be installed before `libvirt-bin` on trusty. It appears 80 // that upstart doesn't reload libvirtd if installed after, and we see 81 // errors related to `qemu-kvm` not being installed. 82 "qemu-kvm", 83 "qemu-utils", 84 "genisoimage", 85 "libvirt-bin", 86 } 87 if a == arch.ARM64 { 88 // ARM64 doesn't support legacy BIOS so it requires Extensible Firmware 89 // Interface. 90 requiredPackages = append([]string{"qemu-efi"}, requiredPackages...) 91 } 92 return requiredPackages 93 } 94 95 // createPool creates the libvirt storage pool directory. runCmd and chownFunc 96 // are here for testing. runCmd so we can check the right shell out calls are 97 // made, and chownFunc because we cannot chown unless we are root. 98 func createPool(pathfinder func(string) (string, error), runCmd runFunc, chownFunc func(string) error) error { 99 poolDir, err := guestPath(pathfinder) 100 if err != nil { 101 return errors.Trace(err) 102 } 103 104 if err = definePool(poolDir, runCmd, chownFunc); err != nil { 105 return errors.Trace(err) 106 } 107 if err = buildPool(runCmd); err != nil { 108 return errors.Trace(err) 109 } 110 111 if err = startPool(runCmd); err != nil { 112 return errors.Trace(err) 113 } 114 if err = autostartPool(runCmd); err != nil { 115 return errors.Trace(err) 116 } 117 118 // We have to set ownership of the guest pool directory after running virsh 119 // commands above, because it appears that the libvirt-bin version that 120 // ships with trusty sets the ownership of the pool directory to the user 121 // running the commands -- root in our case. Which causes container 122 // initialization to fail as we couldn't write volumes to the pool. We 123 // write them as libvirt-qemu:kvm so that libvirt -- which runs as that 124 // user -- can read them to boot the domains. 125 if err = chownFunc(poolDir); err != nil { 126 return errors.Trace(err) 127 } 128 129 return nil 130 } 131 132 // definePool creates the required directories and changes ownershipt of the 133 // guest directory so that libvirt-qemu can read, write, and execute its 134 // guest volumes. 135 func definePool(dir string, runCmd runFunc, chownFunc func(string) error) error { 136 // Permissions gleaned from https://goo.gl/SZIw14 137 // The command itself would change the permissions to match anyhow. 138 err := os.MkdirAll(dir, 0755) 139 if err != nil { 140 return errors.Trace(err) 141 } 142 143 // The dashes are empty positional args for other types of pool storage: 144 // e.g. file, lvm, scsi, disk, NFS. Newer versions support using only named 145 // args (--type, --target) but this is backwards compatible for trusty. 146 output, err := runCmd( 147 "virsh", 148 "pool-define-as", 149 poolName, 150 "dir", 151 "-", "-", "-", "-", 152 dir) 153 if err != nil { 154 return errors.Trace(err) 155 } 156 logger.Debugf("pool-define-as output %s", output) 157 158 return nil 159 } 160 161 // chownToLibvirt changes ownership of the provided directory to 162 // libvirt-qemu:kvm. 163 func chownToLibvirt(dir string) error { 164 uid, gid, err := getUserUIDGID(libvirtUser) 165 if err != nil { 166 logger.Errorf("failed to get livirt-qemu uid:gid %s", err) 167 return errors.Trace(err) 168 } 169 170 err = os.Chown(dir, uid, gid) 171 if err != nil { 172 logger.Errorf("failed to change ownership of %q to uid:gid %d:%d %s", dir, uid, gid, err) 173 return errors.Trace(err) 174 } 175 logger.Tracef("%q is now owned by %q %d:%d", dir, libvirtUser, uid, gid) 176 return nil 177 } 178 179 // buildPool sets up libvirt internals for the guest pool. 180 func buildPool(runCmd runFunc) error { 181 // This can run without error if the pool isn't active. 182 output, err := runCmd("virsh", "pool-build", poolName) 183 if err != nil { 184 return errors.Trace(err) 185 } 186 logger.Debugf("pool-build output %s", output) 187 return nil 188 } 189 190 // startPool makes the pool available for use in libvirt. 191 func startPool(runCmd runFunc) error { 192 output, err := runCmd("virsh", "pool-start", poolName) 193 if err != nil { 194 return errors.Trace(err) 195 } 196 logger.Debugf("pool-start output %s", output) 197 198 return nil 199 } 200 201 // autostartPool sets up the pool to run automatically when libvirt starts. 202 func autostartPool(runCmd runFunc) error { 203 output, err := runCmd("virsh", "pool-autostart", poolName) 204 if err != nil { 205 return errors.Trace(err) 206 } 207 logger.Debugf("pool-autostart output %s", output) 208 209 return nil 210 }