add codes
This commit is contained in:
@@ -90,13 +90,13 @@ func handleCreateVM(cfg config.Config, svc Services) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Resolve disk paths for dir pools
|
// Resolve disk paths for dir pools
|
||||||
for i := range spec.Disks {
|
for i := range spec.Disks {
|
||||||
if spec.Disks[i].Path == "" && spec.Disks[i].Pool != "" {
|
if spec.Disks[i].Path == "" && spec.Disks[i].Pool != "" {
|
||||||
if path, err := storage.ResolveVolume(toPoolConfigs(cfg.StoragePools), spec.Disks[i].Pool, spec.Disks[i].Name+".qcow2"); err == nil {
|
if path, err := storage.ResolveVolume(toPoolConfigs(cfg.StoragePools), spec.Disks[i].Pool, spec.Disks[i].Name+".qcow2"); err == nil {
|
||||||
spec.Disks[i].Path = path
|
spec.Disks[i].Path = path
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err := svc.Store.SaveVM(spec); err != nil {
|
if err := svc.Store.SaveVM(spec); err != nil {
|
||||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -185,6 +185,37 @@ func TestVMLifecycleStartStop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCTLifecycleStartStop(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
store := state.NewStore(filepath.Join(tmpDir, "vm"), filepath.Join(tmpDir, "ct"))
|
||||||
|
cfg := config.Config{
|
||||||
|
StoragePools: []config.StoragePool{{Name: "local"}},
|
||||||
|
Bridges: []config.Bridge{{Name: "vmbr0"}},
|
||||||
|
}
|
||||||
|
t.Setenv("JAGACLOUD_SKIP_BRIDGE_CHECK", "1")
|
||||||
|
svc := Services{
|
||||||
|
Tasks: tasks.NewRegistry(),
|
||||||
|
Libvirt: &fakeLibvirt{},
|
||||||
|
LXC: &fakeLXC{},
|
||||||
|
Podman: &fakePodman{},
|
||||||
|
Store: store,
|
||||||
|
}
|
||||||
|
go svc.Tasks.StartWorker(testCtx(t))
|
||||||
|
r := chi.NewRouter()
|
||||||
|
RegisterRoutes(r, cfg, svc)
|
||||||
|
|
||||||
|
_ = store.SaveCT(lxc.Spec{ID: "ct-run", Name: "ct-run", Template: "debian", RootfsPool: "local", RootfsSizeG: 1, Limits: lxc.Limits{CPU: 1, MemoryMB: 256}})
|
||||||
|
|
||||||
|
for _, action := range []string{"start", "stop"} {
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/containers/ct-run/"+action, nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusAccepted {
|
||||||
|
t.Fatalf("expected 202 for %s, got %d", action, rec.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCTLifecycleDeleteCleansSpec(t *testing.T) {
|
func TestCTLifecycleDeleteCleansSpec(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
store := state.NewStore(filepath.Join(tmpDir, "vm"), filepath.Join(tmpDir, "ct"))
|
store := state.NewStore(filepath.Join(tmpDir, "vm"), filepath.Join(tmpDir, "ct"))
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PoolConfig describes a configured storage pool.
|
// PoolConfig describes a configured storage pool.
|
||||||
@@ -27,7 +30,6 @@ func ResolveVolume(pools []PoolConfig, poolName, vol string) (string, error) {
|
|||||||
if p.Name != poolName {
|
if p.Name != poolName {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// dir pool: join path/vol
|
|
||||||
if p.Type == "dir" {
|
if p.Type == "dir" {
|
||||||
if p.Path == "" {
|
if p.Path == "" {
|
||||||
return "", fmt.Errorf("dir pool %s missing path", poolName)
|
return "", fmt.Errorf("dir pool %s missing path", poolName)
|
||||||
@@ -35,8 +37,13 @@ func ResolveVolume(pools []PoolConfig, poolName, vol string) (string, error) {
|
|||||||
target := filepath.Join(p.Path, vol)
|
target := filepath.Join(p.Path, vol)
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
// TODO: lvm/zfs support
|
if p.Type == "lvm" {
|
||||||
return "", fmt.Errorf("pool type %s not yet supported", p.Type)
|
return fmt.Sprintf("/dev/%s/%s", p.VG, vol), nil
|
||||||
|
}
|
||||||
|
if p.Type == "zfs" {
|
||||||
|
return fmt.Sprintf("%s/%s", p.Path, vol), nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("pool type %s not supported", p.Type)
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("pool %s not found", poolName)
|
return "", fmt.Errorf("pool %s not found", poolName)
|
||||||
}
|
}
|
||||||
@@ -56,3 +63,40 @@ func PoolExists(pools []PoolConfig, name string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateVolume creates a volume in the given pool (dir or LVM). ZFS is stubbed.
|
||||||
|
func CreateVolume(pool PoolConfig, name string, sizeGB int) (string, error) {
|
||||||
|
if name == "" {
|
||||||
|
return "", errors.New("volume name required")
|
||||||
|
}
|
||||||
|
switch pool.Type {
|
||||||
|
case "dir":
|
||||||
|
if pool.Path == "" {
|
||||||
|
return "", fmt.Errorf("dir pool %s missing path", pool.Name)
|
||||||
|
}
|
||||||
|
target := filepath.Join(pool.Path, name)
|
||||||
|
if _, err := os.Stat(target); err == nil {
|
||||||
|
return target, nil
|
||||||
|
}
|
||||||
|
cmd := exec.Command("qemu-img", "create", "-f", "qcow2", target, fmt.Sprintf("%dG", sizeGB))
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return target, nil
|
||||||
|
case "lvm":
|
||||||
|
if pool.VG == "" {
|
||||||
|
return "", fmt.Errorf("lvm pool %s missing vg", pool.Name)
|
||||||
|
}
|
||||||
|
target := fmt.Sprintf("/dev/%s/%s", pool.VG, name)
|
||||||
|
args := []string{"lvcreate", "-L", fmt.Sprintf("%dG", sizeGB), "-n", name, pool.VG}
|
||||||
|
if err := exec.Command("bash", "-lc", strings.Join(args, " ")).Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return target, nil
|
||||||
|
case "zfs":
|
||||||
|
// Stub: in practice use `zfs create -V <size> pool/vol`
|
||||||
|
return "", fmt.Errorf("zfs volume creation not implemented")
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported pool type %s", pool.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user