Skip to content
This repository was archived by the owner on Apr 3, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions hyperstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,15 @@ func (h *hyper) startPod(pod Pod) error {

// stopPod is the agent Pod stopping implementation for hyperstart.
func (h *hyper) stopPod(pod Pod) error {
proxyCmd := hyperstartProxyCmd{
cmd: hyperstart.DestroyPod,
message: nil,
}

if _, err := h.proxy.sendCmd(proxyCmd); err != nil {
return err
}

return nil
}

Expand Down
13 changes: 5 additions & 8 deletions pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,10 +581,6 @@ func createPod(podConfig PodConfig) (*Pod, error) {
return nil, err
}

if err := p.storage.storeHypervisorState(p.id, p.hypervisor.getState()); err != nil {
return nil, err
}

return p, nil
}

Expand Down Expand Up @@ -629,10 +625,6 @@ func doFetchPod(podConfig PodConfig) (*Pod, error) {
wg: &sync.WaitGroup{},
}

if err := p.hypervisor.init(p); err != nil {
return nil, err
}

containers, err := newContainers(p, podConfig.Containers)
if err != nil {
return nil, err
Expand All @@ -644,6 +636,11 @@ func doFetchPod(podConfig PodConfig) (*Pod, error) {
return nil, err
}

if err := p.hypervisor.init(p); err != nil {
p.storage.deletePodResources(p.id, nil)
return nil, err
}

if err := p.hypervisor.createPod(podConfig); err != nil {
p.storage.deletePodResources(p.id, nil)
return nil, err
Expand Down
103 changes: 55 additions & 48 deletions qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
Expand All @@ -32,16 +31,16 @@ import (
)

type qmpChannel struct {
ctx context.Context
path string
disconnectCh chan struct{}
wg sync.WaitGroup
qmp *ciaoQemu.QMP
ctx context.Context
path string
wg sync.WaitGroup
qmp *ciaoQemu.QMP
}

// QemuState keeps Qemu's state
type QemuState struct {
Bridges []Bridge
UUID string
}

// qemu is an Hypervisor interface implementation for the Linux qemu hypervisor.
Expand Down Expand Up @@ -83,6 +82,8 @@ const (

const qmpCapErrMsg = "Failed to negoatiate QMP capabilities"

const qmpSockPathSizeLimit = 107

// Mapping between machine types and QEMU binary paths.
var qemuPaths = map[string]string{
QemuPCLite: "/usr/bin/qemu-lite-system-x86_64",
Expand Down Expand Up @@ -471,25 +472,6 @@ func (q *qemu) appendImage(devices []ciaoQemu.Device, podConfig PodConfig) ([]ci
return devices, nil
}

func (q *qemu) forceUUIDFormat(str string) string {
re := regexp.MustCompile(`[^[0-9,a-f,A-F]]*`)
hexStr := re.ReplaceAllLiteralString(str, ``)

slice := []byte(hexStr)
sliceLen := len(slice)

var uuidSlice uuid.UUID
uuidLen := len(uuidSlice)

if sliceLen > uuidLen {
copy(uuidSlice[:], slice[:uuidLen])
} else {
copy(uuidSlice[:], slice)
}

return uuidSlice.String()
}

func (q *qemu) getMachine(name string) (ciaoQemu.Machine, error) {
for _, m := range supportedQemuMachines {
if m.Type == name {
Expand Down Expand Up @@ -543,16 +525,23 @@ func (q *qemu) init(pod *Pod) error {
q.config = pod.config.HypervisorConfig
q.pod = pod

if err = pod.storage.fetchHypervisorState(pod.id, &q.state); err != nil {
if err := pod.storage.fetchHypervisorState(pod.id, &q.state); err != nil {
q.Logger().Debug("Creating bridges")
q.state.Bridges = NewBridges(q.config.DefaultBridges, q.config.HypervisorMachineType)

q.Logger().Debug("Creating UUID")
q.state.UUID = uuid.Generate().String()

if err := pod.storage.storeHypervisorState(pod.id, q.state); err != nil {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep the hypervisor state storage either in pod.go , after the hypervisor.init() call.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this was done on purpose since I think it should be handled from the hypervisor implementation itself. Indeed, this state file is specific to every hypervisor implementation so that they can store some specific information. But let's say we have other hypervisor implementation that don't need to store/fetch anything, this means we would force them to store nothing.
I agree we would avoid code duplication if storing from pod.go, but in that case I think we should do everything from container/pod code. Or we go with everything controlled from the hypervisor implementation. The huge benefit with having this controlled from the hypervisor implementation is that we don't need to make assumption about when we should fetch/store from the pod/container.
I think we can take this change as is, and if you think we should move the entire fetch/store out of the qemu implementation because we can find a common pattern for all hypervisors about storage, then this should be done in a follow up PR.
Let me know what you think !

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, I'm good with that.

return err
}
}

if err = q.buildPath(); err != nil {
if err := q.buildPath(); err != nil {
return err
}

if err = q.buildKernelParams(q.config); err != nil {
if err := q.buildKernelParams(q.config); err != nil {
return err
}

Expand Down Expand Up @@ -614,6 +603,23 @@ func (q *qemu) setMemoryResources(podConfig PodConfig) (ciaoQemu.Memory, error)
return memory, nil
}

func (q *qemu) qmpSocketPath(socketName string) (string, error) {
parentDirPath := filepath.Join(runStoragePath, q.pod.id)
if len(parentDirPath) > qmpSockPathSizeLimit {
return "", fmt.Errorf("Parent directory path %q is too long "+
"(%d characters), could not add any path for the QMP socket",
parentDirPath, len(parentDirPath))
}

path := fmt.Sprintf("%s/%s-%s", parentDirPath, q.state.UUID, socketName)

if len(path) > qmpSockPathSizeLimit {
return path[:qmpSockPathSizeLimit], nil
}

return path, nil
}

// createPod is the Hypervisor pod creation implementation for ciaoQemu.
func (q *qemu) createPod(podConfig PodConfig) error {
var devices []ciaoQemu.Device
Expand Down Expand Up @@ -669,14 +675,28 @@ func (q *qemu) createPod(podConfig PodConfig) error {
DriftFix: "slew",
}

if q.state.UUID == "" {
return fmt.Errorf("UUID should not be empty")
}

monitorSockPath, err := q.qmpSocketPath(monitorSocket)
if err != nil {
return err
}

q.qmpMonitorCh = qmpChannel{
ctx: context.Background(),
path: fmt.Sprintf("%s/%s/%s", runStoragePath, podConfig.ID, monitorSocket),
path: monitorSockPath,
}

controlSockPath, err := q.qmpSocketPath(controlSocket)
if err != nil {
return err
}

q.qmpControlCh = qmpChannel{
ctx: context.Background(),
path: fmt.Sprintf("%s/%s/%s", runStoragePath, podConfig.ID, controlSocket),
path: controlSockPath,
}

qmpSockets := []ciaoQemu.QMPSocket{
Expand Down Expand Up @@ -718,7 +738,7 @@ func (q *qemu) createPod(podConfig PodConfig) error {

qemuConfig := ciaoQemu.Config{
Name: fmt.Sprintf("pod-%s", podConfig.ID),
UUID: q.forceUUIDFormat(podConfig.ID),
UUID: q.state.UUID,
Path: q.path,
Ctx: q.qmpMonitorCh.ctx,
Machine: machine,
Expand Down Expand Up @@ -762,7 +782,6 @@ func (q *qemu) waitPod(timeout int) error {
return fmt.Errorf("Invalid timeout %ds", timeout)
}

disconnectCh := make(chan struct{})
cfg := ciaoQemu.QMPConfig{Logger: newQMPLogger()}

var qmp *ciaoQemu.QMP
Expand All @@ -771,6 +790,7 @@ func (q *qemu) waitPod(timeout int) error {

timeStart := time.Now()
for {
disconnectCh := make(chan struct{})
qmp, ver, err = ciaoQemu.QMPStart(q.qmpMonitorCh.ctx, q.qmpMonitorCh.path, cfg, disconnectCh)
if err == nil {
break
Expand Down Expand Up @@ -803,11 +823,10 @@ func (q *qemu) waitPod(timeout int) error {
// stopPod will stop the Pod's VM.
func (q *qemu) stopPod() error {
cfg := ciaoQemu.QMPConfig{Logger: newQMPLogger()}
q.qmpControlCh.disconnectCh = make(chan struct{})
const timeout = time.Duration(10) * time.Second
disconnectCh := make(chan struct{})

q.Logger().Info("Stopping Pod")
qmp, _, err := ciaoQemu.QMPStart(q.qmpControlCh.ctx, q.qmpControlCh.path, cfg, q.qmpControlCh.disconnectCh)
qmp, _, err := ciaoQemu.QMPStart(q.qmpControlCh.ctx, q.qmpControlCh.path, cfg, disconnectCh)
if err != nil {
q.Logger().WithError(err).Error("Failed to connect to QEMU instance")
return err
Expand All @@ -819,19 +838,7 @@ func (q *qemu) stopPod() error {
return err
}

if err := qmp.ExecuteQuit(q.qmpMonitorCh.ctx); err != nil {
return err
}

// Wait for the VM disconnection notification
select {
case <-q.qmpControlCh.disconnectCh:
break
case <-time.After(timeout):
return fmt.Errorf("Did not receive the VM disconnection notification (timeout %ds)", timeout)
}

return nil
return qmp.ExecuteQuit(q.qmpMonitorCh.ctx)
}

func (q *qemu) togglePausePod(pause bool) error {
Expand Down
36 changes: 34 additions & 2 deletions qemu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,17 @@ func TestQemuInit(t *testing.T) {
},
}

err := q.init(pod)
if err != nil {
// Create parent dir path for hypervisor.json
parentDir := filepath.Join(runStoragePath, pod.id)
if err := os.MkdirAll(parentDir, dirMode); err != nil {
t.Fatalf("Could not create parent directory %s: %v", parentDir, err)
}

if err := q.init(pod); err != nil {
t.Fatal(err)
}

if err := os.RemoveAll(parentDir); err != nil {
t.Fatal(err)
}

Expand All @@ -405,6 +414,29 @@ func TestQemuInit(t *testing.T) {
}
}

func TestQemuInitMissingParentDirFail(t *testing.T) {
qemuConfig := newQemuConfig()
q := &qemu{}

pod := &Pod{
id: "testPod",
storage: &filesystem{},
config: &PodConfig{
HypervisorConfig: qemuConfig,
},
}

// Ensure parent dir path for hypervisor.json does not exist.
parentDir := filepath.Join(runStoragePath, pod.id)
if err := os.RemoveAll(parentDir); err != nil {
t.Fatal(err)
}

if err := q.init(pod); err == nil {
t.Fatal("Qemu init() expected to fail because of missing parent directory for storage")
}
}

func TestQemuSetCPUResources(t *testing.T) {
vcpus := 1

Expand Down