Merge pull request #13 in ~WANYAOQI/onecloud from feature/wyq/host-server-v2 to feature/host-baremetal-v2

* commit 'be7f8f1e95d8156e29abc601ce03aaf58ad71139':
  host complie success
  temp commit
This commit is contained in:
李泽玺
2019-01-16 12:15:04 +08:00
32 changed files with 1420 additions and 316 deletions
-1
View File
@@ -102,7 +102,6 @@ func (f *SLocalGuestFS) Cleandir(dir string, keepdir, caseInsensitive bool) erro
return fmt.Errorf("No such file %s", sPath)
}
// TODO
func (f *SLocalGuestFS) Zerofiles(dir string, caseInsensitive bool) error {
sPath := f.getLocalPath(dir, caseInsensitive)
if len(sPath) > 0 {
+23 -23
View File
@@ -141,7 +141,7 @@ func guestSync(ctx context.Context, sid string, body jsonutils.JSONObject) (inte
if !guestManger.IsGuestExist(sid) {
return nil, httperrors.NewNotFoundError("Guest %s not found", sid)
}
hostutils.DelayTask(ctx, guestManger.GuestSync, &SGuestSync{sid, body})
hostutils.DelayTask(ctx, guestManger.GuestSync, &SBaseParms{sid, body})
return nil, nil
}
@@ -262,13 +262,13 @@ func guestResume(ctx context.Context, sid string, body jsonutils.JSONObject) (in
return nil, nil
}
func guestStartNbdServer(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) {
if !guestManger.IsGuestExist(sid) {
return nil, httperrors.NewNotFoundError("Guest %s not found", sid)
}
hostutils.DelayTask(ctx, guestManger.StartNbdServer, sid)
return nil, nil
}
// func guestStartNbdServer(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) {
// if !guestManger.IsGuestExist(sid) {
// return nil, httperrors.NewNotFoundError("Guest %s not found", sid)
// }
// hostutils.DelayTask(ctx, guestManger.StartNbdServer, sid)
// return nil, nil
// }
func guestDriveMirror(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) {
if !guestManger.IsGuestExist(sid) {
@@ -278,7 +278,7 @@ func guestDriveMirror(ctx context.Context, sid string, body jsonutils.JSONObject
if err != nil {
return nil, httperrors.NewMissingParameterError("backup_ndb_server_uri")
}
hostutils.DelayTask(ctx, guestManger.StartDriveMirror,
hostutils.DelayTaskWithoutReqctx(ctx, guestManger.StartDriveMirror,
&SDriverMirror{sid, backupNbdServerUri})
return nil, nil
}
@@ -295,11 +295,11 @@ func guestReloadDiskSnapshot(ctx context.Context, sid string, body jsonutils.JSO
var disk storageman.IDisk
guest := guestManger.Servers[sid]
disks, _ := guest.Desc.GetArray("disks")
for _, disk := range disks {
id, _ := disk.GetString("disk_id")
for _, d := range disks {
id, _ := d.GetString("disk_id")
if diskId == id {
diskPath, _ := disk.GetString("path")
disk := storageman.GetManager().GetDiskByPath(diskPath)
diskPath, _ := d.GetString("path")
disk = storageman.GetManager().GetDiskByPath(diskPath)
break
}
}
@@ -327,11 +327,11 @@ func guestSnapshot(ctx context.Context, sid string, body jsonutils.JSONObject) (
var disk storageman.IDisk
guest := guestManger.Servers[sid]
disks, _ := guest.Desc.GetArray("disks")
for _, disk := range disks {
id, _ := disk.GetString("disk_id")
for _, d := range disks {
id, _ := d.GetString("disk_id")
if diskId == id {
diskPath, _ := disk.GetString("path")
disk := storageman.GetManager().GetDiskByPath(diskPath)
diskPath, _ := d.GetString("path")
disk = storageman.GetManager().GetDiskByPath(diskPath)
break
}
}
@@ -356,11 +356,11 @@ func guestDeleteSnapshot(ctx context.Context, sid string, body jsonutils.JSONObj
var disk storageman.IDisk
guest := guestManger.Servers[sid]
disks, _ := guest.Desc.GetArray("disks")
for _, disk := range disks {
id, _ := disk.GetString("disk_id")
for _, d := range disks {
id, _ := d.GetString("disk_id")
if diskId == id {
diskPath, _ := disk.GetString("path")
disk := storageman.GetManager().GetDiskByPath(diskPath)
diskPath, _ := d.GetString("path")
disk = storageman.GetManager().GetDiskByPath(diskPath)
break
}
}
@@ -409,6 +409,6 @@ var actionFuncs = map[string]actionFunc{
"dest-prepare-migrate": guestDestPrepareMigrate,
"live-migrate": guestLiveMigrate,
"resume": guestResume,
"start-nbd-server": guestStartNbdServer,
"drive-mirror": guestDriveMirror,
// "start-nbd-server": guestStartNbdServer,
"drive-mirror": guestDriveMirror,
}
+2 -1
View File
@@ -11,7 +11,8 @@ type SBaseParms struct {
}
type SGuestDeploy struct {
SGuestBaseParms
Sid string
Body jsonutils.JSONObject
IsInit bool
}
+91 -37
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"os/exec"
"path"
@@ -20,6 +19,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/sshkeys"
"yunion.io/x/onecloud/pkg/hostman/guestfs"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/storageman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient/modules"
@@ -29,9 +29,12 @@ import (
"yunion.io/x/onecloud/pkg/util/timeutils2"
)
const VNC_PORT_BASE = 5900
const (
VNC_PORT_BASE = 5900
)
type SGuestManager struct {
host hostutils.IHost
ServersPath string
Servers map[string]*SKVMGuestInstance
CandidateServers map[string]*SKVMGuestInstance
@@ -40,8 +43,9 @@ type SGuestManager struct {
isLoaded bool
}
func NewGuestManager(serversPath string) *SGuestManager {
func NewGuestManager(host hostutils.IHost, serversPath string) *SGuestManager {
manager := &SGuestManager{}
manager.host = host
manager.ServersPath = serversPath
manager.Servers = make(map[string]*SKVMGuestInstance, 0)
manager.CandidateServers = make(map[string]*SKVMGuestInstance, 0)
@@ -66,14 +70,13 @@ func (m *SGuestManager) Bootstrap() {
}
func (m *SGuestManager) VerifyExistingGuests(pendingDelete bool) {
params := url.Values{
"limit": {"0"},
"admin": {"True"},
"system": {"True"},
"pending_delete": {fmt.Sprintf("%s", pendingDelete)},
}
// TODO get host id
params.Set("filter.0", fmt.Sprintf("host_id.equals(%s)", "get host id "))
params := jsonutils.NewDict()
params.Set("limit", jsonutils.NewInt(0))
params.Set("admin", jsonutils.JSONTrue)
params.Set("system", jsonutils.JSONTrue)
params.Set("pending_delete", jsonutils.NewBool(pendingDelete))
params.Set("filter.0", jsonutils.NewString(
fmt.Sprintf("host_id.equals(%s)", m.host.GetHostId())))
if len(m.CandidateServers) > 0 {
keys := make([]string, len(m.CandidateServers))
var index = 0
@@ -81,9 +84,9 @@ func (m *SGuestManager) VerifyExistingGuests(pendingDelete bool) {
keys[index] = k
index++
}
params.Set("filter.1", strings.Join(keys, ","))
params.Set("filter.1", jsonutils.NewString(strings.Join(keys, ",")))
}
res, err := modules.Servers.List(hostutils.GetComputeSession(context.Background()), id, params)
res, err := modules.Servers.List(hostutils.GetComputeSession(context.Background()), params)
if err != nil {
m.OnVerifyExistingGuestsFail(err, pendingDelete)
} else {
@@ -130,7 +133,19 @@ func (m *SGuestManager) RemoveCandidateServer(server *SKVMGuestInstance) {
}
func (m *SGuestManager) OnLoadExistingGuestsComplete() {
log.Infof("Load existing guests complete...")
err := m.host.PutHostOnline()
if err != nil {
log.Errorln(err)
}
// TODO
// hostmetrics.Init()
if !options.HostOptions.EnableCpuBinding {
// TODO
// m.cleanupCpuset()
}
}
func (m *SGuestManager) StartCpusetBalancer() {
@@ -260,8 +275,7 @@ func (m *SGuestManager) GuestDeploy(ctx context.Context, params interface{}) (js
password = seclib.RandomPassword(12)
}
guestInfo, err := guest.DeployFs(&guestfs.SDeployInfo{
publicKey, deploys, password, deployParams.IsInit})
guestInfo, err := guest.DeployFs(guestfs.NewDeployInfo(publicKey, deploys, password, deployParams.IsInit, false))
if err != nil {
log.Errorf("Deploy guest fs error: %s", err)
return nil, err
@@ -269,13 +283,14 @@ func (m *SGuestManager) GuestDeploy(ctx context.Context, params interface{}) (js
return guestInfo, nil
}
} else {
return nil, fmt.Errorf("Guest %s not found", sid)
return nil, fmt.Errorf("Guest %s not found", deployParams.Sid)
}
}
// delay cpuset balance
func (m *SGuestManager) CpusetBalance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
// TODO
return nil, fmt.Errorf("not implement")
}
func (m *SGuestManager) Status(sid string) string {
@@ -313,11 +328,8 @@ func (m *SGuestManager) GuestStart(ctx context.Context, sid string, body jsonuti
}
if guest.IsStopped() {
params, _ := body.Get("params")
if err := guest.StartGuest(ctx, params); err != nil {
return nil, httperrors.NewBadRequestError("Failed to start server")
} else {
return jsonutils.NewDict(jsonutils.JSONPair{"vnc_port", jsonutils.NewInt(0)}), nil
}
guest.StartGuest(ctx, params)
return jsonutils.NewDict(jsonutils.NewPair("vnc_port", jsonutils.NewInt(0))), nil
} else {
vncPort := guest.GetVncPort()
if vncPort > 0 {
@@ -373,9 +385,14 @@ func (m *SGuestManager) SrcPrepareMigrate(ctx context.Context, params interface{
return nil, hostutils.ParamsError
}
guest := m.Servers[migParams.Sid]
disksPrepare := guest.PrepareMigrate(migParams.LiveMigrate)
if len(disksPrepare) > 0 {
return map[string]interface{}{"disks_back": disksPrepare}, nil
disksPrepare, err := guest.PrepareMigrate(migParams.LiveMigrate)
if err != nil {
return nil, err
}
if disksPrepare.Length() > 0 {
ret := jsonutils.NewDict()
ret.Set("disks_back", disksPrepare)
return ret, nil
}
return nil, nil
}
@@ -387,7 +404,7 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
}
guest := m.Servers[migParams.Sid]
if err := guest.CreateFromDesc(desc); err != nil {
if err := guest.CreateFromDesc(migParams.Desc); err != nil {
return nil, err
}
@@ -424,9 +441,10 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
// create snapshots form remote url
diskStorageId, _ := diskinfo.GetString("storage_id")
for _, snapshotId := range snapshots {
snapshotUrl := path.Join(migParams.SnapshotsUri, diskStorageId, diskId, snapshotId)
snapshotPath := path.Join(disk.GetSnapshotDir(), snapshotId)
log.Infof("Disk %s snapshot %s url: %s", diskId, snapshotId, snapshotUrl)
snapId, _ := snapshotId.GetString()
snapshotUrl := path.Join(migParams.SnapshotsUri, diskStorageId, diskId, snapId)
snapshotPath := path.Join(disk.GetSnapshotDir(), snapId)
log.Infof("Disk %s snapshot %s url: %s", diskId, snapId, snapshotUrl)
iStorage.CreateSnapshotFormUrl(ctx, snapshotUrl, diskId, snapshotPath)
}
@@ -434,7 +452,7 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
// create local disk
backingFile, _ := migParams.DisksBackingFile.GetString(diskId)
size, _ := diskinfo.Int("size")
_, err := disk.CreateRaw(ctx, size, "qcow2", "", false, "", backingFile)
_, err := disk.CreateRaw(ctx, int(size), "qcow2", "", false, "", backingFile)
if err != nil {
log.Errorln(err)
return nil, err
@@ -442,7 +460,7 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
} else {
// download disk form remote url
diskUrl := path.Join(migParams.DisksUri, diskStorageId, diskId)
if err := disk.CreateFromUrl(); err != nil {
if err := disk.CreateFromUrl(ctx, diskUrl); err != nil {
log.Errorln(err)
return nil, err
}
@@ -460,7 +478,7 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
startParams := jsonutils.NewDict()
startParams.Set("qemu_version", jsonutils.NewString(migParams.QemuVersion))
startParams.Set("need_migrate", jsonutils.JSONTrue)
guest.StartGuest(ctx, params)
guest.StartGuest(ctx, startParams)
}
return nil, nil
@@ -492,7 +510,7 @@ func (m *SGuestManager) CanMigrate(sid string) bool {
return true
}
func (m *SGuestManager) GetFreePortByBase(basePort int64) int64 {
func (m *SGuestManager) GetFreePortByBase(basePort int) int {
var port = 1
for {
if netutils2.IsTcpPortUsed("0.0.0.0", basePort+port) ||
@@ -504,7 +522,7 @@ func (m *SGuestManager) GetFreePortByBase(basePort int64) int64 {
}
}
func (m *SGuestManager) GetFreeVncPort() int64 {
func (m *SGuestManager) GetFreeVncPort() int {
vncPorts := make(map[int]struct{}, 0)
for _, guest := range m.Servers {
inUsePort := guest.GetVncPort()
@@ -555,31 +573,67 @@ func (m *SGuestManager) DeleteSnapshot(ctx context.Context, params interface{})
return guest.ExecDeleteSnapshotTask(ctx, delParams.Disk, delParams.DeleteSnapshot,
delParams.ConvertSnapshot, delParams.PendingDelete)
} else {
return nil, delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot)
return jsonutils.NewDict(jsonutils.NewPair("deleted", jsonutils.JSONTrue)),
delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot, "", false)
}
}
func (m *SGuestManager) Resume(ctx context.Context, sid string, isLiveMigrate bool) (jsonutils.JSONObject, error) {
guest := guestManger.Servers[sid]
resumeTask := NewGuestResumeTask(ctx, guest)
if isLiveMigrate {
guest.StartPresendArp()
}
resumeTask.Start()
return nil, nil
}
// func (m *SGuestManager) StartNbdServer(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
// sid, ok := params.(string)
// if !ok {
// return nil, hostutils.ParamsError
// }
// guest := guestManger.Servers[sid]
// port := m.GetFreePortByBase(BUILT_IN_NBD_SERVER_PORT_BASE)
// }
func (m *SGuestManager) StartDriveMirror(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
mirrorParams, ok := params.(*SDriverMirror)
if !ok {
return nil, hostutils.ParamsError
}
guest := guestManger.Servers[mirrorParams.Sid]
task := NewDriveMirrorTask(ctx, guest, mirrorParams.NbdServerUri, "top", nil)
task.Start()
return nil, nil
}
func (m *SGuestManager) ExitGuestCleanup() {
for _, guest := range m.Servers {
guest.ExitCleanup(false)
}
// TODO
m.StopCpusetBalancer()
// m.StopCpusetBalancer()
cgrouputils.CgroupCleanAll()
// TODO
// hostmetrics?
}
func (m *SGuestManager) GetHost() hostutils.IHost {
return m.host
}
var guestManger *SGuestManager
func Stop() {
guestManger.ExitGuestCleanup()
}
func Init(serversPath string) {
func Init(host hostutils.IHost, serversPath string) {
if guestManger == nil {
guestManger = NewGuestManager(serversPath)
guestManger = NewGuestManager(host, serversPath)
}
}
+111 -60
View File
@@ -34,7 +34,7 @@ type SGuestStopTask struct {
*SKVMGuestInstance
ctx context.Context
timeout int64
startPowerdown *time.Time
startPowerdown time.Time
}
func NewGuestStopTask(guest *SKVMGuestInstance, ctx context.Context, timeout int64) *SGuestStopTask {
@@ -42,7 +42,7 @@ func NewGuestStopTask(guest *SKVMGuestInstance, ctx context.Context, timeout int
SKVMGuestInstance: guest,
ctx: ctx,
timeout: timeout,
startPowerdown: nil,
startPowerdown: time.Time{},
}
}
@@ -57,12 +57,13 @@ func (s *SGuestStopTask) Start() {
func (s *SGuestStopTask) onPowerdownGuest(results string) {
s.ExitCleanup(true)
s.startPowerdown = &time.Now()
s.startPowerdown = time.Now()
s.checkGuestRunning()
}
func (s *SGuestStopTask) checkGuestRunning() {
if !s.IsRunning() || time.Now().Sub(*s.startPowerdown) > (s.timeout*time.Second) {
if !s.IsRunning() ||
time.Now().Sub(s.startPowerdown) > time.Duration(s.timeout)*time.Second {
s.Stop() // force stop
hostutils.TaskComplete(s.ctx, nil)
} else {
@@ -82,13 +83,13 @@ func (s *SGuestStopTask) CheckGuestRunningLater() {
type SGuestSyncConfigTaskExecutor struct {
ctx context.Context
guest *SKVMGuestInstance
tasks IGuestTasks
tasks []IGuestTasks
errors []error
callback func([]error)
}
func NewGuestSyncConfigTaskExecutor(ctx context.Context, guest *SKVMGuestInstance, tasks []IGuestTasks, callback func(error)) *SGuestSyncConfigTaskExecutor {
func NewGuestSyncConfigTaskExecutor(ctx context.Context, guest *SKVMGuestInstance, tasks []IGuestTasks, callback func([]error)) *SGuestSyncConfigTaskExecutor {
return &SGuestSyncConfigTaskExecutor{ctx, guest, tasks, make([]error, 0), callback}
}
@@ -98,7 +99,7 @@ func (t *SGuestSyncConfigTaskExecutor) Start(delay int) {
func (t *SGuestSyncConfigTaskExecutor) runNextTask() {
if len(t.tasks) > 0 {
task := t.tasks[len(t.tasks-1)]
task := t.tasks[len(t.tasks)-1]
t.tasks = t.tasks[:len(t.tasks)-1]
task.Start(t.runNextTaskCallback)
} else {
@@ -130,27 +131,27 @@ type SGuestDiskSyncTask struct {
addDisks []jsonutils.JSONObject
cdrom *string
callback func(error)
callback func(...error)
}
func NewGuestDiskSyncTask(guest *SKVMGuestInstance, delDisks, addDisks []jsonutils.JSONObject, cdrom *string) *SGuestDiskSyncTask {
return &SGuestDiskSyncTask{guest, delDisks, addDisks, cdrom}
return &SGuestDiskSyncTask{guest, delDisks, addDisks, cdrom, nil}
}
func (d *SGuestDiskSyncTask) Start(callback func(error)) {
func (d *SGuestDiskSyncTask) Start(callback func(...error)) {
d.callback = callback
d.syncDisksConf()
}
func (d *SGuestDiskSyncTask) syncDisksConf() {
if len(d.delDisks) > 0 {
disk = d.delDisks[len(d.delDisks)-1]
disk := d.delDisks[len(d.delDisks)-1]
d.delDisks = d.delDisks[:len(d.delDisks)-1]
d.removeDisk(disk)
return
}
if len(d.addDisks) > 0 {
disk = d.addDisks[len(d.addDisks)-1]
disk := d.addDisks[len(d.addDisks)-1]
d.addDisks = d.addDisks[:len(d.addDisks)-1]
d.addDisk(disk)
return
@@ -159,7 +160,7 @@ func (d *SGuestDiskSyncTask) syncDisksConf() {
d.changeCdrom()
return
}
d.callback(nil)
d.callback()
}
func (d *SGuestDiskSyncTask) changeCdrom() {
@@ -170,7 +171,7 @@ func (d *SGuestDiskSyncTask) onGetBlockInfo(results *jsonutils.JSONArray) {
var cdName string
for _, r := range results.Value() {
device, _ := r.GetString("device")
if regexp.MustCompile(`^ide\d+-cd\d+$`, device) {
if regexp.MustCompile(`^ide\d+-cd\d+$`).MatchString(device) {
cdName = device
break
}
@@ -195,7 +196,7 @@ func (d *SGuestDiskSyncTask) OnChangeCdromContentSucc(results string) {
func (d *SGuestDiskSyncTask) removeDisk(disk jsonutils.JSONObject) {
index, _ := disk.Int("index")
devId = fmt.Sprintf("drive_%s", index)
devId := fmt.Sprintf("drive_%d", index)
d.guest.Monitor.DriveDel(devId,
func(results string) { d.onRemoveDriveSucc(devId, results) })
}
@@ -258,7 +259,7 @@ func (d *SGuestDiskSyncTask) onAddDiskSucc(disk jsonutils.JSONObject, results st
}
if diskDirver == DISK_DRIVER_VIRTIO {
params["addr"] = fmt.Sprintf("0x%x", d.guest.GetDiskAddr(diskIndex))
params["addr"] = fmt.Sprintf("0x%x", d.guest.GetDiskAddr(int(diskIndex)))
} else if DISK_DRIVER_IDE == diskDirver {
params["unit"] = diskIndex % 2
}
@@ -289,12 +290,12 @@ func (n *SGuestNetworkSyncTask) Start(callback func(...error)) {
func (n *SGuestNetworkSyncTask) syncNetworkConf() {
if len(n.delNics) > 0 {
nic = n.delNics[len(n.delNics)-1]
nic := n.delNics[len(n.delNics)-1]
n.delNics = n.delNics[:len(n.delNics)-1]
n.removeNic(nic)
return
} else if len(n.addNics) > 0 {
nic = n.addNics[len(n.addNics)-1]
nic := n.addNics[len(n.addNics)-1]
n.addNics = n.addNics[:len(n.addNics)-1]
n.addNic(nic)
return
@@ -311,8 +312,8 @@ func (n *SGuestNetworkSyncTask) addNic(nic jsonutils.JSONObject) {
// pass not implement
}
func NewGuestNetworkSyncTask(guest *SKVMGuestInstance, delNics, addNics jsonutils.JSONObject) *SGuestNetworkSyncTask {
return &SGuestNetworkSyncTask{guest, delNics, addNics, make(error, 0)}
func NewGuestNetworkSyncTask(guest *SKVMGuestInstance, delNics, addNics []jsonutils.JSONObject) *SGuestNetworkSyncTask {
return &SGuestNetworkSyncTask{guest, delNics, addNics, make([]error, 0), nil}
}
/**
@@ -351,10 +352,10 @@ func (s *SGuestLiveMigrateTask) startMigrateStatusCheck(string) {
s.c = make(chan struct{})
for {
select {
case <-c: // on c close
case <-s.c: // on c close
break
case <-time.After(time.Second * 1):
s.Monitor.getMigrateStatus(s.onGetMigrateStatus)
s.Monitor.GetMigrateStatus(s.onGetMigrateStatus)
}
}
}
@@ -362,10 +363,10 @@ func (s *SGuestLiveMigrateTask) startMigrateStatusCheck(string) {
func (s *SGuestLiveMigrateTask) onGetMigrateStatus(status string) {
if status == "completed" {
close(s.c)
hostutils.TaskComplete(ctx, nil)
hostutils.TaskComplete(s.ctx, nil)
} else if status == "failed" {
close(c)
hostutils.TaskFailed(ctx, fmt.Sprintf("Query migrate got status: %s", status))
close(s.c)
hostutils.TaskFailed(s.ctx, fmt.Sprintf("Query migrate got status: %s", status))
}
}
@@ -380,6 +381,13 @@ type SGuestResumeTask struct {
startTime time.Time
}
func NewGuestResumeTask(ctx context.Context, s *SKVMGuestInstance) *SGuestResumeTask {
return &SGuestResumeTask{
SKVMGuestInstance: s,
ctx: ctx,
}
}
func (s *SGuestResumeTask) Start() {
s.startTime = time.Now()
s.confirmRunning()
@@ -409,8 +417,8 @@ func (s *SGuestResumeTask) onConfirmRunning(status string) {
func (s *SGuestResumeTask) taskFailed(reason string) {
log.Infof("Start guest %s failed: %s", s.GetId(), reason)
s.ForceStop()
if len(appctx.AppContextTaskId(ctx)) > 0 {
hostutils.TaskFailed(ctx, reason)
if len(appctx.AppContextTaskId(s.ctx)) > 0 {
hostutils.TaskFailed(s.ctx, reason)
} else {
s.SyncStatus()
}
@@ -426,7 +434,7 @@ func (s *SGuestResumeTask) onGetBlockInfo(results *jsonutils.JSONArray) {
func (s *SGuestResumeTask) resumeGuest() {
s.startTime = time.Now()
s.Monitor.Cont(s.onResumeSucc)
s.Monitor.SimpleCommand("cont", s.onResumeSucc)
}
func (s *SGuestResumeTask) onResumeSucc(res string) {
@@ -435,8 +443,8 @@ func (s *SGuestResumeTask) onResumeSucc(res string) {
func (s *SGuestResumeTask) onStartRunning() {
s.removeStatefile()
if len(appctx.AppContextTaskId(ctx)) > 0 {
hostutils.TaskComplete(ctx, nil)
if len(appctx.AppContextTaskId(s.ctx)) > 0 {
hostutils.TaskComplete(s.ctx, nil)
}
if options.HostOptions.SetVncPassword {
s.SetVncPassword()
@@ -448,13 +456,14 @@ func (s *SGuestResumeTask) onStartRunning() {
if len(disksIdx) > 0 {
timeutils2.AddTimeout(time.Second*5, func() { s.startStreamDisks(disksIdx) })
} else if options.HostOptions.AutoMergeBackingTemplate {
timeutils2.AddTimeout(time.Second*options.HostOptions.AutoMergeDelaySeconds,
timeutils2.AddTimeout(
time.Second*time.Duration(options.HostOptions.AutoMergeDelaySeconds),
func() { s.startStreamDisks(nil) })
}
}
func (s *SGuestResumeTask) startStreamDisks(disksIdx []int) {
s.startTime = nil
s.startTime = time.Time{}
s.CleanStartupTask()
if s.IsMonitorAlive() {
s.StreamDisks(s.ctx, func() { s.onStreamComplete(disksIdx) }, disksIdx)
@@ -500,7 +509,7 @@ type SGuestStreamDisksTask struct {
streamDevs []string
}
func NewGuestStreamDisksTask(ctx context.Context, guest *SKVMGuestInstance, callback func(), disksIdx []int) {
func NewGuestStreamDisksTask(ctx context.Context, guest *SKVMGuestInstance, callback func(), disksIdx []int) *SGuestStreamDisksTask {
return &SGuestStreamDisksTask{
SKVMGuestInstance: guest,
ctx: ctx,
@@ -516,7 +525,7 @@ func (s *SGuestStreamDisksTask) Start() {
func (s *SGuestStreamDisksTask) onInitCheckStreamJobs(jobs int) {
if jobs > 0 {
log.Warningf("GuestStreamDisksTask: duplicate block streaming???")
s.startWaitBlockStream()
s.startWaitBlockStream("")
} else if jobs == 0 {
s.startBlockStreaming()
}
@@ -565,13 +574,13 @@ func (s *SGuestStreamDisksTask) onBlockDrivesSucc(res *jsonutils.JSONArray) {
func (s *SGuestStreamDisksTask) startDoBlockStream() {
if len(s.streamDevs) > 0 {
dev = s.streamDevs[0]
dev := s.streamDevs[0]
s.streamDevs = s.streamDevs[1:]
s.Monitor.BlockStream(dev, s.startWaitBlockStream)
}
}
func (s *SGuestStreamDisksTask) startWaitBlockStream() {
func (s *SGuestStreamDisksTask) startWaitBlockStream(res string) {
if s.c == nil {
s.c = make(chan struct{})
for {
@@ -636,7 +645,7 @@ type SGuestReloadDiskTask struct {
}
func NewGuestReloadDiskTask(
ctx context.Context, s *SKVMGuestInstance, disk *storageman.IDisk,
ctx context.Context, s *SKVMGuestInstance, disk storageman.IDisk,
) *SGuestReloadDiskTask {
return &SGuestReloadDiskTask{
SKVMGuestInstance: s,
@@ -704,21 +713,17 @@ func (s *SGuestReloadDiskTask) onReloadSucc(err string) {
if len(err) > 0 {
log.Errorf("monitor new snapshot blkdev error: %s", err)
}
s.Monitor.SimpleCommand(cont, s.onResumeSucc)
s.Monitor.SimpleCommand("cont", s.onResumeSucc)
}
func (s *SGuestReloadDiskTask) onResumeSucc(results string) {
log.Infof("guest reload disk task resume succ %s", results)
if _, err := hostutils.TaskComplete(ctx, nil); err != nil {
log.Errorln(err)
}
hostutils.TaskComplete(s.ctx, nil)
}
func (s *SGuestReloadDiskTask) taskFailed(reason string) {
log.Errorf("SGuestReloadDiskTask error: %s", reason)
if _, err := hostutils.TaskFailed(s.ctx, reason); err != nil {
log.Errorln(err)
}
hostutils.TaskFailed(s.ctx, reason)
}
/**
@@ -732,7 +737,7 @@ type SGuestDiskSnapshotTask struct {
}
func NewGuestDiskSnapshotTask(
ctx context.Context, s *SKVMGuestInstance, disk *storageman.IDisk, snapshotId string,
ctx context.Context, s *SKVMGuestInstance, disk storageman.IDisk, snapshotId string,
) *SGuestDiskSnapshotTask {
return &SGuestDiskSnapshotTask{
SGuestReloadDiskTask: NewGuestReloadDiskTask(ctx, s, disk),
@@ -764,21 +769,16 @@ func (s *SGuestDiskSnapshotTask) onSnapshotBlkdevFail(string) {
if err != nil {
log.Errorln(err)
}
_, err = hostutils.TaskFailed(s.ctx, "Reload blkdev error")
if err != nil {
log.Errorln(err)
}
hostutils.TaskFailed(s.ctx, "Reload blkdev error")
}
func (s *SGuestDiskSnapshotTask) onResumeSucc(res string) {
log.Infof("guest disk snapshot task resume succ %s", res)
snapshotDir := s.disk.GetSnapshotDir()
snapshotLocation := path.Join(snapshotDir, s.snapshotId)
_, err := hostutils.TaskComplete(s.ctx, jsonutils.NewDict(
jsonutils.JSONPair{"localtion", jsonutils.NewString(snapshotLocation)}))
if err != nil {
log.Errorln(err)
}
hostutils.TaskComplete(s.ctx, jsonutils.NewDict(
jsonutils.NewPair("localtion", jsonutils.NewString(snapshotLocation))))
}
/**
@@ -795,8 +795,8 @@ type SGuestSnapshotDeleteTask struct {
}
func NewGuestSnapshotDeleteTask(
ctx context.Context, s *SKVMGuestInstance, disk *storageman.IDisk,
deleteSnapshot, convertSnapshot string, pendingDelete string,
ctx context.Context, s *SKVMGuestInstance, disk storageman.IDisk,
deleteSnapshot, convertSnapshot string, pendingDelete bool,
) *SGuestSnapshotDeleteTask {
return &SGuestSnapshotDeleteTask{
SGuestReloadDiskTask: NewGuestReloadDiskTask(ctx, s, disk),
@@ -881,9 +881,60 @@ func (s *SGuestSnapshotDeleteTask) onResumeSucc(res string) {
snapshotDir := s.disk.GetSnapshotDir()
exec.Command("rm", "-f", path.Join(snapshotDir, s.deleteSnapshot))
}
_, err := hostutils.TaskComplete(s.ctx,
jsonutils.NewDict(jsonutils.JSONPair{"localtion", jsonutils.JSONTrue}))
if err != nil {
log.Errorln(err)
hostutils.TaskComplete(s.ctx,
jsonutils.NewDict(jsonutils.NewPair("deleted", jsonutils.JSONTrue)))
}
/**
* GuestDriveMirrorTask
**/
type SDriveMirrorTask struct {
*SKVMGuestInstance
ctx context.Context
nbdUri string
onSucc func()
syncMode string
index int
}
func NewDriveMirrorTask(
ctx context.Context, s *SKVMGuestInstance, nbdUri, syncMode string, onSucc func(),
) *SDriveMirrorTask {
return &SDriveMirrorTask{
SKVMGuestInstance: s,
ctx: ctx,
nbdUri: nbdUri,
syncMode: syncMode,
onSucc: onSucc,
}
}
func (s *SDriveMirrorTask) Start() {
s.startMirror("")
}
func (s *SDriveMirrorTask) startMirror(res string) {
log.Infof("drive mirror results:%s", res)
if len(res) > 0 {
hostutils.TaskFailed(s.ctx, res)
return
}
disks, _ := s.Desc.GetArray("disks")
if s.index < len(disks) {
if s.index >= 1 { // data disk
s.syncMode = "none"
}
target := fmt.Sprintf("%s:exportname=drive_%d", s.nbdUri, s.index)
s.Monitor.DriveMirror(s.startMirror, fmt.Sprintf("drive_%d", s.index),
target, s.syncMode, true)
s.index += 1
} else {
if s.onSucc != nil {
s.onSucc()
} else {
hostutils.TaskComplete(s.ctx, nil)
}
}
}
+145 -59
View File
@@ -28,6 +28,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/cgrouputils"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/timeutils2"
"yunion.io/x/onecloud/pkg/util/version"
)
@@ -71,6 +72,14 @@ func (s *SKVMGuestInstance) GetName() string {
return fmt.Sprintf("%s(%s)", name, id)
}
func (s *SKVMGuestInstance) getStateFilePathRootPrefix() string {
return path.Join(s.HomeDir(), STATE_FILE_PREFIX)
}
func (s *SKVMGuestInstance) IsLoaded() bool {
return s.Desc != nil
}
func (s *SKVMGuestInstance) HomeDir() string {
return path.Join(s.manager.ServersPath, s.Id)
}
@@ -200,19 +209,22 @@ func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params interfa
if err = s.saveVncPort(vncPort); err != nil {
goto finally
} else {
data.Set("vnc_port", jsonutils.NewInt(vncPort))
data.Set("vnc_port", jsonutils.NewInt(int64(vncPort)))
}
if err = s.saveScripts(data); err != nil {
goto finally
} else {
isStarted, err = s.scriptStart()
err = s.scriptStart()
if err == nil {
isStarted = true
}
}
finally:
if !isStarted {
log.Errorf("Start VM failed: %s", err)
time.Sleep((1 << (tried - 1)) * time.Seconde)
time.Sleep(time.Duration(1<<uint(tried-1)) * time.Second)
} else {
log.Infof("VM started ...")
}
@@ -225,18 +237,21 @@ func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params interfa
return nil, nil
} else {
log.Infof("Async start server %s failed: %s!!!", s.GetName(), err)
timeutils2.AddTimeout(100*time.Millisecond, s.SyncStatus())
timeutils2.AddTimeout(100*time.Millisecond, s.SyncStatus)
return nil, err
}
}
func (s *SKVMGuestInstance) saveScripts(data *jsonutils.JSONDict) error {
startScript := s.generateStartScript(data)
if err := fileutils2.FilePutContents(s.GetStartScriptPath, startScript, false); err != nil {
startScript, err := s.generateStartScript(data)
if err != nil {
return err
}
if err = fileutils2.FilePutContents(s.GetStartScriptPath(), startScript, false); err != nil {
return err
}
stopScript := s.generateStopScript(data)
return fileutils2.FilePutContents(s.GetStopScriptPath, stopScript, false)
return fileutils2.FilePutContents(s.GetStopScriptPath(), stopScript, false)
}
func (s *SKVMGuestInstance) GetStartScriptPath() string {
@@ -292,25 +307,29 @@ func (s *SKVMGuestInstance) IsMonitorAlive() bool {
return s.Monitor != nil && s.Monitor.IsConnected()
}
func (s *SKVMGuestInstance) ListStateFilePaths() []string {
files, err := ioutil.ReadDir(s.HomeDir())
if err == nil {
var ret = make([]string, 0)
for i := 0; i < len(files); i++ {
if strings.HasPrefix(files[i].Name(), STATE_FILE_PREFIX) {
ret = append(ret, files[i].Name())
}
}
return ret
}
return nil
}
// func (s *SKVMGuestInstance) ListStateFilePaths() []string {
// files, err := ioutil.ReadDir(s.HomeDir())
// if err == nil {
// var ret = make([]string, 0)
// for i := 0; i < len(files); i++ {
// if strings.HasPrefix(files[i].Name(), STATE_FILE_PREFIX) {
// ret = append(ret, files[i].Name())
// }
// }
// return ret
// }
// return nil
// }
func (s *SKVMGuestInstance) StartMonitor(ctx context.Context) {
timeutils2.AddTimeout(100*time.Millisecond, func() { s.delayStartMonitor(ctx) })
}
func (s *SKVMGuestInstance) delayStartMonitor(ctx context.Context) {
if s.GetQmpMonitorPort(-1) > 0 {
// TODO enable hmp?
}
if options.HostOptions.EnableQmpMonitor && s.GetQmpMonitorPort(-1) > 0 {
s.Monitor = monitor.NewQmpMonitor(
s.onMonitorDisConnect,
@@ -333,8 +352,7 @@ func (s *SKVMGuestInstance) onGetQemuVersion(ctx context.Context, version string
log.Infof("Guest(%s) qemu version %s", s.Id, s.QemuVersion)
if s.Desc.Contains("live_migrate_dest_port") && ctx != nil {
migratePort, _ := s.Desc.Get("live_migrate_dest_port")
body := jsonutils.NewDict(
jsonutils.JSONPair{"live_migrate_dest_port", migratePort})
body := jsonutils.NewDict(jsonutils.NewPair("live_migrate_dest_port", migratePort))
hostutils.TaskComplete(ctx, body)
} else if jsonutils.QueryBoolean(s.Desc, "is_slave", false) {
// TODO
@@ -375,6 +393,17 @@ func (s *SKVMGuestInstance) onMonitorTimeout(ctx context.Context, err error) {
func() { s.asyncScriptStart(ctx, jsonutils.NewDict()) })
}
func (s *SKVMGuestInstance) GetHmpMonitorPort(vncPort int) int {
if vncPort <= 0 {
vncPort = s.GetVncPort()
}
if vncPort > 0 {
return vncPort + MONITOR_PORT_BASE
} else {
return -1
}
}
func (s *SKVMGuestInstance) GetQmpMonitorPort(vncPort int) int {
if vncPort <= 0 {
vncPort = s.GetVncPort()
@@ -403,10 +432,15 @@ func (s *SKVMGuestInstance) GetVncPort() int {
return -1
}
func (s *SKVMGuestInstance) saveVncPort(port int64) error {
func (s *SKVMGuestInstance) saveVncPort(port int) error {
return fileutils2.FilePutContents(s.GetVncFilePath(), fmt.Sprintf("%d", port), false)
}
func (s *SKVMGuestInstance) DoResumeTask(ctx context.Context) {
s.startupTask = NewGuestResumeTask(ctx, s)
s.startupTask.Start()
}
func (s *SKVMGuestInstance) SyncStatus() {
if s.IsRunning() {
s.Monitor.GetBlockJobs(s.CheckBlockOrRunning)
@@ -433,9 +467,10 @@ func (s *SKVMGuestInstance) CheckBlockOrRunning(jobs int) {
func (s *SKVMGuestInstance) SaveDesc(desc jsonutils.JSONObject) error {
s.Desc = desc.(*jsonutils.JSONDict)
if err := fileutils2.FilePutContents(s.GetDescFilePath(), desc.String()); err != nil {
if err := fileutils2.FilePutContents(s.GetDescFilePath(), desc.String(), false); err != nil {
log.Errorln(err)
}
return nil
}
func (s *SKVMGuestInstance) StartGuest(ctx context.Context, params jsonutils.JSONObject) {
@@ -449,7 +484,7 @@ func (s *SKVMGuestInstance) DeployFs(deployInfo *guestfs.SDeployInfo) (jsonutils
diskId, _ := disks[0].GetString("disk_id")
disk := storageman.GetManager().GetStorageDisk(storageId, diskId)
return disk.DeployGuestFs(disk.GetPath, s.Desc, deployInfo)
return disk.DeployGuestFs(disk.GetPath(), s.Desc, deployInfo)
} else {
return nil, fmt.Errorf("Guest dosen't have disk ??")
}
@@ -500,7 +535,7 @@ func (s *SKVMGuestInstance) ExitCleanup(clearCgroup bool) {
pid := s.GetPid()
if pid > 0 {
// TODO: ClearCgroup
s.ClearCgroup(pid)
// s.ClearCgroup(pid)
}
}
if s.Monitor != nil {
@@ -513,29 +548,33 @@ func (s *SKVMGuestInstance) GetCleanFiles() []string {
return []string{s.GetPidFilePath(), s.GetVncFilePath()}
}
func (s *SKVMGuestInstance) delTmpDisks(ctx context.Context, migrated bool) {
func (s *SKVMGuestInstance) delTmpDisks(ctx context.Context, migrated bool) error {
disks, _ := s.Desc.GetArray("disks")
for _, disk := range disks {
if disk.Contains("path") {
diskPath, _ := disk.GetString("path")
// TODO GetDisksByPath, storagetypes, deleteallsnapshot, delete
d := storageman.GetManager().GetDiskByPath(diskPath)
if d != nil && d.GetType == storagetypes.STORAGE_LOCAL && migrated {
if d != nil && d.GetType() == storagetypes.STORAGE_LOCAL && migrated {
if err := d.DeleteAllSnapshot(); err != nil {
log.Errorln(err)
return err
}
if err := d.Delete(ctx); err != nil {
if _, err := d.Delete(ctx, nil); err != nil {
log.Errorln(err)
return err
}
}
}
}
return nil
}
func (s *SKVMGuestInstance) Delete(ctx context.Context, migrated bool) error {
// self._del_bw_limit()
// self._del_netmon_nic() ?? 需要开发?
s.delTmpDisks(ctx, migrated)
if err := s.delTmpDisks(ctx, migrated); err != nil {
return err
}
return exec.Command("rm", "-rf", s.HomeDir()).Run()
}
@@ -548,6 +587,15 @@ func (s *SKVMGuestInstance) Stop() bool {
}
}
func (s *SKVMGuestInstance) scriptStart() error {
err := exec.Command("sh", s.GetStartScriptPath()).Run()
if err != nil {
s.scriptStop()
return err
}
return nil
}
func (s *SKVMGuestInstance) scriptStop() bool {
err := exec.Command("sh", s.GetStopScriptPath()).Run()
if err != nil {
@@ -557,9 +605,13 @@ func (s *SKVMGuestInstance) scriptStop() bool {
return true
}
func (s *SKVMGuestInstance) ExecStopTask(ctx context.Context, params interface{}) {
timeout := params.(int64)
func (s *SKVMGuestInstance) ExecStopTask(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
timeout, ok := params.(int64)
if !ok {
return nil, hostutils.ParamsError
}
NewGuestStopTask(s, ctx, timeout).Start()
return nil, nil
}
func (s *SKVMGuestInstance) ExecSuspendTask(ctx context.Context) {
@@ -603,14 +655,14 @@ func (s *SKVMGuestInstance) compareDescDisks(newDesc jsonutils.JSONObject) ([]js
newDisks, _ := newDesc.GetArray("disks")
for _, disk := range newDisks {
driver, _ := disk.GetString("driver")
if utils.IsInStringArray("driver", []string{"virtio", "scsi"}) {
if utils.IsInStringArray(driver, []string{"virtio", "scsi"}) {
addDisks = append(addDisks, disk)
}
}
oldDisks, _ := s.Desc.GetArray("disks")
for _, disk := range oldDisks {
driver, _ := disk.GetString("driver")
if utils.IsInStringArray("driver", []string{"virtio", "scsi"}) {
if utils.IsInStringArray(driver, []string{"virtio", "scsi"}) {
var find = false
for idx, ndisk := range addDisks {
diskIndex, _ := disk.Int("index")
@@ -634,22 +686,23 @@ func (s *SKVMGuestInstance) compareDescCdrom(newDesc jsonutils.JSONObject) *stri
return nil
} else if !s.Desc.Contains("cdrom") && newDesc.Contains("cdrom") {
cdrom, _ := newDesc.GetString("cdrom", "path")
return cdrom
return &cdrom
} else if s.Desc.Contains("cdrom") && !newDesc.Contains("cdrom") {
return ""
var res = ""
return &res
} else {
cdrom, _ := s.Desc.GetString("cdrom", "path")
ncdrom, _ := newDesc.GetString("cdrom", "path")
if cdrom == ncdrom {
return nil
} else {
return ncdrom
return &ncdrom
}
}
}
func (s *SKVMGuestInstance) compareDescNetworks(newDesc jsonutils.JSONObject) ([]jsonutils.JSONObject, []jsonutils.JSONObject) {
var isVaild = func(net jsonutils.JSONObject) bool {
var isValid = func(net jsonutils.JSONObject) bool {
driver, _ := net.GetString("driver")
return driver == "virtio"
}
@@ -666,17 +719,20 @@ func (s *SKVMGuestInstance) compareDescNetworks(newDesc jsonutils.JSONObject) ([
}
var delNics, addNics = []jsonutils.JSONObject{}, []jsonutils.JSONObject{}
for _, n := range newDesc.GetArray("nics") {
nics, _ := newDesc.GetArray("nics")
for _, n := range nics {
if isValid(n) {
addNics = append(addNics, n)
}
}
for _, n := range s.Desc.GetArray("nics") {
nics, _ = newDesc.GetArray("nics")
for _, n := range nics {
if isValid(n) {
idx := findNet(addNics, n)
if idx >= 0 {
addNics = append(addNics[:idx], addNics[idx+1:])
// remove n
addNics = append(addNics[:idx], addNics[idx+1:]...)
} else {
delNics = append(delNics, n)
}
@@ -688,12 +744,12 @@ func (s *SKVMGuestInstance) compareDescNetworks(newDesc jsonutils.JSONObject) ([
// 目测sync_cgroup没有要用到,先不写
func (s *SKVMGuestInstance) SyncConfig(ctx context.Context, desc jsonutils.JSONObject, fwOnly bool) (jsonutils.JSONObject, error) {
var delDisks, addDisks, delNetworks, addNetworks []jsonutils.JSONObject
var cdrom string
var cdrom *string
if !fwOnly {
delDisks, addDisks := s.compareDescDisks(desc)
cdrom := s.compareDescCdrom(desc)
delNetworks, addNetworks := s.compareDescNetworks(desc)
delDisks, addDisks = s.compareDescDisks(desc)
cdrom = s.compareDescCdrom(desc)
delNetworks, addNetworks = s.compareDescNetworks(desc)
}
if err := s.SaveDesc(desc); err != nil {
return nil, err
@@ -719,7 +775,7 @@ func (s *SKVMGuestInstance) SyncConfig(ctx context.Context, desc jsonutils.JSONO
var tasks = []IGuestTasks{}
var callBack = func(errs []error) {
if len(err) > 0 {
if len(errs) == 0 {
hostutils.TaskComplete(ctx, nil)
} else {
var reason string
@@ -733,17 +789,19 @@ func (s *SKVMGuestInstance) SyncConfig(ctx context.Context, desc jsonutils.JSONO
if len(delDisks)+len(addDisks) > 0 || cdrom != nil {
task := NewGuestDiskSyncTask(s, delDisks, addDisks, cdrom)
runTaskNames = append(runTaskNames, jsonutils.NewString("disksync"))
tasks = append(tasks, tasks)
tasks = append(tasks, task)
}
if len(delNetworks)+len(addNetworks) > 0 {
task := NewGuestNetworkSyncTask(s, delNetworks, addNetworks)
runTaskNames = append(runTaskNames, jsonutils.NewString("networksync"))
tasks = append(tasks, tasks)
tasks = append(tasks, task)
}
NewGuestSyncConfigTaskExecutor(ctx, s, tasks, callBack).Start(1)
return jsonutils.NewDict(jsonutils.JSONPair{"task", runTaskNames}), nil
res := jsonutils.NewDict()
res.Set("task", jsonutils.NewArray(runTaskNames...))
return res, nil
}
func (s *SKVMGuestInstance) getApptags() []string {
@@ -770,7 +828,7 @@ func (s *SKVMGuestInstance) getStorageDeviceId() string {
}
func (s *SKVMGuestInstance) SetCgroup() {
s.cgroupId = s.GetPid()
s.cgroupId = fmt.Sprintf("%d", s.GetPid())
s.setCgroupIo()
s.setCgroupCpu()
}
@@ -826,7 +884,7 @@ func (s *SKVMGuestInstance) setCgroupCpu() {
}
func (s *SKVMGuestInstance) CreateFromDesc(desc jsonutils.JSONObject) error {
if !s.PrepareDir() {
if err := s.PrepareDir(); err != nil {
uuid, _ := desc.GetString("uuid")
return fmt.Errorf("Failed to create server dir %s", uuid)
}
@@ -849,7 +907,7 @@ func (s *SKVMGuestInstance) streamDisksComplete(ctx context.Context) {
disks, _ := s.Desc.GetArray("disks")
for i, disk := range disks {
diskpath, _ := disk.GetString("path")
d := storageman.GetManager().GetDiskByPath(diskPath)
d := storageman.GetManager().GetDiskByPath(diskpath)
if d != nil {
d.PostCreateFromImageFuse()
}
@@ -859,7 +917,8 @@ func (s *SKVMGuestInstance) streamDisksComplete(ctx context.Context) {
}
}
s.SaveDesc(s.Desc)
_, err := modules.Servers.PerformAction(ctx, s.GetId(), "stream-disks-complete", nil)
_, err := modules.Servers.PerformAction(hostutils.GetComputeSession(ctx),
s.GetId(), "stream-disks-complete", nil)
if err != nil {
log.Infof("stream disks complete sync error %s", err)
}
@@ -880,8 +939,13 @@ func (s *SKVMGuestInstance) SyncMetadata(meta *jsonutils.JSONDict) {
func (s *SKVMGuestInstance) SetVncPassword() {
password := seclib.RandomPassword(8)
s.VncPassword = password
var callback = func(res string) {
if len(res) > 0 {
log.Errorln("Set vnc password failed: %s", res)
}
}
timeutils2.AddTimeout(time.Second*3,
func() { s.Monitor.SetVncPassword(s.GetVdiProtocol(), password, nil) })
func() { s.Monitor.SetVncPassword(s.GetVdiProtocol(), password, callback) })
}
func (s *SKVMGuestInstance) ListStateFilePaths() []string {
@@ -946,7 +1010,7 @@ func (s *SKVMGuestInstance) ExecReloadDiskTask(ctx context.Context, disk storage
return nil, fmt.Errorf("Guest dosen't support reload disk")
}
} else {
return jsonutils.NewDict(jsonutils.JSONPair{"reopen", jsonutils.JSONTrue}), nil
return jsonutils.NewDict(jsonutils.NewPair("reopen", jsonutils.JSONTrue)), nil
}
}
@@ -977,8 +1041,8 @@ func (s *SKVMGuestInstance) StaticSaveSnapshot(
return nil, err
}
location := path.Join(disk.GetSnapshotDir(), snapshotId)
return jsonutils.NewDict(jsonutils.JSONPair{"localtion",
jsonutils.NewString(snapshotLocation)}), nil
return jsonutils.NewDict(jsonutils.NewPair("localtion",
jsonutils.NewString(location))), nil
}
func (s *SKVMGuestInstance) ExecDeleteSnapshotTask(
@@ -1008,5 +1072,27 @@ func (s *SKVMGuestInstance) deleteStaticSnapshotFile(
log.Errorln(err)
return nil, err
}
return jsonutils.NewDict(jsonutils.JSONPair{"localtion", jsonutils.JSONTrue}), nil
return jsonutils.NewDict(jsonutils.NewPair("deleted", jsonutils.JSONTrue)), nil
}
func (s *SKVMGuestInstance) PrepareMigrate(liveMigrage bool) (*jsonutils.JSONDict, error) {
disksBackFile := jsonutils.NewDict()
disks, _ := s.Desc.GetArray("disks")
for _, disk := range disks {
if disk.Contains("path") {
diskPath, _ := disk.GetString("path")
d := storageman.GetManager().GetDiskByPath(diskPath)
if d.GetType() == storagetypes.STORAGE_LOCAL {
back, err := d.PrepareMigrate(liveMigrage)
if err != nil {
return nil, err
}
if len(back) > 0 {
diskId, _ := disk.GetString("disk_id")
disksBackFile.Set(diskId, jsonutils.NewString(back))
}
}
}
}
return disksBackFile, nil
}
+63 -35
View File
@@ -3,10 +3,9 @@ package guestman
import (
"fmt"
"path"
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/hostman/hostinfo"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/storageman"
"yunion.io/x/onecloud/pkg/util/fileutils2"
@@ -32,7 +31,7 @@ const (
)
func (s *SKVMGuestInstance) IsKvmSupport() bool {
return hostinfo.Instance().IsKvmSupport()
return guestManger.GetHost().IsKvmSupport()
}
func (s *SKVMGuestInstance) IsVdiSpice() bool {
@@ -40,7 +39,7 @@ func (s *SKVMGuestInstance) IsVdiSpice() bool {
return vdi == "spice"
}
func (s *SKVMGuestInstance) getMonitorDesc(idstr, port, mode string) string {
func (s *SKVMGuestInstance) getMonitorDesc(idstr string, port int, mode string) string {
var cmd = ""
cmd += fmt.Sprintf(" -chardev socket,id=%sdev", idstr)
cmd += fmt.Sprintf(",port=%d", port)
@@ -124,7 +123,6 @@ func (s *SKVMGuestInstance) GetDiskAddr(idx int) int {
if s.IsVdiSpice() {
base += 10
}
idxNum, _ := strconv.Atoi(idx)
return base + idx
}
@@ -142,7 +140,7 @@ func (s *SKVMGuestInstance) GetDiskDeviceModel(driver string) string {
}
}
func (s *SKVMGuestInstance) getDriveDesc(disk jsonutils.JSONObject) string {
func (s *SKVMGuestInstance) getVdiskDesc(disk jsonutils.JSONObject) string {
diskIndex, _ := disk.Int("index")
diskDriver, _ := disk.GetString("driver")
@@ -151,7 +149,7 @@ func (s *SKVMGuestInstance) getDriveDesc(disk jsonutils.JSONObject) string {
cmd += fmt.Sprintf(",drive=drive_%s", diskIndex)
if diskDriver == DISK_DRIVER_VIRTIO {
cmd += fmt.Sprintf(",bus=%s,addr=0x%x", s.GetPciBus(), s.GetDiskAddr(int(diskIndex)))
} else if utils.IsInStringArray(iskDriver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) {
} else if utils.IsInStringArray(diskDriver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) {
cmd += ",bus=scsi.0"
} else if diskDriver == DISK_DRIVER_IDE {
cmd += fmt.Sprintf(",bus=ide.%d,unit=%d", diskIndex/2, diskIndex%2)
@@ -174,11 +172,31 @@ func (s *SKVMGuestInstance) getNicDownScriptPath(nic jsonutils.JSONObject) strin
return path.Join(s.HomeDir(), fmt.Sprintf("if-down-%s-%s.sh", bridge, ifname))
}
func (s *SKVMGuestInstance) getNetdevDesc(nic jsonutils.JSONObject) string {
func (s *SKVMGuestInstance) generateNicScripts(nic jsonutils.JSONObject) error {
bridge, _ := nic.GetString("bridge")
dev := guestManger.GetHost().GetBridgeDev(bridge)
if dev == nil {
return fmt.Errorf("Can't find bridge %s", bridge)
}
if err := dev.GenerateIfupScripts(s.getNicUpScriptPath(nic), nic); err != nil {
log.Errorln(err)
return err
}
if err := dev.GenerateIfdownScripts(s.getNicDownScriptPath(nic), nic); err != nil {
log.Errorln(err)
return err
}
return nil
}
func (s *SKVMGuestInstance) getNetdevDesc(nic jsonutils.JSONObject) (string, error) {
ifname, _ := nic.GetString("ifname")
driver, _ := nic.GetString("driver")
s.generateNicScripts(nic)
// TODO
if err := s.generateNicScripts(nic); err != nil {
return "", err
}
upscript := s.getNicUpScriptPath(nic)
downscript := s.getNicDownScriptPath(nic)
cmd := " -netdev type=tap"
@@ -189,7 +207,7 @@ func (s *SKVMGuestInstance) getNetdevDesc(nic jsonutils.JSONObject) string {
}
cmd += fmt.Sprintf(",script=%s", upscript)
cmd += fmt.Sprintf(",downscript=%s", downscript)
return cmd
return cmd, nil
}
func (s *SKVMGuestInstance) getNicDeviceModel(name string) string {
@@ -202,7 +220,7 @@ func (s *SKVMGuestInstance) getNicDeviceModel(name string) string {
}
}
func (s *SKVMGuestInstance) getNicAddr(index int) string {
func (s *SKVMGuestInstance) getNicAddr(index int) int {
var diskCnt = 10
disks, _ := s.Desc.GetArray("disks")
if len(disks) > 10 {
@@ -241,7 +259,7 @@ func (s *SKVMGuestInstance) getQgaDesc() string {
return cmd
}
func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string {
func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (string, error) {
var (
uuid, _ = s.Desc.GetString("uuid")
mem, _ = s.Desc.Int("mem")
@@ -249,7 +267,8 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
name, _ = s.Desc.GetString("name")
nics, _ = s.Desc.GetArray("nics")
disks, _ = s.Desc.GetArray("disks")
osname = s.GetOsname()
osname = s.getOsname()
cmd = ""
)
if osname == OS_NAME_MACOS {
@@ -261,7 +280,7 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
qemuVersion := options.HostOptions.DefaultQemuVersion
if data.Contains("qemu_version") {
qemuVersion, _ := data.GetString("qemu_version")
qemuVersion, _ = data.GetString("qemu_version")
}
if qemuVersion == "latest" {
qemuVersion = ""
@@ -288,17 +307,17 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
diskPath, _ := disk.GetString("path")
d := storageman.GetManager().GetDiskByPath(diskPath)
if d == nil {
return fmt.Errorf("get disk %s by storage error", diskPath)
return "", fmt.Errorf("get disk %s by storage error", diskPath)
}
diskIndex, _ := disk.Int("index")
cmd += d.GetDiskSetupScripts(diskIndex)
cmd += d.GetDiskSetupScripts(int(diskIndex))
}
cmd += fmt.Sprintf("STATE_FILE=`ls -d %s* | head -n 1`\n", s.getStateFilePathRootPrefix())
var qemuCmd = qemutils.GetQemu(qemuVersion)
cmd += fmt.Sprintf("DEFAULT_QEMU_CMD='%s'\n", qemu_cmd)
cmd += fmt.Sprintf("DEFAULT_QEMU_CMD='%s'\n", qemuCmd)
cmd += `if [ -n "$STATE_FILE" ]; then\n`
cmd += " QEMU_VER=`echo $STATE_FILE" +
` | grep -o '_[[:digit:]]\+\.[[:digit:]]\+.*'` + "`\n"
@@ -330,8 +349,8 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
cpuType = "host"
}
if !hostinfo.Instance().IsNestedVirtualization() {
cpu_type += ",kvm=off"
if !guestManger.GetHost().IsNestedVirtualization() {
cpuType += ",kvm=off"
}
// TODO
@@ -340,13 +359,13 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
} else {
cmd += " -no-kvm"
accel = "tcg"
cpu_type = "qemu64"
cpuType = "qemu64"
}
cmd += fmt.Sprintf(" -cpu %s", cpu_type)
cmd += fmt.Sprintf(" -cpu %s", cpuType)
// TODO hmp - -
// cmd += s.getMonitorDesc("hmqmon", s.GetQmpMonitorPort(int(vncPort)), MODE_READLINE)
cmd += s.getMonitorDesc("hmqmon", s.GetQmpMonitorPort(int(vncPort)), MODE_READLINE)
if options.HostOptions.EnableQmpMonitor {
cmd += s.getMonitorDesc("qmqmon", s.GetQmpMonitorPort(int(vncPort)), MODE_CONTROL)
}
@@ -437,15 +456,15 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
diskDrivers = append(diskDrivers, driver)
}
if utils.IsInStringArray(DISK_DRIVER_SCSI, disk_drivers) {
if utils.IsInStringArray(DISK_DRIVER_SCSI, diskDrivers) {
cmd += " -device virtio-scsi-pci,id=scsi"
} else if utils.IsInStringArray(DISK_DRIVER_PVSCSI, disk_drivers) {
} else if utils.IsInStringArray(DISK_DRIVER_PVSCSI, diskDrivers) {
cmd += " -device pvscsi,id=scsi"
}
for _, disk := range disks {
format, _ := disk.GetString("format")
cmd += s.getDriveDesc(disk, disk["format"])
cmd += s.getDriveDesc(disk, format)
cmd += s.getVdiskDesc(disk)
}
@@ -479,19 +498,24 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
for _, nic := range nics {
if osname == OS_NAME_VMWARE {
nic.(jsonutils.JSONDict).Set("driver", jsonutils.NewString("vmxnet3"))
nic.(*jsonutils.JSONDict).Set("driver", jsonutils.NewString("vmxnet3"))
}
nicCmd, err := s.getNetdevDesc(nic)
if err != nil {
return "", err
} else {
cmd += nicCmd
}
cmd += s.getNetdevDesc(nic)
cmd += s.getVnicDesc(nic)
}
cmd += fmt.Sprintf(" -pidfile %s", S.GetPidFilePath())
cmd += fmt.Sprintf(" -pidfile %s", s.GetPidFilePath())
extraOptions, _ := s.Desc.GetMap("extra_options")
for k, v := range extraOptions {
cmd += fmt.Sprintf(" -%s %s", k, v.String())
}
cmd += self.getQgaDesc()
cmd += s.getQgaDesc()
if fileutils2.Exists("/dev/random") {
cmd += " -object rng-random,filename=/dev/random,id=rng0"
cmd += " -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000"
@@ -499,7 +523,7 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
if jsonutils.QueryBoolean(data, "need_migrate", false) {
migratePort := s.manager.GetFreePortByBase(LIVE_MIGRATE_PORT_BASE)
s.Desc.Set("live_migrate_dest_port", jsonutils.NewInt(migratePort))
s.Desc.Set("live_migrate_dest_port", jsonutils.NewInt(int64(migratePort)))
cmd += fmt.Sprintf(" -incoming tcp:0:%d", migratePort)
} else if jsonutils.QueryBoolean(s.Desc, "is_slave", false) {
cmd += fmt.Sprintf(" -incoming tcp:0:%d",
@@ -517,7 +541,7 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string
cmd += ` $CMD\n`
cmd += `fi\n`
return cmd
return cmd, nil
}
func (s *SKVMGuestInstance) generateStopScript(data *jsonutils.JSONDict) string {
@@ -526,9 +550,9 @@ func (s *SKVMGuestInstance) generateStopScript(data *jsonutils.JSONDict) string
nics, _ = s.Desc.GetArray("nics")
)
cmd = ""
cmd += fmt.Sprintf("VNC_FILE=%s\n", self.GetVncFilePath())
cmd += fmt.Sprintf("PID_FILE=%s\n", self.GetPidFilePath())
cmd := ""
cmd += fmt.Sprintf("VNC_FILE=%s\n", s.GetVncFilePath())
cmd += fmt.Sprintf("PID_FILE=%s\n", s.GetPidFilePath())
cmd += "if [ -f $VNC_FILE ]; then\n"
cmd += " VNC=`cat $VNC_FILE`\n"
@@ -558,8 +582,12 @@ func (s *SKVMGuestInstance) generateStopScript(data *jsonutils.JSONDict) string
}
for _, nic := range nics {
ifname, _ := nic.GetString("ifname")
downscript := self.getNicDownScriptPath(nic)
downscript := s.getNicDownScriptPath(nic)
cmd += fmt.Sprintf("%s %s\n", downscript, ifname)
}
return cmd
}
func (s *SKVMGuestInstance) StartPresendArp() {
// TODO go func
}
@@ -3,13 +3,18 @@ package hostbridge
import (
"fmt"
"net"
"os"
"os/exec"
"strings"
"syscall"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/util/bwutils"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/ovsutils"
)
@@ -21,6 +26,10 @@ type IBridgeDriver interface {
Interfaces() []string
GetMac() string
GenerateIfupScripts(scriptPath string, nic jsonutils.JSONObject) error
GenerateIfdownScripts(scriptPath string, nic jsonutils.JSONObject) error
RegisterHostlocalServer(mac, ip string) error
WarmupConfig() error
}
type SBaseBridgeDriver struct {
@@ -255,6 +264,233 @@ func (o *SOVSBridgeDriver) SetupBridgeDev() error {
return nil
}
func (o *SOVSBridgeDriver) GenerateIfdownScripts(scriptPath string, nic jsonutils.JSONObject) error {
script, err := o.getDownScripts(nic)
if err != nil {
log.Errorln(err)
return err
}
return o.saveFileExecutable(scriptPath, script)
}
func (o *SOVSBridgeDriver) GenerateIfupScripts(scriptPath string, nic jsonutils.JSONObject) error {
script, err := o.getUpScripts(nic)
if err != nil {
log.Errorln(err)
return err
}
return o.saveFileExecutable(scriptPath, script)
}
func (o *SOVSBridgeDriver) saveFileExecutable(scriptPath, script string) error {
if err := fileutils2.FilePutContents(scriptPath, script, false); err != nil {
return err
}
return os.Chmod(scriptPath, syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR)
}
func (o *SOVSBridgeDriver) getUpScripts(nic jsonutils.JSONObject) (string, error) {
var (
bridge, _ = nic.GetString("bridge")
ifname, _ = nic.GetString("ifname")
ip, _ = nic.GetString("ip")
mac, _ = nic.GetString("mac")
vlan, _ = nic.Int("vlan")
)
s := "#!/bin/bash\n\n"
s += fmt.Sprintf("SWITCH='%s'\n", bridge)
s += fmt.Sprintf("IF='%s'\n", ifname)
s += fmt.Sprintf("IP='%s'\n", ip)
s += fmt.Sprintf("MAC='%s'\n", mac)
s += fmt.Sprintf("VLAN_ID=%d\n", vlan)
limit, burst, err := bwutils.GetOvsBwValues(nic)
if err != nil {
return "", err
}
s += fmt.Sprintf("LIMIT=%d\n", limit)
s += fmt.Sprintf("BURST=%d\n", burst)
bwDownload, err := bwutils.GetDownloadBwValue(nic, options.HostOptions.BwDownloadBandwidth)
if err != nil {
return "", err
}
s += fmt.Sprintf("LIMIT_DOWNLOAD='%dmbit'\n", bwDownload)
if options.HostOptions.TunnelPaddingBytes > 0 {
s += fmt.Sprintf("/sbin/ifconfig $IF mtu %d\n",
1500+options.HostOptions.TunnelPaddingBytes)
}
s += "/sbin/ifconfig $IF 0.0.0.0 up\n"
s += "ovs-vsctl list-ifaces $SWITCH | grep -w $IF > /dev/null 2>&1\n"
s += "if [ $? -eq '0' ]; then\n"
s += " ovs-vsctl del-port $SWITCH $IF\n"
s += "fi\n"
s += "if [ \"$VLAN_ID\" -ne \"1\" ]; then\n"
s += " TAG=\"tag=$VLAN_ID\"\n"
s += "fi\n"
s += "ovs-vsctl add-port $SWITCH $IF $TAG\n"
s += "PORT=$(ovs-ofctl show $SWITCH | grep -w $IF)\n"
s += "PORT=$(echo $PORT | awk 'BEGIN{FS=\"(\"}{print $1}')\n"
s += "OFCTL=$(ovs-vsctl get-controller $SWITCH)\n"
s += `if [ -z "$OFCTL" ]; then\n`
s += " ovs-vsctl set Interface $IF ingress_policing_rate=$LIMIT\n"
s += " ovs-vsctl set Interface $IF ingress_policing_burst=$BURST\n"
for _, r := range o.GetOfRules(nic) {
s += " " + o.AddFlow(r.cond, r.priority, r.actions)
}
s += "fi\n"
s += `if [ $LIMIT_DOWNLOAD != "0mbit" ]; then\n`
s += " tc qdisc del dev $IF root 2>/dev/null\n"
s += " tc qdisc add dev $IF root handle 1: htb default 10\n"
s += " tc class add dev $IF parent 1: classid 1:1 htb " +
"rate $LIMIT_DOWNLOAD ceil $LIMIT_DOWNLOAD\n"
s += " tc class add dev $IF parent 1:1 classid 1:10 htb " +
"rate $LIMIT_DOWNLOAD ceil $LIMIT_DOWNLOAD\n"
s += "fi\n"
return s, nil
}
func (o *SOVSBridgeDriver) getDownScripts(nic jsonutils.JSONObject) (string, error) {
var (
bridge, _ = nic.GetString("bridge")
ifname, _ = nic.GetString("ifname")
ip, _ = nic.GetString("ip")
mac, _ = nic.GetString("mac")
vlan, _ = nic.Int("vlan")
)
s := "#!/bin/bash\n\n"
s += fmt.Sprintf("SWITCH='%s'\n", bridge)
s += fmt.Sprintf("IF='%s'\n", ifname)
s += fmt.Sprintf("IP='%s'\n", ip)
s += fmt.Sprintf("MAC='%s'\n", mac)
s += fmt.Sprintf("VLAN_ID=%d\n", vlan)
s += "PORT=$(ovs-ofctl show $SWITCH | grep -w $IF)\n"
s += "if [ $? -ne '0' ]; then\n"
s += " exit 0\n"
s += "fi\n"
s += "OFCTL=$(ovs-vsctl get-controller $SWITCH)\n"
s += "PORT=$(echo $PORT | awk 'BEGIN{FS=\"(\"}{print $1}')\n"
s += `if [ -z "$OFCTL" ]; then\n`
for _, r := range o.GetOfRules(nic) {
s += " " + o.DelFlow(r.cond)
}
s += "fi\n"
s += "/sbin/ifconfig $IF 0.0.0.0 down\n"
s += "ovs-vsctl -- --if-exists del-port $SWITCH $IF\n"
return s, nil
}
type SRule struct {
priority int
cond string
actions string
}
func (o *SOVSBridgeDriver) AddFlow(cond string, priority int, actions string) string {
s := ""
s += fmt.Sprintf("ovs-ofctl add-flow $SWITCH \"%s", cond)
s += fmt.Sprintf(" priority=%d", priority)
s += fmt.Sprintf(" actions=%s\"\n", actions)
return s
}
func (o *SOVSBridgeDriver) DoAddFlow(cond string, pri int, actions, swt string) error {
return exec.Command("ovs-ofctl", "add-flow", swt,
fmt.Sprintf("%s priority=%d actions=%s", cond, pri, actions)).Run()
}
func (o *SOVSBridgeDriver) DelFlow(cond string) string {
return fmt.Sprintf("ovs-ofctl del-flows $SWITCH \"%s\"\n", cond)
}
func (o *SOVSBridgeDriver) GetOfRules(nic jsonutils.JSONObject) []SRule {
rules := []SRule{}
metadataPort := o.GetMetadataServerPort()
rules = append(rules,
SRule{9000, fmt.Sprintf("table=0 in_port=local tcp nw_dst=$IP tp_src=%d", metadataPort),
"mod_nw_src=169.254.169.254,mod_tp_src:80,output:$PORT"},
SRule{9500, "table=0 in_port=$PORT udp tp_src=68 tp_dst=67", "local"},
SRule{8000, "table=0 in_port=$PORT", "resubmit(,1)"},
)
if vlan, _ := nic.Int("vlan"); vlan != 1 {
rules = append(rules,
SRule{4901, "table=1 dl_dst=$MAC,dl_vlan=$VLAN_ID", "strip_vlan,output:$PORT"})
}
rules = append(rules,
SRule{4900, "table=1 dl_dst=$MAC", "output:$PORT"})
return rules
}
func (o *SOVSBridgeDriver) GetMetadataServerPort() int {
return options.HostOptions.Port
}
func (o *SOVSBridgeDriver) RegisterHostlocalServer(mac, ip string) error {
if !options.HostOptions.EnableOpenflowController {
metadataPort := o.GetMetadataServerPort()
if err := o.DoAddFlow("table=0 ipv6", 20000, "drop", o.bridge.String()); err != nil {
return err
}
if err := o.DoAddFlow("table=0 tcp nw_dst=169.254.169.254 tp_dst=80", 10000,
fmt.Sprintf("mod_dl_dst:%s,mod_nw_dst:%s,mod_tp_dst:%d,local",
mac, ip, metadataPort),
o.bridge.String()); err != nil {
return err
}
log.Infof("OVS: metadata server %s:%d", ip, metadataPort)
k8sCidr := options.HostOptions.K8sClusterCidr
if len(k8sCidr) > 0 {
addr, mask, err := netutils2.PrefixSplit(k8sCidr)
if err != nil {
log.Errorln(err)
return err
}
k8sCidr = fmt.Sprintf("%s/%d", addr, mask)
err = o.DoAddFlow(fmt.Sprintf("table=0 ip,nw_dst=%s", k8sCidr),
10050, fmt.Sprintf("mod_dl_dst:%s,local", mac), o.bridge.String())
if err != nil {
return err
}
err = o.DoAddFlow("table=0", 0, "resubmit(,1)", o.bridge.String())
if err != nil {
return err
}
err = o.DoAddFlow("table=1", 0, "normal", o.bridge.String())
if err != nil {
return err
}
}
}
return nil
}
func (o *SOVSBridgeDriver) ovsSetParams(params map[string]map[string]string) {
for tbl, tblval := range params {
for k, v := range tblval {
exec.Command("ovs-vsctl", "set", tbl, o.bridge.String(),
fmt.Sprintf("%s=%s", k, v)).Run()
}
}
}
func (o *SOVSBridgeDriver) WarmupConfig() error {
// if options.OvsSflowBridges ...
if options.HostOptions.EnableOpenflowController {
// ...
} else {
params := map[string]map[string]string{
"bridge": {
"stp_enable": "false",
"fail_mode": "standalone",
"other-config:flow-eviction-threshold": "2500",
},
}
o.ovsSetParams(params)
}
return nil
}
func CleanOvsBridge() {
ovsutils.CleanAllHiddenPorts()
}
+112 -28
View File
@@ -18,12 +18,14 @@ import (
bare2 "yunion.io/x/onecloud/pkg/baremetal"
"yunion.io/x/onecloud/pkg/cloudcommon/storagetypes"
bare1 "yunion.io/x/onecloud/pkg/compute/baremetal"
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostbridge"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/storageman"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/cgrouputils"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/qemutils"
"yunion.io/x/onecloud/pkg/util/sysutils"
@@ -41,8 +43,11 @@ var (
)
type SHostInfo struct {
isRegistered bool
IsRegistered chan struct{}
isRegistered bool
IsRegistered chan struct{}
registerCallback func()
saved bool
pinger *SHostPingTask
kvmModuleSupport string
nestStatus string
@@ -65,6 +70,19 @@ type SHostInfo struct {
FullName string
}
func (h *SHostInfo) GetBridgeDev(bridge string) hostbridge.IBridgeDriver {
for _, n := range h.Nics {
if bridge == n.Bridge {
return n.BridgeDev
}
}
return nil
}
func (h *SHostInfo) GetHostId() string {
return h.HostId
}
func (h *SHostInfo) GetZone() string {
return h.Zone
}
@@ -632,6 +650,7 @@ func (h *SHostInfo) GetMasterMac() string {
return n.BridgeDev.GetMac()
}
}
return ""
}
func (h *SHostInfo) GetMatchNic(bridge, iface, mac string) *SNIC {
@@ -644,18 +663,22 @@ func (h *SHostInfo) GetMatchNic(bridge, iface, mac string) *SNIC {
return nil
}
func (h *SHostInfo) StartRegister(delay int) {
timeutils2.AddTimeout(delay*time.Second, h.register)
func (h *SHostInfo) StartRegister(delay int, callback func()) {
if callback != nil {
h.registerCallback = callback
}
timeutils2.AddTimeout(time.Duration(delay)*time.Second, h.register)
}
func (h *SHostInfo) register() {
if !h.isRegistered {
h.fetch_access_network_info()
h.fetchAccessNetworkInfo()
}
}
func (h *SHostInfo) onFail() {
h.StartRegister(30)
h.StartRegister(30, nil)
panic("register failed, try 30 seconds later...")
}
@@ -689,13 +712,13 @@ func (h *SHostInfo) getZoneInfo(zoneId string, standalone bool) {
zoneId, params)
if err != nil {
log.Errorln(err)
h.onFail(err)
h.onFail()
}
h.Zone, _ = res.GetString("name")
h.ZoneId, _ = res.GetString("id")
h.Cloudregion = res.GetString("cloudregion")
h.CloudregionId = res.GetString("cloudregion_id")
h.Cloudregion, _ = res.GetString("cloudregion")
h.CloudregionId, _ = res.GetString("cloudregion_id")
if res.Contains("manager_uri") {
h.ZoneManagerUri, _ = res.GetString("manager_uri")
}
@@ -735,11 +758,11 @@ func (h *SHostInfo) setHostname(name string) {
}
}
func (h *SHostInfo) fetchHostname() {
func (h *SHostInfo) fetchHostname() string {
if len(options.HostOptions.Hostname) > 0 {
return options.HostOptions.Hostname
} else {
masterIp = h.GetMasterIp()
masterIp := h.GetMasterIp()
return "host-" + masterIp
}
}
@@ -782,7 +805,7 @@ func (h *SHostInfo) updateHostRecord(hostId string) {
content.Set("mem_size", jsonutils.NewInt(int64(h.Mem.MemInfo.Total)))
content.Set("storage_driver", jsonutils.NewString(bare1.DISK_DRIVER_LINUX))
content.Set("storage_type", jsonutils.NewString(h.sysinfo.StorageType))
content.Set("storage_size", jsonutils.NewInt(storageman.GetManager().GetTotalCapacity()))
content.Set("storage_size", jsonutils.NewInt(int64(storageman.GetManager().GetTotalCapacity())))
// TODO optimize content data struct
content.Set("sys_info", jsonutils.Marshal(h.sysinfo))
@@ -800,7 +823,7 @@ func (h *SHostInfo) updateHostRecord(hostId string) {
body.Set("host", content)
session := hostutils.GetComputeSession(context.Background())
_, res, err := session.JSONVersionRequest("compute",
session.GetEndpointType(), method, url, nil, body, "v2")
session.GetEndpointType(), httputils.THttpMethod(method), url, nil, body, "v2")
if err != nil {
log.Errorln(err)
h.onFail()
@@ -843,7 +866,7 @@ func (h *SHostInfo) getReservedMem() int64 {
}
func (h *SHostInfo) putHostOffline() {
res, err := modules.Hosts.PerformAction(
_, err := modules.Hosts.PerformAction(
hostutils.GetComputeSession(context.Background()), h.HostId, "offline", nil)
if err != nil {
log.Errorln(err)
@@ -853,6 +876,13 @@ func (h *SHostInfo) putHostOffline() {
}
}
func (h *SHostInfo) PutHostOnline() error {
_, err := modules.Hosts.PerformAction(
hostutils.GetComputeSession(context.Background()),
h.HostId, "online", nil)
return err
}
func (h *SHostInfo) getNetworkInfo() {
params := jsonutils.NewDict()
params.Set("details", jsonutils.JSONTrue)
@@ -872,7 +902,7 @@ func (h *SHostInfo) getNetworkInfo() {
if nic != nil {
wire, _ := hostwire.GetString("wire")
wireId, _ := hostwire.GetString("wire_id")
bandwidth, err := hostwire.GetString("bandwidth")
bandwidth, err := hostwire.Int("bandwidth")
if err != nil {
bandwidth = 1000
}
@@ -912,7 +942,7 @@ func (h *SHostInfo) uploadNetworkInfo() {
h.getStoragecacheInfo()
}
func (h *SHostInfo) doUploadNicInfo(nic *SNIC) (jsonutils.JSONObject, error) {
func (h *SHostInfo) doUploadNicInfo(nic *SNIC) {
log.Infof("Upload NIC br:%s if:%s", nic.Bridge, nic.Inter)
content := jsonutils.NewDict()
content.Set("mac", jsonutils.NewString(nic.BridgeDev.GetMac()))
@@ -959,9 +989,9 @@ func (h *SHostInfo) onUploadNicInfoSucc(nic *SNIC) {
macAddr, _ := res.GetString("mac_addr")
nic = h.GetMatchNic(bridge, iface, macAddr)
if nic != nil {
wire, _ := hostwire.GetString("wire")
wireId, _ := hostwire.GetString("wire_id")
bandwidth, err := hostwire.GetString("bandwidth")
wire, _ := res.GetString("wire")
wireId, _ := res.GetString("wire_id")
bandwidth, err := res.Int("bandwidth")
if err != nil {
bandwidth = 1000
}
@@ -1027,21 +1057,22 @@ func (h *SHostInfo) getStorageInfo() {
func (h *SHostInfo) onGetStorageInfoSucc(hoststorages []jsonutils.JSONObject) {
var detachStorages = []jsonutils.JSONObject{}
storageManager := storageman.GetManager()
for _, hs := range hoststorages {
mountPoint, _ := hs.GetString("mount_point")
storagecacheId, _ := hs.GetString("storagecache_id")
storagetype, _ := hs.GetString("storage_type")
storageManager := storageman.GetManager()
imagecachePath, _ := hs.GetString("imagecache_path")
storageId, _ := hs.GetString("storage_id")
storageName, _ := hs.GetString("storage")
storageConf, _ := hs.Get("storage_conf")
storage := storageManager.NewSharedStorageInstance(mountPoint, storagetype)
if s != nil {
storageManager.Storages = append(storageManager.Storages, s)
if storage != nil {
storageManager.Storages = append(storageManager.Storages, storage)
}
storageManager.InitStorageImageCache(storagetype, storagecacheId, imagecachePath, s)
storageManager.InitSharedStorageImageCache(storagetype,
storagecacheId, imagecachePath, storage)
storage = storageManager.GetStorageByPath(mountPoint)
if storage != nil {
storage.SetStorageInfo(storageId, storageName, storageConf)
@@ -1068,7 +1099,7 @@ func (h *SHostInfo) uploadStorageInfo() {
}
}
// TODO
h.getIsolatedDevices()
// h.getIsolatedDevices()
}
func (h *SHostInfo) onSyncStorageInfoSucc(storage storageman.IStorage, storageInfo jsonutils.JSONObject) {
@@ -1097,17 +1128,69 @@ func (h *SHostInfo) onSucc() {
log.Infof("Host registration process success....")
h.isRegistered = true
// TODO
h.save()
if err := h.save(); err != nil {
panic(err.Error())
}
// TODO
h.StartPinger()
if h.registerCallback != nil {
h.registerCallback()
}
// To notify caller, host register is success
close(h.IsRegistered)
}
}
func (h *SHostInfo) StartPinger() {
h.pinger = NewHostPingTask(options.HostOptions.PingRegionInterval)
go h.pinger.Start()
}
func (h *SHostInfo) save() error {
if h.saved {
return nil
}
h.saved = true
if err := h.registerHostlocalServer(); err != nil {
return err
}
// TODO XXX >>> ???
// file put content
return h.setupBridges()
}
func (h *SHostInfo) setupBridges() error {
for _, n := range h.Nics {
if err := n.BridgeDev.WarmupConfig(); err != nil {
return err
}
}
return nil
}
func (h *SHostInfo) registerHostlocalServer() error {
for _, n := range h.Nics {
mac := h.GetMasterMac()
if len(mac) == 0 {
panic("len mac == 0")
}
ip := h.GetMasterIp()
if len(ip) == 0 {
panic("len ip == 0")
}
err := n.BridgeDev.RegisterHostlocalServer(mac, ip)
if err != nil {
log.Errorln(err)
return err
}
}
return nil
}
func NewHostInfo() (*SHostInfo, error) {
var res = new(SHostInfo)
res.sysinfo = &SSysInfo{}
@@ -1134,9 +1217,10 @@ var hostInfo *SHostInfo
func Instance() *SHostInfo {
if hostInfo == nil {
hostInfo, err := NewHostInfo()
var err error
hostInfo, err = NewHostInfo()
if err != nil {
log.Fatalf(err)
log.Fatalf(err.Error())
}
}
return hostInfo
+13
View File
@@ -0,0 +1,13 @@
package hostinfo
type SHostPingTask struct {
interval int
}
func NewHostPingTask(interval int) *SHostPingTask {
return &SHostPingTask{interval}
}
func (p *SHostPingTask) Start() {
//TODO
}
+14
View File
@@ -11,6 +11,7 @@ import (
"yunion.io/x/onecloud/pkg/appctx"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/workmanager"
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostbridge"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -18,6 +19,19 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
type IHost interface {
GetZone() string
GetHostId() string
GetMediumType() string
IsKvmSupport() bool
IsNestedVirtualization() bool
PutHostOnline() error
GetBridgeDev(bridge string) hostbridge.IBridgeDriver
}
func GetComputeSession(ctx context.Context) *mcclient.ClientSession {
return auth.GetAdminSession(ctx, options.HostOptions.Region, "v2")
}
+24
View File
@@ -311,3 +311,27 @@ func (m *HmpMonitor) GetBlockJobs(callback func(jobs int)) {
func (m *HmpMonitor) ReloadDiskBlkdev(device, path string, callback StringCallback) {
m.Query(fmt.Sprintf("reload_disk_snapshot_blkdev -n %s %s", device, path), callback)
}
func (m *HmpMonitor) DriveMirror(callback StringCallback, drive, target, syncMode string, unmap bool) {
cmd := "drive_mirror -n"
if syncMode == "full" {
cmd += " -f"
}
cmd += fmt.Sprintf(" %s %s", drive, target)
m.Query(cmd, callback)
}
func (m *HmpMonitor) BlockStream(drive string, callback StringCallback) {
var (
speed = 30 // MB/s
cmd = fmt.Sprintf("block_stream %s %d", drive, speed)
)
m.Query(cmd, callback)
}
func (m *HmpMonitor) SetVncPassword(proto, password string, callback StringCallback) {
if len(password) > 8 {
password = password[:8]
}
m.Query(fmt.Sprintf("set_password %s %s", proto, password), callback)
}
+5 -1
View File
@@ -14,7 +14,7 @@ type StringCallback func(string)
type Monitor interface {
Connect(host string, port int) error
Dicconnect()
Disconnect()
IsConnected() bool
// The callback function will be called in another goroutine
@@ -33,11 +33,15 @@ type Monitor interface {
DriveAdd(bus string, params map[string]string, callback StringCallback)
DeviceAdd(dev string, params map[string]interface{}, callback StringCallback)
BlockStream(drive string, callback StringCallback)
DriveMirror(callback StringCallback, drive, target, syncMode string, unmap bool)
MigrateSetCapability(capability, state string, callback StringCallback)
Migrate(destStr string, copyIncremental, copyFull bool, callback StringCallback)
GetMigrateStatus(callback StringCallback)
ReloadDiskBlkdev(device, path string, callback StringCallback)
SetVncPassword(proto, password string, callback StringCallback)
}
type MonitorErrorFunc func(error)
+55
View File
@@ -563,3 +563,58 @@ func (m *QmpMonitor) ReloadDiskBlkdev(device, path string, callback StringCallba
)
m.Query(cmd, cb)
}
func (m *QmpMonitor) DriveMirror(callback StringCallback, drive, target, syncMode string, unmap bool) {
var (
cb = func(res *Response) {
callback(m.actionResult(res))
}
cmd = &Command{
Execute: "drive-mirror",
Args: map[string]interface{}{
"device": drive,
"target": target,
"mode": "existing",
"sync": syncMode,
"unmap": unmap,
},
}
)
m.Query(cmd, cb)
}
func (m *QmpMonitor) BlockStream(drive string, callback StringCallback) {
var (
speed = 30 // MB/s
cb = func(res *Response) {
callback(m.actionResult(res))
}
cmd = &Command{
Execute: "block-stream",
Args: map[string]interface{}{
"device": drive,
"speed": speed,
},
}
)
m.Query(cmd, cb)
}
func (m *QmpMonitor) SetVncPassword(proto, password string, callback StringCallback) {
if len(password) > 8 {
password = password[:8]
}
var (
cb = func(res *Response) {
callback(m.actionResult(res))
}
cmd = &Command{
Execute: "set_password",
Args: map[string]interface{}{
"protocol": proto,
"password": password,
},
}
)
m.Query(cmd, cb)
}
+6
View File
@@ -71,6 +71,12 @@ type SHostOptions struct {
DefaultWriteBpsPerCpu int `default:"54525952" help:"Default write bps per cpu for hard IO limit"`
DefaultWriteIopsPerCpu int `default:"416" help:"Default write iops per cpu for hard IO limit"`
SetVncPassword bool `default:"true" help:"Auto set vnc password after monitor connected"`
EnableCpuBinding bool `default:"true" help:"Enable cpu binding and rebalance"`
EnableOpenflowController bool `default:"false"`
K8sClusterCidr string `default:"10.43.0.0/16" help:"Kubernetes cluster IP range"`
PingRegionInterval int `default:"60" help:"interval to ping region, deefault is 1 minute"`
}
var HostOptions SHostOptions
+10 -14
View File
@@ -23,42 +23,38 @@ type SHostService struct {
}
func (host *SHostService) StartService() {
cloudcommon.ParseOptions(&options.HostOptions, &options.HostOptions.CommonOptions, os.Args, "host.conf")
cloudcommon.ParseOptions(&options.HostOptions, os.Args, "host.conf", "host")
// isolatedman.Init()
hostInstance := hostinfo.Instance()
if err := hostInstance.Init(); err != nil {
log.Fatalf(err)
log.Fatalf(err.Error())
}
if err := storageman.Init(hostInstance.HostId, hostInstance.Zone); err != nil {
log.Fatalf(err)
if err := storageman.Init(hostInstance); err != nil {
log.Fatalf(err.Error())
}
// wait host registerd
// Firewall.Init()
guestman.Init(hostInstance, options.HostOptions.ServersPath)
var c = make(chan struct{})
cloudcommon.InitAuth(&options.HostOptions.Options, func() {
cloudcommon.InitAuth(&options.HostOptions.CommonOptions, func() {
log.Infof("Auth complete!!")
hostinfo.Instance().StartRegister()
hostInstance.StartRegister(5, guestman.GetGuestManager().Bootstrap)
<-hostinfo.Instance().IsRegistered
guestman.Init(options.HostOptions.ServersPath)
guestman.GetGuestManager().Bootstrap()
close(c)
})
app := cloudcommon.InitApp(&options.HostOptions.Options)
app := cloudcommon.InitApp(&options.HostOptions.CommonOptions, false)
host.TrapSignals(func() { host.quitSignalHandler(app) })
host.InitHandlers(app)
host.initHandlers(app)
<-c // wait host and guest init
cloudcommon.ServeForever(app, &options.HostOptions)
cloudcommon.ServeForever(app, &options.HostOptions.CommonOptions)
}
func (host *SHostService) quitSignalHandler(app *appsrv.Application) {
+5 -16
View File
@@ -7,20 +7,14 @@ import (
"strings"
"yunion.io/x/onecloud/pkg/cloudcommon/storagetypes"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/options"
)
const MINIMAL_FREE_SPACE = 128
type IHost interface {
GetZone() string
GetHostId() string
GetMediumType() string
}
type SStorageManager struct {
host IHost
host hostutils.IHost
Storages []IStorage
AgentStorage IStorage
@@ -32,7 +26,7 @@ type SStorageManager struct {
NfsStorageImagecacheManagers map[string]IImageCacheManger
}
func NewStorageManager(host IHost) (*SStorageManager, error) {
func NewStorageManager(host hostutils.IHost) (*SStorageManager, error) {
var (
ret = &SStorageManager{
host: host,
@@ -276,13 +270,8 @@ func Manager() *SStorageManager {
return storageManager
}
func Init(host IHost) error {
var (
err error
zone = host.GetZone()
hostId = host.GetHostId()
)
func Init(host hostutils.IHost) error {
var err error
storageManager, err = NewStorageManager(host)
return err
}
+12 -14
View File
@@ -12,34 +12,32 @@ import (
)
type IDisk interface {
GetType() string
GetId() string
Probe() error
GetPath() string
GetSnapshotDir() string
GetDiskDesc() jsonutils.JSONObject
GetDiskSetupScripts(idx int) string
// TODO
// DeleteAllSnapshot() error
DeleteAllSnapshot() error
Delete(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
Resize(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
CleanupSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
GetPath() string
GetSnapshotDir() string
PrepareMigrate(liveMigrate bool) (string, error)
CreateFromUrl(context.Context, string) error
// CreateFromSnapshot
CreateFromTemplate(context.Context, string, string, int64) (jsonutils.JSONObject, error)
CreateFromImageFuse(context.Context, string) error
CreateRaw(ctx context.Context, sizeMb int, diskFromat string, fsFormat string,
encryption bool, diskId string, back string) (jsonutils.JSONObject, error)
CreateSnapshot(snapshotId string) error
DeleteSnapshot(snapshotId string) error
// @params: diskPath, guestDesc, deployInfo
DeployGuestFs(string, *jsonutils.JSONDict, *guestfs.SDeployInfo) (jsonutils.JSONObject, error)
PostCreateFromImageFuse()
CreateSnapshot(snapshotId string) error
DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error
DeployGuestFs(diskPath string, guestDesc *jsonutils.JSONDict,
deployInfo *guestfs.SDeployInfo) (jsonutils.JSONObject, error)
}
type SBaseDisk struct {
+53 -5
View File
@@ -18,9 +18,13 @@ import (
var (
keyWords = []string{"disks"}
actionFuncs = map[string]actionFunc{
"create": diskCreate,
"delete": diskDelete,
"resize": diskResize,
"create": diskCreate,
"delete": diskDelete,
"resize": diskResize,
"save-prepare": diskSavePrepare,
"reset": diskReset,
// "snapshot": diskSnapshot,
"cleanup-snapshots": diskCleanupSnapshots,
}
)
@@ -151,7 +155,7 @@ func perfomrDiskActions(ctx context.Context, w http.ResponseWriter, r *http.Requ
func diskCreate(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
diskInfo, err := body.Get("disk")
if err != nil {
return nil, httperrors.NewInputParameterError("Missing disk")
return nil, httperrors.NewMissingParameterError("disk")
}
hostutils.DelayTask(ctx, storage.CreateDiskByDiskinfo,
&SDiskCreateByDiskinfo{diskId, disk, diskInfo, storage})
@@ -166,8 +170,52 @@ func diskDelete(ctx context.Context, storage IStorage, diskId string, disk IDisk
func diskResize(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
diskInfo, err := body.Get("disk")
if err != nil {
return nil, httperrors.NewInputParameterError("Missing disk")
return nil, httperrors.NewMissingParameterError("disk")
}
hostutils.DelayTask(ctx, disk.Resize, diskInfo)
return nil, nil
}
func diskSavePrepare(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
diskInfo, err := body.Get("disk")
if err != nil {
return nil, httperrors.NewMissingParameterError("disk")
}
hostutils.DelayTask(ctx, disk.PrepareSaveToGlance, diskInfo)
return nil, nil
}
func diskReset(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
snapshotId, err := body.GetString("snapshot_id")
if err != nil {
return nil, httperrors.NewMissingParameterError("snapshot_id")
}
outOfChain, err := body.Bool("out_of_chain")
if err != nil {
return nil, httperrors.NewMissingParameterError("out_of_chain")
}
hostutils.DelayTask(ctx, disk.ResetFromSnapshot, &SDiskReset{snapshotId, outOfChain})
return nil, nil
}
// func diskSnapshot(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
// snapshotId, err := body.GetString("snapshot_id")
// if err != nil {
// return nil, httperrors.NewMissingParameterError("snapshot_id")
// }
// hostutils.DelayTask(ctx, disk.CreateSnapshot(snapshotId), snapshotId)
// return nil, nil
// }
func diskCleanupSnapshots(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) {
convertSnapshots, err := body.GetArray("convert_snapshots")
if err != nil {
return nil, httperrors.NewMissingParameterError("convert_snapshots")
}
deleteSnapshots, err := body.GetArray("delete_snapshots")
if err != nil {
return nil, httperrors.NewMissingParameterError("delete_snapshots")
}
hostutils.DelayTask(ctx, disk.CleanupSnapshots, &SDiskCleanupSnapshots{convertSnapshots, deleteSnapshots})
return nil, nil
}
+205
View File
@@ -9,6 +9,8 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/appctx"
"yunion.io/x/onecloud/pkg/cloudcommon/storagetypes"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/storageman/remotefile"
@@ -32,6 +34,10 @@ func NewLocalDisk(storage IStorage, id string) *SLocalDisk {
return ret
}
func (d *SBaseDisk) GetType() string {
return storagetypes.STORAGE_LOCAL
}
func (d *SLocalDisk) getPath() string {
return path.Join(d.Storage.GetPath(), d.Id)
}
@@ -302,3 +308,202 @@ func (d *SLocalDisk) PostCreateFromImageFuse() {
log.Errorln(err)
}
}
func (d *SLocalDisk) CreateSnapshot(snapshotId string) error {
snapshotDir := d.GetSnapshotDir()
if !fileutils2.Exists(snapshotDir) {
err := exec.Command("mkdir", "-p", snapshotDir).Run()
if err != nil {
log.Errorln(err)
return err
}
}
snapshotPath := path.Join(snapshotDir, snapshotId)
err := exec.Command("mv", "-f", d.getPath(), snapshotPath).Run()
if err != nil {
log.Errorln(err)
return err
}
img, err := qemuimg.NewQemuImage(d.getPath())
if err != nil {
log.Errorln(err)
exec.Command("mv", "-f", snapshotPath, d.getPath()).Run()
return err
}
if err := img.CreateQcow2(0, false, snapshotPath); err != nil {
log.Errorf("Snapshot create image error %s", err)
exec.Command("mv", "-f", snapshotPath, d.getPath()).Run()
return err
}
return nil
}
func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error {
snapshotDir := d.GetSnapshotDir()
if len(convertSnapshot) > 0 {
if !fileutils2.Exists(snapshotDir) {
err := exec.Command("mkdir", "-p", snapshotDir).Run()
if err != nil {
log.Errorln(err)
return err
}
}
convertSnapshotPath := path.Join(snapshotDir, convertSnapshot)
output := convertSnapshotPath + ".tmp"
if fileutils2.Exists(output) {
exec.Command("rm", "-f", output).Run()
}
img, err := qemuimg.NewQemuImage(convertSnapshotPath)
if err != nil {
log.Errorln(err)
return err
}
if err = img.Convert2Qcow2To(output, true); err != nil {
log.Errorln(err)
exec.Command("rm", "-f", output).Run()
return err
}
if err = exec.Command("rm", "-f", convertSnapshotPath).Run(); err != nil {
log.Errorln(err)
return err
}
if err = exec.Command("mv", "-f", output, convertSnapshotPath).Run(); err != nil {
log.Errorln(err)
return err
}
if !pendingDelete {
err = exec.Command("rm", "-f", path.Join(snapshotDir, snapshotId)).Run()
if err != nil {
log.Errorln(err)
return err
}
}
return nil
} else {
err := exec.Command("rm", "-f", path.Join(snapshotDir, snapshotId)).Run()
if err != nil {
log.Errorln(err)
return err
}
return nil
}
}
func (d *SLocalDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
// diskInfo, ok := params.(*jsonutils.JSONDict)
// if !ok {
// return nil, hostutils.ParamsError
// }
if err := d.Probe(); err != nil {
return nil, err
}
destDir := d.Storage.GetImgsaveBackupPath()
if err := exec.Command("mkdir", "-p", destDir).Run(); err != nil {
log.Errorln(err)
return nil, err
}
backupPath := path.Join(destDir, fmt.Sprintf("%s.%s", d.Id, appctx.AppContextTaskId(ctx)))
if err := exec.Command("cp", "--sparse=always", "-f", d.GetPath(), backupPath).Run(); err != nil {
log.Errorln(err)
exec.Command("rm", "-f", backupPath).Run()
return nil, err
}
return jsonutils.NewDict(jsonutils.NewPair("backup", jsonutils.NewString(backupPath))), nil
}
func (d *SLocalDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
resetParams, ok := params.(*SDiskReset)
if !ok {
return nil, hostutils.ParamsError
}
snapshotDir := d.GetSnapshotDir()
snapshotPath := path.Join(snapshotDir, resetParams.SnapshotId)
diskTmpPath := d.GetPath() + "_reset.tmp"
if err := exec.Command("mv", "-f", d.GetPath(), diskTmpPath).Run(); err != nil {
log.Errorln(err)
return nil, err
}
if !resetParams.OutOfChain {
img, err := qemuimg.NewQemuImage(d.GetPath())
if err != nil {
log.Errorln(err)
exec.Command("mv", "-f", diskTmpPath, d.GetPath()).Run()
return nil, err
}
if err := img.CreateQcow2(0, false, snapshotPath); err != nil {
log.Errorln(err)
exec.Command("mv", "-f", diskTmpPath, d.GetPath()).Run()
return nil, err
}
} else {
if err := exec.Command("cp", "-f", snapshotPath, d.GetPath()).Run(); err != nil {
log.Errorln(err)
exec.Command("mv", "-f", diskTmpPath, d.GetPath()).Run()
return nil, err
}
}
return nil, exec.Command("rm", "-f", diskTmpPath).Run()
}
func (d *SLocalDisk) CleanupSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
cleanupParams, ok := params.(*SDiskCleanupSnapshots)
if !ok {
return nil, hostutils.ParamsError
}
snapshotDir := d.GetSnapshotDir()
for _, snapshotId := range cleanupParams.ConvertSnapshots {
snapId, _ := snapshotId.GetString()
snapshotPath := path.Join(snapshotDir, snapId)
output := snapshotPath + "_convert.tmp"
img, err := qemuimg.NewQemuImage(snapshotPath)
if err != nil {
log.Errorln(err)
return nil, err
}
if err = img.Convert2Qcow2To(output, true); err != nil {
log.Errorln(err)
return nil, err
}
if exec.Command("mv", "-f", output, snapshotPath).Run(); err != nil {
exec.Command("rm", "-f", output).Run()
log.Errorln(err)
return nil, err
}
}
for _, snapshotId := range cleanupParams.DeleteSnapshots {
snapId, _ := snapshotId.GetString()
if err := exec.Command("rm", "-f", path.Join(snapshotDir, snapId)).Run(); err != nil {
log.Errorln(err)
return nil, err
}
}
return nil, nil
}
func (d *SLocalDisk) DeleteAllSnapshot() error {
snapshotDir := d.GetSnapshotDir()
log.Infof("Delete disk(%s) snapshot dir %s", d.Id, snapshotDir)
return exec.Command("rm", "-rf", snapshotDir).Run()
}
func (d *SLocalDisk) PrepareMigrate(liveMigrate bool) (string, error) {
disk, err := qemuimg.NewQemuImage(d.getPath())
if err != nil {
log.Errorln(err)
return "", err
}
ret, err := disk.WholeChainFormatIs("qcow2")
if err != nil {
log.Errorln(err)
return "", err
}
if liveMigrate && !ret {
return "", fmt.Errorf("Disk format doesn't support live migrate")
}
if disk.IsChained() {
return disk.BackFilePath, nil
}
return "", nil
}
-1
View File
@@ -90,7 +90,6 @@ func (d *SKVMGuestDisk) findPartitions() error {
if err != nil {
return err
}
var partitions []*guestfs.SKVMGuestDiskPartition
for i := 0; i < len(files); i++ {
if files[i].Name() != dev && strings.HasPrefix(files[i].Name(), dev+"p") {
var part = guestfs.NewKVMGuestDiskPartition(path.Join(devpath, files[i].Name()))
+8 -5
View File
@@ -24,7 +24,7 @@ type IImageCacheManger interface {
AcquireImage(ctx context.Context, imageId, zone, srcUrl, format string) IImageCache
ReleaseImage(imageId string)
// LoadCache() error ???
LoadImageCache(imageId string)
}
type SBaseImageCacheManager struct {
@@ -78,12 +78,12 @@ func (c *SLocalImageCacheManager) loadCache() {
files, _ := ioutil.ReadDir(c.cachePath)
for _, f := range files {
if regutils.MatchUUIDExact(f.Name()) {
c.loadImageCache(f.Name())
c.LoadImageCache(f.Name())
}
}
}
func (c *SLocalImageCacheManager) loadImageCache(imageId string) {
func (c *SLocalImageCacheManager) LoadImageCache(imageId string) {
imageCache := NewLocalImageCache(imageId, c)
if imageCache.Load() {
c.cachedImages[imageId] = imageCache
@@ -155,7 +155,10 @@ func (c *SLocalImageCacheManager) PrefetchImageCache(ctx context.Context, data i
res.Set("image_id", jsonutils.NewString(imageId))
res.Set("path", jsonutils.NewString(imgCache.GetPath()))
var name, size = "", 0
var (
name string
size int64
)
if desc := imgCache.GetDesc(); desc != nil {
name = desc.Name
size = desc.Size
@@ -175,7 +178,6 @@ func (c *SLocalImageCacheManager) PrefetchImageCache(ctx context.Context, data i
} else {
return nil, fmt.Errorf("Failed to fetch image %s", imageId)
}
}
// TODO: AgentImageCacheManager
@@ -195,4 +197,5 @@ type SRbdImageCacheManager struct {
func NewRbdImageCacheManager() *SRbdImageCacheManager {
// TODO
return nil
}
+10 -6
View File
@@ -3,7 +3,6 @@ package storageman
import (
"context"
"fmt"
"path"
"sync"
"syscall"
@@ -22,7 +21,7 @@ type IStorage interface {
GetZone() string
SetStorageInfo(storageId, storageName string, conf jsonutils.JSONObject)
SyncStorageInfo()
SyncStorageInfo() (jsonutils.JSONObject, error)
StorageType() string
SetPath(string)
@@ -47,6 +46,7 @@ type IStorage interface {
DeleteDiskfile(diskPath string) error
GetFuseTmpPath() string
GetFuseMountPath() string
GetImgsaveBackupPath() string
}
type SBaseStorage struct {
@@ -162,11 +162,11 @@ func (s *SBaseStorage) CreateDiskByDiskinfo(ctx context.Context, params interfac
switch {
case createParams.DiskInfo.Contains("snapshot"):
return s.CreateDiskFromSnpashot(ctx, disk, createParams) // TODO
return s.CreateDiskFromSnpashot(ctx, disk, createParams)
case createParams.DiskInfo.Contains("image_id"):
return s.CreateDiskFromTemplate(ctx, disk, createParams) // TODO
return s.CreateDiskFromTemplate(ctx, disk, createParams)
case createParams.DiskInfo.Contains("size"):
return s.CreateRawDisk(ctx, disk, createParams) // TODO
return s.CreateRawDisk(ctx, disk, createParams)
default:
return nil, fmt.Errorf("Not fount")
}
@@ -193,7 +193,7 @@ func (s *SBaseStorage) CreateDiskFromTemplate(ctx context.Context, disk IDisk, c
func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, createParams *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
var (
diskPath = path.Join(s.Path, createParams.DiskId)
// diskPath = path.Join(s.Path, createParams.DiskId)
snapshotUrl, _ = createParams.DiskInfo.GetString("snapshot_url")
transferProtocol, _ = createParams.DiskInfo.GetString("url")
)
@@ -219,3 +219,7 @@ func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, c
}
return disk.GetDiskDesc(), nil
}
func (s *SLocalStorage) GetImgsaveBackupPath() string {
return s.getSubdirPath(_IMGSAVE_BACKUPS_)
}
+5 -5
View File
@@ -32,9 +32,9 @@ func AddStorageHandler(prefix string, app *appsrv.Application) {
auth.Authenticate(storageActions))
// TODO
app.AddHandler("POST",
fmt.Sprintf("%s/%s/<storageId>/delete-snapshots", prefix, keyWords),
auth.Authenticate(storageDeleteSnapshots))
// app.AddHandler("POST",
// fmt.Sprintf("%s/%s/<storageId>/delete-snapshots", prefix, keyWords),
// auth.Authenticate(storageDeleteSnapshots))
}
}
@@ -42,7 +42,7 @@ func storageActions(ctx context.Context, w http.ResponseWriter, r *http.Request)
params, _, body := appsrv.FetchEnv(ctx, w, r)
var action = params["<action>"]
if f, ok := storageActionFunc[action]; !ok {
if f, ok := storageActionFuncs[action]; !ok {
hostutils.Response(ctx, w, httperrors.NewNotFoundError("Not found"))
} else {
res, err := f(ctx, body)
@@ -106,7 +106,7 @@ func storageUpdate(ctx context.Context, body jsonutils.JSONObject) (interface{},
storage := storageManager.GetStorage(storageId)
ret, err := modules.Hoststorages.Get(hostutils.GetComputeSession(context.Background()),
storageManager.GetHostId(), storageId,
jsonutils.NewDict(jsonutils.JSONPair{"details", jsonutils.JSONTrue}))
jsonutils.NewDict(jsonutils.NewPair("details", jsonutils.JSONTrue)))
if err != nil {
log.Errorln(err)
return nil, err
+10
View File
@@ -9,3 +9,13 @@ type SDiskCreateByDiskinfo struct {
Storage IStorage
}
type SDiskReset struct {
SnapshotId string
OutOfChain bool
}
type SDiskCleanupSnapshots struct {
ConvertSnapshots []jsonutils.JSONObject
DeleteSnapshots []jsonutils.JSONObject
}
+5 -5
View File
@@ -54,10 +54,10 @@ func (s *SLocalStorage) GetSnapshotDir() string {
return path.Join(s.Path, _SNAPSHOT_PATH_)
}
func (s *SLocalStorage) SyncStorageInfo() {
func (s *SLocalStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
content := jsonutils.NewDict()
content.Set("name", jsonutils.NewString(s.StorageName))
content.Set("capacity", jsonutils.NewInt(s.GetAvailSizeMb()))
content.Set("capacity", jsonutils.NewInt(int64(s.GetAvailSizeMb())))
content.Set("storage_type", jsonutils.NewString(s.StorageType()))
content.Set("medium_type", jsonutils.NewString(s.GetMediumType()))
content.Set("zone", jsonutils.NewString(s.GetZone()))
@@ -172,7 +172,7 @@ func (s *SLocalStorage) SaveToGlance(ctx context.Context, params interface{}) (j
if err := s.saveToGlance(ctx, imageId, imagePath, compress, format); err != nil {
log.Errorf("Save to glance failed: %s", err)
s.onSaveToGlanceFailed(ctx, imageId, imagePath, compress, format)
s.onSaveToGlanceFailed(ctx, imageId)
}
imagecacheManager := s.Manager.LocalStorageImagecacheManager
@@ -209,9 +209,9 @@ func (s *SLocalStorage) saveToGlance(ctx context.Context, imageId, imagePath str
defer kvmDisk.Umount(root)
osInfo = root.GetOs()
relInfo = root.GetReleaseInfo()
relInfo = root.GetReleaseInfo(root.GetPartition())
if compress {
if err := root.PrepareFsForTemplate(); err != nil {
if err := root.PrepareFsForTemplate(root.GetPartition()); err != nil {
log.Errorln(err)
return err
}
+67
View File
@@ -0,0 +1,67 @@
package bwutils
import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/netutils"
)
func GetBwValue(nicDesc jsonutils.JSONObject) int {
bw, err := nicDesc.Int("bw")
if err != nil {
ip, err := nicDesc.GetString("ip")
if err != nil {
bw = 1
} else {
ipv4, err := netutils.NewIPV4Addr(ip)
if err == nil && netutils.IsExitAddress(ipv4) {
bw = 1
} else {
bw = 10000
}
}
}
return int(bw)
}
func GetDownloadBwValue(nicDesc jsonutils.JSONObject, bwDownloadBandwidth int) (int, error) {
ip, _ := nicDesc.GetString("ip")
ifname, _ := nicDesc.GetString("ifname")
if len(ip) > 0 {
ipv4, err := netutils.NewIPV4Addr(ip)
if err != nil {
return 0, err
}
if netutils.IsExitAddress(ipv4) && len(ifname) > 0 && bwDownloadBandwidth > 0 {
bw := GetBwValue(nicDesc)
if bw > bwDownloadBandwidth {
return bw, nil
} else {
return bwDownloadBandwidth, nil
}
}
}
return 0, nil
}
func GetOvsBwValues(nicDesc jsonutils.JSONObject) (int, int, error) {
var bwOvs int
bw := GetBwValue(nicDesc)
ip, err := nicDesc.GetString("ip")
if err == nil {
ipv4, err := netutils.NewIPV4Addr(ip)
if err != nil {
return 0, 0, err
}
if netutils.IsExitAddress(ipv4) {
bwOvs = 1000
if bwOvs > bw*15 {
bwOvs = bw * 15
}
} else {
bwOvs = bw
}
} else {
bwOvs = bw
}
return bwOvs * 1000, bwOvs * 2000, nil
}
+1
View File
@@ -0,0 +1 @@
package cgrouputils
+77
View File
@@ -1,6 +1,7 @@
package netutils2
import (
"bytes"
"fmt"
"net"
"os/exec"
@@ -8,10 +9,12 @@ import (
"regexp"
"strconv"
"strings"
"unicode"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/regutils2"
@@ -303,3 +306,77 @@ func (n *SNetInterface) GetSlaveAddresses() [][]string {
}
return slaves
}
func FormatMac(macStr string) string {
var ret = []byte{}
for i := 0; i < len(macStr); i++ {
if bytes.IndexByte([]byte("0123456789abcdef"), macStr[i]) >= 0 {
ret = append(ret, macStr[i])
} else if bytes.IndexByte([]byte("ABCDEF"), macStr[i]) >= 0 {
ret = append(ret, byte(unicode.ToLower(rune(macStr[i]))))
}
}
if len(ret) == 12 {
var res string
for i := 0; i < 12; i += 2 {
res += string(ret[i:i+2]) + ":"
}
return res[:len(res)-1]
}
return ""
}
func MacEqual(mac1, mac2 string) bool {
mac1 = FormatMac(mac1)
mac2 = FormatMac(mac2)
if len(mac1) > 0 && len(mac2) > 0 && mac1 == mac2 {
return true
}
return false
}
func netmask2len(mask string) int {
masks := []string{"0", "128", "192", "224", "240", "248", "252", "254", "255"}
for i := 0; i < len(masks); i++ {
if masks[i] == mask {
return i
}
}
return -1
}
func Netmask2Len(mask string) int {
data := strings.Split(mask, ".")
mlen := 0
for _, d := range data {
if d != "0" {
nle := netmask2len(d)
if nle < 0 {
return -1
}
mlen += nle
}
}
return mlen
}
func PrefixSplit(pref string) (string, int, error) {
slash := strings.Index(pref, "/")
var intMask int
var err error
if slash > 0 {
ip := pref[:slash]
mask := pref[slash+1:]
if regutils.MatchIPAddr(ip) {
intMask = Netmask2Len(mask)
} else {
intMask, err = strconv.Atoi(mask)
if err != nil {
return "", 0, err
}
}
return ip, intMask, nil
} else {
return pref, 32, nil
}
}
+34
View File
@@ -124,3 +124,37 @@ func TestNetBytes2Mask(t *testing.T) {
})
}
}
func TestFormatMac(t *testing.T) {
type args struct {
macStr string
}
tests := []struct {
name string
args args
want string
}{
{
name: "test-format-mac-1",
args: args{"FFFFFFFFFFFF"},
want: "ff:ff:ff:ff:ff:ff",
},
{
name: "test-format-mac-2",
args: args{"FFFFFFFFFF"},
want: "",
},
{
name: "test-format-mac-3",
args: args{"FFDDEECCBBAA"},
want: "ff:dd:ee:cc:bb:aa",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := FormatMac(tt.args.macStr); got != tt.want {
t.Errorf("FormatMac() = %v, want %v", got, tt.want)
}
})
}
}
+14
View File
@@ -466,3 +466,17 @@ func (img *SQemuImage) Fallocate() error {
func (img *SQemuImage) String() string {
return fmt.Sprintf("Qemu %s %d(%d) %s", img.Format, img.GetSizeMB(), img.GetActualSizeMB(), img.Path)
}
func (img *SQemuImage) WholeChainFormatIs(format string) (bool, error) {
if img.Format.String() != format {
return false, nil
}
if len(img.BackFilePath) > 0 {
backImg, err := NewQemuImage(img.BackFilePath)
if err != nil {
return false, err
}
return backImg.WholeChainFormatIs(format)
}
return true, nil
}
+4
View File
@@ -14,6 +14,10 @@ type JSONPair struct {
val JSONObject
}
func NewPair(key string, val JSONObject) JSONPair {
return JSONPair{key, val}
}
func NewDict(objs ...JSONPair) *JSONDict {
dict := JSONDict{data: make(map[string]JSONObject)}
for _, o := range objs {