2015-09-08 21:08:49 +00:00
package driver
import (
2017-04-13 16:52:16 +00:00
"context"
2015-12-23 00:10:30 +00:00
"encoding/json"
2017-10-31 06:03:36 +00:00
"errors"
2015-09-08 21:08:49 +00:00
"fmt"
2015-12-23 00:10:30 +00:00
"log"
2017-10-18 00:54:22 +00:00
"net"
2016-10-07 19:37:52 +00:00
"os"
2015-09-08 21:08:49 +00:00
"os/exec"
"path/filepath"
"regexp"
2015-09-22 23:32:05 +00:00
"runtime"
2015-09-08 21:08:49 +00:00
"strings"
"time"
2017-10-18 00:54:22 +00:00
"github.com/coreos/go-semver/semver"
plugin "github.com/hashicorp/go-plugin"
2016-02-05 00:03:17 +00:00
"github.com/hashicorp/nomad/client/driver/executor"
2016-06-12 03:15:50 +00:00
dstructs "github.com/hashicorp/nomad/client/driver/structs"
2015-11-05 21:46:02 +00:00
"github.com/hashicorp/nomad/client/fingerprint"
2016-06-12 03:15:50 +00:00
cstructs "github.com/hashicorp/nomad/client/structs"
2016-04-09 22:38:42 +00:00
"github.com/hashicorp/nomad/helper/fields"
2015-09-08 21:08:49 +00:00
"github.com/hashicorp/nomad/nomad/structs"
2015-11-14 02:09:42 +00:00
"github.com/mitchellh/mapstructure"
2015-09-08 21:08:49 +00:00
)
var (
2015-10-27 22:27:11 +00:00
reQemuVersion = regexp . MustCompile ( ` version (\d[\.\d+]+) ` )
2017-11-03 00:37:49 +00:00
// Prior to qemu 2.10.1, monitor socket paths are truncated to 108 bytes.
// We should consider this if driver.qemu.version is < 2.10.1 and the
// generated monitor path is too long.
2017-11-03 21:45:09 +00:00
2017-11-03 00:37:49 +00:00
//
// Relevant fix is here:
// https://github.com/qemu/qemu/commit/ad9579aaa16d5b385922d49edac2c96c79bcfb6
qemuVersionLongSocketPathFix = semver . New ( "2.10.1" )
2015-09-08 21:08:49 +00:00
)
2016-04-01 01:11:27 +00:00
const (
2017-10-18 00:54:22 +00:00
// The key populated in Node Attributes to indicate presence of the Qemu driver
2017-11-03 00:37:49 +00:00
qemuDriverAttr = "driver.qemu"
qemuDriverVersionAttr = "driver.qemu.version"
2017-10-18 00:54:22 +00:00
// Represents an ACPI shutdown request to the VM (emulates pressing a physical power button)
// Reference: https://en.wikibooks.org/wiki/QEMU/Monitor
qemuGracefulShutdownMsg = "system_powerdown\n"
2017-11-01 21:47:14 +00:00
qemuMonitorSocketName = "qemu-monitor.sock"
2017-11-03 00:37:49 +00:00
// Maximum socket path length prior to qemu 2.10.1
qemuLegacyMaxMonitorPathLen = 108
2016-04-01 01:11:27 +00:00
)
2015-09-08 21:08:49 +00:00
// QemuDriver is a driver for running images via Qemu
// We attempt to chose sane defaults for now, with more configuration available
// planned in the future
type QemuDriver struct {
2015-09-10 01:38:52 +00:00
DriverContext
2015-11-05 21:46:02 +00:00
fingerprint . StaticFingerprinter
2017-06-21 23:29:39 +00:00
driverConfig * QemuDriverConfig
2015-09-08 21:08:49 +00:00
}
2015-11-14 04:22:49 +00:00
type QemuDriverConfig struct {
2017-10-18 00:54:22 +00:00
ImagePath string ` mapstructure:"image_path" `
Accelerator string ` mapstructure:"accelerator" `
GracefulShutdown bool ` mapstructure:"graceful_shutdown" `
PortMap [ ] map [ string ] int ` mapstructure:"port_map" ` // A map of host port labels and to guest ports.
Args [ ] string ` mapstructure:"args" ` // extra arguments to qemu executable
2015-11-14 02:09:42 +00:00
}
2015-09-08 21:08:49 +00:00
// qemuHandle is returned from Start/Open as a handle to the PID
type qemuHandle struct {
2016-03-03 17:21:21 +00:00
pluginClient * plugin . Client
userPid int
executor executor . Executor
2017-10-18 00:54:22 +00:00
monitorPath string
2016-03-03 17:21:21 +00:00
killTimeout time . Duration
maxKillTimeout time . Duration
logger * log . Logger
version string
2016-06-12 03:15:50 +00:00
waitCh chan * dstructs . WaitResult
2016-03-03 17:21:21 +00:00
doneCh chan struct { }
2015-09-08 21:08:49 +00:00
}
2017-11-03 00:37:49 +00:00
// getMonitorPath is used to determine whether a qemu monitor socket can be
// safely created and accessed in the task directory by the version of qemu
// present on the host. If it is safe to use, the socket's full path is
// returned along with a nil error. Otherwise, an empty string is returned
// along with a descriptive error.
func ( d * QemuDriver ) getMonitorPath ( dir string ) ( string , error ) {
var longPathSupport bool
currentQemuVer := d . DriverContext . node . Attributes [ qemuDriverVersionAttr ]
currentQemuSemver := semver . New ( currentQemuVer )
if currentQemuSemver . LessThan ( * qemuVersionLongSocketPathFix ) {
longPathSupport = false
d . logger . Printf ( "[DEBUG] driver.qemu: long socket paths are not available in this version of QEMU (%s)" , currentQemuVer )
} else {
longPathSupport = true
d . logger . Printf ( "[DEBUG] driver.qemu: long socket paths available in this version of QEMU (%s)" , currentQemuVer )
}
fullSocketPath := fmt . Sprintf ( "%s/%s" , dir , qemuMonitorSocketName )
if len ( fullSocketPath ) > qemuLegacyMaxMonitorPathLen && longPathSupport == false {
return "" , fmt . Errorf ( "monitor path is too long for this version of qemu" )
2017-10-18 00:54:22 +00:00
}
2017-11-03 00:37:49 +00:00
return fullSocketPath , nil
2017-10-18 00:54:22 +00:00
}
2015-09-08 21:08:49 +00:00
// NewQemuDriver is used to create a new exec driver
2015-09-10 01:38:52 +00:00
func NewQemuDriver ( ctx * DriverContext ) Driver {
2015-11-05 21:46:02 +00:00
return & QemuDriver { DriverContext : * ctx }
2015-09-08 21:08:49 +00:00
}
2016-04-09 23:15:09 +00:00
// Validate is used to validate the driver configuration
2016-04-08 20:19:43 +00:00
func ( d * QemuDriver ) Validate ( config map [ string ] interface { } ) error {
2016-04-09 22:38:42 +00:00
fd := & fields . FieldData {
Raw : config ,
Schema : map [ string ] * fields . FieldSchema {
2017-10-02 20:16:48 +00:00
"image_path" : {
2016-04-09 22:38:42 +00:00
Type : fields . TypeString ,
Required : true ,
} ,
2017-10-02 20:16:48 +00:00
"accelerator" : {
2016-04-09 22:38:42 +00:00
Type : fields . TypeString ,
} ,
2017-10-18 00:54:22 +00:00
"graceful_shutdown" : {
Type : fields . TypeBool ,
Required : false ,
} ,
2017-10-02 20:16:48 +00:00
"port_map" : {
2016-04-09 22:38:42 +00:00
Type : fields . TypeArray ,
} ,
2017-10-02 20:16:48 +00:00
"args" : {
2016-08-16 03:36:13 +00:00
Type : fields . TypeArray ,
} ,
2016-04-09 22:38:42 +00:00
} ,
}
if err := fd . Validate ( ) ; err != nil {
return err
}
2016-04-08 20:19:43 +00:00
return nil
}
2016-10-19 22:06:23 +00:00
func ( d * QemuDriver ) Abilities ( ) DriverAbilities {
return DriverAbilities {
SendSignals : false ,
2017-04-13 16:52:16 +00:00
Exec : false ,
2016-10-19 22:06:23 +00:00
}
}
2016-12-03 01:04:07 +00:00
func ( d * QemuDriver ) FSIsolation ( ) cstructs . FSIsolation {
return cstructs . FSIsolationImage
}
2018-01-24 14:09:53 +00:00
func ( d * QemuDriver ) Fingerprint ( req * cstructs . FingerprintRequest , resp * cstructs . FingerprintResponse ) error {
2015-10-27 22:27:11 +00:00
bin := "qemu-system-x86_64"
if runtime . GOOS == "windows" {
2015-10-29 23:57:02 +00:00
// On windows, the "qemu-system-x86_64" command does not respond to the
// version flag.
2015-10-27 22:27:11 +00:00
bin = "qemu-img"
2015-09-22 23:32:05 +00:00
}
2015-10-27 22:27:11 +00:00
outBytes , err := exec . Command ( bin , "--version" ) . Output ( )
2015-09-08 21:08:49 +00:00
if err != nil {
2018-01-26 19:31:37 +00:00
// return no error, as it isn't an error to not find qemu, it just means we
// can't use it.
2018-01-24 14:09:53 +00:00
return nil
2015-09-08 21:08:49 +00:00
}
out := strings . TrimSpace ( string ( outBytes ) )
matches := reQemuVersion . FindStringSubmatch ( out )
if len ( matches ) != 2 {
2018-01-26 19:31:37 +00:00
resp . RemoveAttribute ( qemuDriverAttr )
2018-01-24 14:09:53 +00:00
return fmt . Errorf ( "Unable to parse Qemu version string: %#v" , matches )
2015-09-08 21:08:49 +00:00
}
2017-10-31 03:20:31 +00:00
currentQemuVersion := matches [ 1 ]
2015-09-08 21:08:49 +00:00
2018-01-26 16:21:07 +00:00
resp . AddAttribute ( qemuDriverAttr , "1" )
resp . AddAttribute ( qemuDriverVersionAttr , currentQemuVersion )
2018-01-30 17:57:37 +00:00
resp . Applicable = true
2017-10-18 00:54:22 +00:00
2018-01-24 14:09:53 +00:00
return nil
2015-09-08 21:08:49 +00:00
}
2017-06-21 23:29:39 +00:00
func ( d * QemuDriver ) Prestart ( _ * ExecContext , task * structs . Task ) ( * PrestartResponse , error ) {
2015-11-14 04:22:49 +00:00
var driverConfig QemuDriverConfig
2015-11-14 02:09:42 +00:00
if err := mapstructure . WeakDecode ( task . Config , & driverConfig ) ; err != nil {
return nil , err
}
2015-11-18 04:54:53 +00:00
if len ( driverConfig . PortMap ) > 1 {
return nil , fmt . Errorf ( "Only one port_map block is allowed in the qemu driver config" )
}
2017-06-21 23:29:39 +00:00
d . driverConfig = & driverConfig
r := NewPrestartResponse ( )
if len ( driverConfig . PortMap ) == 1 {
r . Network = & cstructs . DriverNetwork {
PortMap : driverConfig . PortMap [ 0 ] ,
}
}
return r , nil
}
// Run an existing Qemu image. Start() will pull down an existing, valid Qemu
// image and save it to the Drivers Allocation Dir
func ( d * QemuDriver ) Start ( ctx * ExecContext , task * structs . Task ) ( * StartResponse , error ) {
2015-09-08 21:08:49 +00:00
// Get the image source
2017-06-21 23:29:39 +00:00
vmPath := d . driverConfig . ImagePath
2016-03-15 17:53:20 +00:00
if vmPath == "" {
return nil , fmt . Errorf ( "image_path must be set" )
2015-09-09 19:28:16 +00:00
}
2016-03-15 17:53:20 +00:00
vmID := filepath . Base ( vmPath )
2015-09-09 19:28:16 +00:00
2015-09-08 21:08:49 +00:00
// Parse configuration arguments
// Create the base arguments
accelerator := "tcg"
2017-06-21 23:29:39 +00:00
if d . driverConfig . Accelerator != "" {
accelerator = d . driverConfig . Accelerator
2015-09-08 21:08:49 +00:00
}
2017-09-27 04:07:48 +00:00
2017-10-02 21:29:44 +00:00
if task . Resources . MemoryMB < 128 || task . Resources . MemoryMB > 4000000 {
2017-09-27 04:07:48 +00:00
return nil , fmt . Errorf ( "Qemu memory assignment out of bounds" )
}
2015-09-09 19:30:35 +00:00
mem := fmt . Sprintf ( "%dM" , task . Resources . MemoryMB )
2015-09-08 21:08:49 +00:00
2016-03-16 02:22:40 +00:00
absPath , err := GetAbsolutePath ( "qemu-system-x86_64" )
if err != nil {
return nil , err
}
2015-09-08 21:08:49 +00:00
args := [ ] string {
2016-03-16 02:22:40 +00:00
absPath ,
2015-09-08 21:08:49 +00:00
"-machine" , "type=pc,accel=" + accelerator ,
"-name" , vmID ,
"-m" , mem ,
2015-10-15 21:40:08 +00:00
"-drive" , "file=" + vmPath ,
2015-09-08 21:08:49 +00:00
"-nographic" ,
}
2017-10-18 00:54:22 +00:00
var monitorPath string
if d . driverConfig . GracefulShutdown {
2017-11-03 21:45:09 +00:00
if runtime . GOOS == "windows" {
return nil , errors . New ( "QEMU graceful shutdown is unsupported on the Windows platform" )
}
2017-10-18 00:54:22 +00:00
// This socket will be used to manage the virtual machine (for example,
// to perform graceful shutdowns)
2017-11-03 00:37:49 +00:00
monitorPath , err := d . getMonitorPath ( ctx . TaskDir . Dir )
if err != nil {
2017-11-03 21:45:09 +00:00
d . logger . Printf ( "[ERR] driver.qemu: could not get qemu monitor path: %s" , err )
2017-11-03 00:37:49 +00:00
return nil , err
2017-10-18 00:54:22 +00:00
}
2017-11-03 00:37:49 +00:00
d . logger . Printf ( "[DEBUG] driver.qemu: got monitor path OK: %s" , monitorPath )
args = append ( args , "-monitor" , fmt . Sprintf ( "unix:%s,server,nowait" , monitorPath ) )
2017-10-18 00:54:22 +00:00
}
2016-08-16 03:36:13 +00:00
// Add pass through arguments to qemu executable. A user can specify
// these arguments in driver task configuration. These arguments are
// passed directly to the qemu driver as command line options.
// For example, args = [ "-nodefconfig", "-nodefaults" ]
// This will allow a VM with embedded configuration to boot successfully.
2017-06-21 23:29:39 +00:00
args = append ( args , d . driverConfig . Args ... )
2016-08-16 03:36:13 +00:00
2015-09-24 19:32:54 +00:00
// Check the Resources required Networks to add port mappings. If no resources
// are required, we assume the VM is a purely compute job and does not require
// the outside world to be able to reach it. VMs ran without port mappings can
// still reach out to the world, but without port mappings it is effectively
// firewalled
2015-11-18 04:54:53 +00:00
protocols := [ ] string { "udp" , "tcp" }
2017-06-21 23:29:39 +00:00
if len ( task . Resources . Networks ) > 0 && len ( d . driverConfig . PortMap ) == 1 {
2015-11-18 04:54:53 +00:00
// Loop through the port map and construct the hostfwd string, to map
2015-09-24 19:32:54 +00:00
// reserved ports to the ports listenting in the VM
2015-11-18 04:54:53 +00:00
// Ex: hostfwd=tcp::22000-:22,hostfwd=tcp::80-:8080
var forwarding [ ] string
2017-06-09 17:29:41 +00:00
taskPorts := task . Resources . Networks [ 0 ] . PortLabels ( )
2017-06-21 23:29:39 +00:00
for label , guest := range d . driverConfig . PortMap [ 0 ] {
2015-11-18 04:54:53 +00:00
host , ok := taskPorts [ label ]
if ! ok {
return nil , fmt . Errorf ( "Unknown port label %q" , label )
}
for _ , p := range protocols {
forwarding = append ( forwarding , fmt . Sprintf ( "hostfwd=%s::%d-:%d" , p , host , guest ) )
}
2015-09-24 19:32:54 +00:00
}
2015-09-24 04:15:28 +00:00
2015-11-18 04:54:53 +00:00
if len ( forwarding ) != 0 {
args = append ( args ,
"-netdev" ,
2016-01-05 21:17:51 +00:00
fmt . Sprintf ( "user,id=user.0,%s" , strings . Join ( forwarding , "," ) ) ,
2015-11-18 04:54:53 +00:00
"-device" , "virtio-net,netdev=user.0" ,
)
2015-09-24 19:32:54 +00:00
}
}
2015-09-24 04:15:28 +00:00
2015-09-08 21:08:49 +00:00
// If using KVM, add optimization args
if accelerator == "kvm" {
2017-11-03 21:45:09 +00:00
if runtime . GOOS == "windows" {
return nil , errors . New ( "KVM accelerator is unsupported on the Windows platform" )
}
2015-09-08 21:08:49 +00:00
args = append ( args ,
"-enable-kvm" ,
"-cpu" , "host" ,
// Do we have cores information available to the Driver?
// "-smp", fmt.Sprintf("%d", cores),
)
}
2017-10-31 03:10:18 +00:00
d . logger . Printf ( "[DEBUG] driver.qemu: starting QemuVM command: %q" , strings . Join ( args , " " ) )
2017-01-06 00:02:20 +00:00
pluginLogFile := filepath . Join ( ctx . TaskDir . Dir , "executor.out" )
2017-01-12 19:50:49 +00:00
executorConfig := & dstructs . ExecutorConfig {
LogFile : pluginLogFile ,
LogLevel : d . config . LogLevel ,
2015-11-06 18:41:42 +00:00
}
2015-09-08 21:08:49 +00:00
2017-01-12 19:50:49 +00:00
exec , pluginClient , err := createExecutor ( d . config . LogOutput , d . config , executorConfig )
2016-02-04 02:01:34 +00:00
if err != nil {
return nil , err
}
2016-02-05 00:03:17 +00:00
executorCtx := & executor . ExecutorContext {
2017-05-23 00:46:40 +00:00
TaskEnv : ctx . TaskEnv ,
2016-12-03 01:04:07 +00:00
Driver : "qemu" ,
Task : task ,
TaskDir : ctx . TaskDir . Dir ,
LogDir : ctx . TaskDir . LogDir ,
2015-09-08 21:08:49 +00:00
}
2016-10-12 18:35:29 +00:00
if err := exec . SetContext ( executorCtx ) ; err != nil {
pluginClient . Kill ( )
return nil , fmt . Errorf ( "failed to set executor context: %v" , err )
}
execCmd := & executor . ExecCommand {
2016-03-23 11:57:31 +00:00
Cmd : args [ 0 ] ,
Args : args [ 1 : ] ,
User : task . User ,
2016-10-12 18:35:29 +00:00
}
ps , err := exec . LaunchCmd ( execCmd )
2016-02-04 02:01:34 +00:00
if err != nil {
pluginClient . Kill ( )
2016-03-19 19:18:10 +00:00
return nil , err
2016-02-04 02:01:34 +00:00
}
2017-10-31 03:10:18 +00:00
d . logger . Printf ( "[INFO] driver.qemu: started new QemuVM: %s" , vmID )
2015-09-08 21:08:49 +00:00
// Create and Return Handle
2016-03-03 17:21:21 +00:00
maxKill := d . DriverContext . config . MaxKillTimeout
2015-09-08 21:08:49 +00:00
h := & qemuHandle {
2016-03-03 17:21:21 +00:00
pluginClient : pluginClient ,
executor : exec ,
userPid : ps . Pid ,
killTimeout : GetKillTimeout ( task . KillTimeout , maxKill ) ,
maxKillTimeout : maxKill ,
2017-10-18 00:54:22 +00:00
monitorPath : monitorPath ,
2017-08-16 22:42:15 +00:00
version : d . config . Version . VersionNumber ( ) ,
2016-03-03 17:21:21 +00:00
logger : d . logger ,
doneCh : make ( chan struct { } ) ,
2016-06-12 03:15:50 +00:00
waitCh : make ( chan * dstructs . WaitResult , 1 ) ,
2015-09-08 21:08:49 +00:00
}
go h . run ( )
2017-06-21 23:29:39 +00:00
resp := & StartResponse { Handle : h }
if len ( d . driverConfig . PortMap ) == 1 {
resp . Network = & cstructs . DriverNetwork {
PortMap : d . driverConfig . PortMap [ 0 ] ,
}
}
return resp , nil
2015-09-08 21:08:49 +00:00
}
2015-12-23 00:10:30 +00:00
type qemuId struct {
2016-03-03 17:21:21 +00:00
Version string
KillTimeout time . Duration
MaxKillTimeout time . Duration
UserPid int
PluginConfig * PluginReattachConfig
2015-12-23 00:10:30 +00:00
}
2015-09-08 21:08:49 +00:00
func ( d * QemuDriver ) Open ( ctx * ExecContext , handleID string ) ( DriverHandle , error ) {
2015-12-23 00:10:30 +00:00
id := & qemuId { }
if err := json . Unmarshal ( [ ] byte ( handleID ) , id ) ; err != nil {
2017-10-31 03:20:31 +00:00
return nil , fmt . Errorf ( "Failed to parse handle %q: %v" , handleID , err )
2015-12-23 00:10:30 +00:00
}
2016-02-04 02:01:34 +00:00
pluginConfig := & plugin . ClientConfig {
2016-02-05 18:49:54 +00:00
Reattach : id . PluginConfig . PluginConfig ( ) ,
2016-02-04 02:01:34 +00:00
}
2016-02-04 21:22:38 +00:00
2017-01-12 19:50:49 +00:00
exec , pluginClient , err := createExecutorWithConfig ( pluginConfig , d . config . LogOutput )
2016-02-04 02:01:34 +00:00
if err != nil {
2017-10-31 03:20:31 +00:00
d . logger . Printf ( "[ERR] driver.qemu: error connecting to plugin so destroying plugin pid %d and user pid %d" , id . PluginConfig . Pid , id . UserPid )
2016-02-04 21:53:30 +00:00
if e := destroyPlugin ( id . PluginConfig . Pid , id . UserPid ) ; e != nil {
2017-10-31 03:20:31 +00:00
d . logger . Printf ( "[ERR] driver.qemu: error destroying plugin pid %d and userpid %d: %v" , id . PluginConfig . Pid , id . UserPid , e )
2016-02-04 21:53:30 +00:00
}
2016-02-04 02:01:34 +00:00
return nil , fmt . Errorf ( "error connecting to plugin: %v" , err )
2015-09-08 21:08:49 +00:00
}
2016-03-30 05:05:02 +00:00
ver , _ := exec . Version ( )
d . logger . Printf ( "[DEBUG] driver.qemu: version of executor: %v" , ver . Version )
2015-09-08 21:08:49 +00:00
// Return a driver handle
2016-02-04 02:01:34 +00:00
h := & qemuHandle {
2016-03-03 17:21:21 +00:00
pluginClient : pluginClient ,
2016-03-29 23:27:31 +00:00
executor : exec ,
2016-03-03 17:21:21 +00:00
userPid : id . UserPid ,
logger : d . logger ,
killTimeout : id . KillTimeout ,
maxKillTimeout : id . MaxKillTimeout ,
version : id . Version ,
doneCh : make ( chan struct { } ) ,
2016-06-12 03:15:50 +00:00
waitCh : make ( chan * dstructs . WaitResult , 1 ) ,
2015-09-08 21:08:49 +00:00
}
go h . run ( )
return h , nil
}
2017-01-14 00:46:08 +00:00
func ( d * QemuDriver ) Cleanup ( * ExecContext , * CreatedResources ) error { return nil }
2017-01-10 21:24:45 +00:00
2015-09-08 21:08:49 +00:00
func ( h * qemuHandle ) ID ( ) string {
2015-12-23 00:10:30 +00:00
id := qemuId {
2016-03-03 17:21:21 +00:00
Version : h . version ,
KillTimeout : h . killTimeout ,
MaxKillTimeout : h . maxKillTimeout ,
PluginConfig : NewPluginReattachConfig ( h . pluginClient . ReattachConfig ( ) ) ,
UserPid : h . userPid ,
2015-12-23 00:10:30 +00:00
}
data , err := json . Marshal ( id )
if err != nil {
h . logger . Printf ( "[ERR] driver.qemu: failed to marshal ID to JSON: %s" , err )
}
return string ( data )
2015-09-08 21:08:49 +00:00
}
2016-06-12 03:15:50 +00:00
func ( h * qemuHandle ) WaitCh ( ) chan * dstructs . WaitResult {
2015-09-08 21:08:49 +00:00
return h . waitCh
}
func ( h * qemuHandle ) Update ( task * structs . Task ) error {
2016-02-04 03:43:44 +00:00
// Store the updated kill timeout.
2016-03-03 17:21:21 +00:00
h . killTimeout = GetKillTimeout ( task . KillTimeout , h . maxKillTimeout )
2016-03-17 09:53:31 +00:00
h . executor . UpdateTask ( task )
2016-02-04 03:43:44 +00:00
2015-09-08 21:08:49 +00:00
// Update is not possible
return nil
}
2017-04-13 16:52:16 +00:00
func ( h * qemuHandle ) Exec ( ctx context . Context , cmd string , args [ ] string ) ( [ ] byte , int , error ) {
return nil , 0 , fmt . Errorf ( "Qemu driver can't execute commands" )
}
2016-10-07 19:37:52 +00:00
func ( h * qemuHandle ) Signal ( s os . Signal ) error {
return fmt . Errorf ( "Qemu driver can't send signals" )
}
2015-09-08 21:08:49 +00:00
func ( h * qemuHandle ) Kill ( ) error {
2017-11-03 21:45:09 +00:00
gracefulShutdownSent := false
// Attempt a graceful shutdown only if it was configured in the job
if h . monitorPath != "" {
if err := sendQemuShutdown ( h . logger , h . monitorPath , h . userPid ) ; err == nil {
gracefulShutdownSent = true
} else {
h . logger . Printf ( "[DEBUG] driver.qemu: error sending graceful shutdown for user process pid %d: %s" , h . userPid , err )
}
}
// If Nomad did not send a graceful shutdown signal, issue an interrupt to
// the qemu process as a last resort
if gracefulShutdownSent == false {
2017-10-31 06:03:36 +00:00
if err := h . executor . ShutDown ( ) ; err != nil {
if h . pluginClient . Exited ( ) {
2017-10-18 00:54:22 +00:00
return nil
}
2017-10-31 06:03:36 +00:00
return fmt . Errorf ( "executor Shutdown failed: %v" , err )
2016-02-17 05:00:49 +00:00
}
}
2017-11-01 21:47:14 +00:00
// If the qemu process exits before the kill timeout is reached, doneChan
2017-10-31 06:03:36 +00:00
// will close and we'll exit without an error. If it takes too long, the
// timer will fire and we'll attempt to kill the process.
2015-09-08 21:08:49 +00:00
select {
case <- h . doneCh :
return nil
2015-12-23 00:10:30 +00:00
case <- time . After ( h . killTimeout ) :
2017-11-01 21:47:14 +00:00
h . logger . Printf ( "[DEBUG] driver.qemu: kill timeout of %s exceeded for user process pid %d" , h . killTimeout . String ( ) , h . userPid )
2016-02-17 05:00:49 +00:00
if h . pluginClient . Exited ( ) {
return nil
}
if err := h . executor . Exit ( ) ; err != nil {
return fmt . Errorf ( "executor Exit failed: %v" , err )
}
return nil
2015-09-08 21:08:49 +00:00
}
}
2016-04-28 23:06:01 +00:00
func ( h * qemuHandle ) Stats ( ) ( * cstructs . TaskResourceUsage , error ) {
return h . executor . Stats ( )
}
2015-09-08 21:08:49 +00:00
func ( h * qemuHandle ) run ( ) {
2016-11-04 21:58:55 +00:00
ps , werr := h . executor . Wait ( )
if ps . ExitCode == 0 && werr != nil {
2016-02-09 18:17:33 +00:00
if e := killProcess ( h . userPid ) ; e != nil {
2017-11-01 17:41:48 +00:00
h . logger . Printf ( "[ERR] driver.qemu: error killing user process pid %d: %v" , h . userPid , e )
2016-02-09 18:17:33 +00:00
}
}
2015-09-08 21:08:49 +00:00
close ( h . doneCh )
2016-11-04 21:58:55 +00:00
// Exit the executor
2016-03-18 19:04:11 +00:00
h . executor . Exit ( )
2016-02-04 02:01:34 +00:00
h . pluginClient . Kill ( )
2016-11-04 21:58:55 +00:00
// Send the results
h . waitCh <- & dstructs . WaitResult { ExitCode : ps . ExitCode , Signal : ps . Signal , Err : werr }
close ( h . waitCh )
2015-09-08 21:08:49 +00:00
}
2017-10-31 06:03:36 +00:00
2017-11-03 00:37:49 +00:00
// sendQemuShutdown attempts to issue an ACPI power-off command via the qemu
// monitor
2017-11-01 21:47:14 +00:00
func sendQemuShutdown ( logger * log . Logger , monitorPath string , userPid int ) error {
if monitorPath == "" {
2017-11-01 23:52:44 +00:00
return errors . New ( "monitorPath not set" )
2017-11-03 00:37:49 +00:00
}
monitorSocket , err := net . Dial ( "unix" , monitorPath )
if err != nil {
logger . Printf ( "[WARN] driver.qemu: could not connect to qemu monitor %q for user process pid %d: %s" , monitorPath , userPid , err )
2017-11-01 23:52:44 +00:00
return err
2017-10-31 06:03:36 +00:00
}
2017-11-03 00:37:49 +00:00
defer monitorSocket . Close ( )
logger . Printf ( "[DEBUG] driver.qemu: sending graceful shutdown command to qemu monitor socket %q for user process pid %d" , monitorPath , userPid )
_ , err = monitorSocket . Write ( [ ] byte ( qemuGracefulShutdownMsg ) )
if err != nil {
logger . Printf ( "[WARN] driver.qemu: failed to send shutdown message %q to monitor socket %q for user process pid %d: %s" , qemuGracefulShutdownMsg , monitorPath , userPid , err )
}
return err
2017-10-31 06:03:36 +00:00
}