Skip to content

Commit

Permalink
initial qmp && local volume hotplug (#1509)
Browse files Browse the repository at this point in the history
* qmp for local volume hotplug

* .

* .
  • Loading branch information
eyberg authored Aug 17, 2023
1 parent cecc103 commit 42ce438
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 29 deletions.
55 changes: 50 additions & 5 deletions cmd/cmd_volume_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package cmd_test

import (
"os"
"testing"
"time"

"github.com/nanovms/ops/testutils"
"github.com/nanovms/ops/types"

"github.com/nanovms/ops/cmd"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -38,35 +43,75 @@ func TestDeleteVolumeCommand(t *testing.T) {
}

func TestAttachVolumeCommand(t *testing.T) {
imageName := buildImage("test")
imageName := buildWaitImage("test")
defer removeImage(imageName)
instanceName := buildInstance(imageName)

configFileName := "test-" + testutils.String(5) + ".json"
expected := &types.Config{
RunConfig: types.RunConfig{
QMP: true,
},
}

writeConfigToFile(expected, configFileName)
defer os.Remove(configFileName)

instanceName := buildInstanceWithConfig(imageName, configFileName)
defer removeInstance(instanceName)

volumeName := buildVolume("vol-test")
defer removeVolume(volumeName)

attachVolumeCmd := cmd.VolumeCommands()

attachVolumeCmd.SetArgs([]string{"attach", instanceName, volumeName, "does not matter"})

// FIXME: tests prob. should not be spawning
time.Sleep(1 * time.Second)

err := attachVolumeCmd.Execute()

assert.Nil(t, err)
}

func TestDetachVolumeCommand(t *testing.T) {
imageName := buildImage("test")
imageName := buildWaitImage("test")
defer removeImage(imageName)
instanceName := buildInstance(imageName)

configFileName := "test-" + testutils.String(5) + ".json"
expected := &types.Config{
RunConfig: types.RunConfig{
QMP: true,
},
}

writeConfigToFile(expected, configFileName)
defer os.Remove(configFileName)

instanceName := buildInstanceWithConfig(imageName, configFileName)
defer removeInstance(instanceName)

volumeName := buildVolume("vol-test")
defer removeVolume(volumeName)

attachVolumeCmd := cmd.VolumeCommands()

attachVolumeCmd.SetArgs([]string{"attach", instanceName, volumeName, "does not matter"})

// FIXME: tests prob. should not be spawning
time.Sleep(1 * time.Second)

err := attachVolumeCmd.Execute()

assert.Nil(t, err)

//

detachVolumeCmd := cmd.VolumeCommands()

detachVolumeCmd.SetArgs([]string{"detach", instanceName, volumeName})

err := detachVolumeCmd.Execute()
err = detachVolumeCmd.Execute()

assert.Nil(t, err)
}
36 changes: 35 additions & 1 deletion cmd/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package cmd_test
import (
"encoding/json"
"os"
"strconv"
"testing"
"time"

"github.com/nanovms/ops/testutils"
"github.com/nanovms/ops/types"
Expand Down Expand Up @@ -49,8 +51,24 @@ func buildImage(imageName string) string {
return imageName
}

func buildWaitImage(imageName string) string {
imageName += testutils.String(5)
basicWaitProgram := testutils.BuildWaitProgram()
defer os.Remove(basicWaitProgram)

createImageCmd := cmd.ImageCommands()

createImageCmd.SetArgs([]string{"create", basicWaitProgram, "-i", imageName})

createImageCmd.Execute()

return imageName
}

func buildInstance(imageName string) string {
instanceName := imageName + testutils.String(5)
tm := strconv.FormatInt(time.Now().Unix(), 10)
instanceName := imageName + "-" + tm

createInstanceCmd := cmd.InstanceCommands()

createInstanceCmd.SetArgs([]string{"create", imageName, "--instance-name", instanceName})
Expand All @@ -63,6 +81,22 @@ func buildInstance(imageName string) string {
return instanceName
}

func buildInstanceWithConfig(imageName string, config string) string {
tm := strconv.FormatInt(time.Now().Unix(), 10)
instanceName := imageName + "-" + tm

createInstanceCmd := cmd.InstanceCommands()

createInstanceCmd.SetArgs([]string{"create", imageName, "--instance-name", instanceName, "-c", config})

err := createInstanceCmd.Execute()
if err != nil {
log.Error(err)
}

return instanceName
}

func removeInstance(instanceName string) {
createInstanceCmd := cmd.InstanceCommands()

Expand Down
106 changes: 101 additions & 5 deletions provider/onprem/onprem_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package onprem

import (
"fmt"
"net"
"os"
"path"
"strconv"
Expand Down Expand Up @@ -65,17 +66,112 @@ func (op *OnPrem) DeleteVolume(ctx *lepton.Context, name string) error {
// on `ops image create --mount`, it simply creates a mount path
// with the given volume label
// label can refer to volume UUID or volume label
//
// You must start the instance with QMP otherwise this won't work.
// {
// "RunConfig": {
// "QMP": true
// }
// }
func (op *OnPrem) AttachVolume(ctx *lepton.Context, image, name string, attachID int) error {
log.Warn("not implemented")
fmt.Println("use <ops run> or <ops pkg load> with --mounts flag instead")
fmt.Println("alternatively, use <ops image create -t onprem> with --mounts flag")
fmt.Println("and run it with <ops instance create -t onprem>")
vol := ""

buildDir := ctx.Config().VolumesDir
vols, err := GetVolumes(buildDir, nil)
if err != nil {
fmt.Println(err)
}

for i := 0; i < len(vols); i++ {
if vols[i].Name == name {
vol = vols[i].Path
}
}

last := getMgmtPort(image)

commands := []string{
`{ "execute": "qmp_capabilities" }`,
`{ "execute": "blockdev-add", "arguments": {"driver": "raw", "node-name":"` + name + `", "file": {"driver": "file", "filename": "` + vol + `"} } }`,
`{ "execute": "device_add", "arguments": {"driver": "scsi-hd", "bus": "scsi0.0", "drive": "` + name + `", "id": "` + name + `"}}`,
}

c, err := net.Dial("tcp", "localhost:"+last)
if err != nil {
fmt.Println(err)
}
defer c.Close()

for i := 0; i < len(commands); i++ {
_, err := c.Write([]byte(commands[i] + "\n"))
if err != nil {
fmt.Println(err)
}
received := make([]byte, 1024)
_, err = c.Read(received)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
return nil
}

// set to 4 + last 4 of instanceid
// we don't want 0 and needs to be less than 65k
// could still have collisions regardless; punting
func getMgmtPort(image string) string {
ts := strings.Split(image, "-")[1]
return "4" + ts[len(ts)-4:]
}

// DetachVolume detaches volume
func (op *OnPrem) DetachVolume(ctx *lepton.Context, image, name string) error {
log.Warn("not implemented")
vol := ""

buildDir := ctx.Config().VolumesDir
vols, err := GetVolumes(buildDir, nil)
if err != nil {
fmt.Println(err)
}

for i := 0; i < len(vols); i++ {
if vols[i].Name == name {
vol = vols[i].Path
}
}

if vol != "" {
fmt.Printf("removing %s\n", vol)
}

last := getMgmtPort(image)

commands := []string{
`{ "execute": "qmp_capabilities" }`,
`{ "execute": "device_del", "arguments": {"id": "` + name + `"}}`,
`{ "execute": "blockdev-del", "arguments": {"node-name":"` + name + `" } }`,
}

c, err := net.Dial("tcp", "localhost:"+last)
if err != nil {
fmt.Println(err)
}
defer c.Close()

for i := 0; i < len(commands); i++ {
_, err := c.Write([]byte(commands[i] + "\n"))
if err != nil {
fmt.Println(err)
}
received := make([]byte, 1024)
_, err = c.Read(received)
if err != nil {
println("Read data failed:", err.Error())
os.Exit(1)
}
}

return nil
}

Expand Down
4 changes: 2 additions & 2 deletions provider/upcloud/upcloud_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (p *Provider) CreateImage(ctx *lepton.Context, imagePath string) error {
return err
}

ctx.Logger().Debug("%+v", templateDetails)
ctx.Logger().Debugf("%+v", templateDetails)

err = p.waitForStorageState(storageDetails.UUID, "online")
if err != nil {
Expand Down Expand Up @@ -115,7 +115,7 @@ func (p *Provider) GetImages(ctx *lepton.Context) (images []lepton.CloudImage, e
return
}

ctx.Logger().Debug("%+v", templates)
ctx.Logger().Debugf("%+v", templates)

for _, s := range templates.Storages {
images = append(images, *p.parseStorageToCloudImage(&s))
Expand Down
20 changes: 10 additions & 10 deletions provider/upcloud/upcloud_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) error {
return err
}

ctx.Logger().Debug("%+v", serverDetails)
ctx.Logger().Debugf("%+v", serverDetails)

ctx.Logger().Info("getting ops tags")
opsTag, err := p.findOrCreateTag(opsTag)
if err != nil {
ctx.Logger().Warn("failed creating ops tag: %s", err)
ctx.Logger().Warnf("failed creating ops tag: %s", err)
return nil
}

Expand All @@ -69,7 +69,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) error {
Description: "Creted with image " + image.Name,
})
if err != nil {
ctx.Logger().Warn("failed creating image tag: %s", err)
ctx.Logger().Warnf("failed creating image tag: %s", err)
return nil
}

Expand All @@ -82,7 +82,7 @@ func (p *Provider) CreateInstance(ctx *lepton.Context) error {

_, err = p.upcloud.TagServer(context.Background(), assignOpsTagsRequest)
if err != nil {
ctx.Logger().Warn("failed assigning ops tags: %s", err)
ctx.Logger().Warnf("failed assigning ops tags: %s", err)
return nil
}

Expand Down Expand Up @@ -133,7 +133,7 @@ func (p *Provider) GetInstances(ctx *lepton.Context) (instances []lepton.CloudIn

opsTag, err := p.findOrCreateTag(opsTag)
if err != nil {
ctx.Logger().Warn("failed creating tags: %s", err)
ctx.Logger().Warnf("failed creating tags: %s", err)

var servers *upcloud.Servers
servers, err = p.upcloud.GetServers(context.Background())
Expand Down Expand Up @@ -191,7 +191,7 @@ func (p *Provider) DeleteInstance(ctx *lepton.Context, instancename string) (err
if instance.Status != "stopped" {
err = p.stopServer(instance.ID)
if err != nil {
ctx.Logger().Warn("failed stopping server: %s", err)
ctx.Logger().Warnf("failed stopping server: %s", err)
}

err = p.waitForServerState(instance.ID, "stopped")
Expand All @@ -204,7 +204,7 @@ func (p *Provider) DeleteInstance(ctx *lepton.Context, instancename string) (err
UUID: instance.ID,
}

ctx.Logger().Debug(`deleting server with uuid "%s"`, instance.ID)
ctx.Logger().Debugf(`deleting server with uuid "%s"`, instance.ID)
err = p.upcloud.DeleteServer(context.Background(), deleteServerReq)

return
Expand All @@ -217,7 +217,7 @@ func (p *Provider) StopInstance(ctx *lepton.Context, instancename string) (err e
return
}

ctx.Logger().Debug(`stopping server with uuid "%s"`, instance.ID)
ctx.Logger().Debugf(`stopping server with uuid "%s"`, instance.ID)
err = p.stopServer(instance.ID)

return
Expand All @@ -240,7 +240,7 @@ func (p *Provider) StartInstance(ctx *lepton.Context, instancename string) (err
return
}

ctx.Logger().Debug(`starting server with uuid "%s"`, instance.ID)
ctx.Logger().Debugf(`starting server with uuid "%s"`, instance.ID)

err = p.startServer(instance.ID)

Expand All @@ -259,7 +259,7 @@ func (p *Provider) startServer(uuid string) (err error) {

// GetInstanceByName returns upcloud instance with given name
func (p *Provider) GetInstanceByName(ctx *lepton.Context, name string) (instance *lepton.CloudInstance, err error) {
ctx.Logger().Debug(`getting instance by name "%s"`, name)
ctx.Logger().Debugf(`getting instance by name "%s"`, name)
server, err := p.getServerByName(ctx, name)
if err != nil {
return
Expand Down
Loading

0 comments on commit 42ce438

Please sign in to comment.