diff --git a/pkg/hostman/guestfs/localfs.go b/pkg/hostman/guestfs/localfs.go index 111fa8b0e1..7e8542f184 100644 --- a/pkg/hostman/guestfs/localfs.go +++ b/pkg/hostman/guestfs/localfs.go @@ -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 { diff --git a/pkg/hostman/guestman/guesthandler.go b/pkg/hostman/guestman/guesthandler.go index 71454ef374..498b08968a 100644 --- a/pkg/hostman/guestman/guesthandler.go +++ b/pkg/hostman/guestman/guesthandler.go @@ -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, } diff --git a/pkg/hostman/guestman/guesthelper.go b/pkg/hostman/guestman/guesthelper.go index 3d76678f7b..18802d5de7 100644 --- a/pkg/hostman/guestman/guesthelper.go +++ b/pkg/hostman/guestman/guesthelper.go @@ -11,7 +11,8 @@ type SBaseParms struct { } type SGuestDeploy struct { - SGuestBaseParms + Sid string + Body jsonutils.JSONObject IsInit bool } diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index 62e5decfba..db1617377a 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -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) } } diff --git a/pkg/hostman/guestman/guesttasks.go b/pkg/hostman/guestman/guesttasks.go index 32a69e54d9..d788357a0a 100644 --- a/pkg/hostman/guestman/guesttasks.go +++ b/pkg/hostman/guestman/guesttasks.go @@ -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) + } } } diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index d9da4c14d0..c3c6f9c92b 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -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< 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 } diff --git a/pkg/hostman/guestman/qemu-kvmhelper.go b/pkg/hostman/guestman/qemu-kvmhelper.go index 29b5c2af37..b3618c890c 100644 --- a/pkg/hostman/guestman/qemu-kvmhelper.go +++ b/pkg/hostman/guestman/qemu-kvmhelper.go @@ -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 +} diff --git a/pkg/hostman/hostinfo/hostbridge/hostbridge.go b/pkg/hostman/hostinfo/hostbridge/hostbridge.go index 1c1e271757..fa70f7a1c8 100644 --- a/pkg/hostman/hostinfo/hostbridge/hostbridge.go +++ b/pkg/hostman/hostinfo/hostbridge/hostbridge.go @@ -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() } diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index 63162421fd..1d8aa4e67f 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -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 diff --git a/pkg/hostman/hostinfo/hostpinger.go b/pkg/hostman/hostinfo/hostpinger.go new file mode 100644 index 0000000000..1d2b59b013 --- /dev/null +++ b/pkg/hostman/hostinfo/hostpinger.go @@ -0,0 +1,13 @@ +package hostinfo + +type SHostPingTask struct { + interval int +} + +func NewHostPingTask(interval int) *SHostPingTask { + return &SHostPingTask{interval} +} + +func (p *SHostPingTask) Start() { + //TODO +} diff --git a/pkg/hostman/hostutils/hostutils.go b/pkg/hostman/hostutils/hostutils.go index e0d8d3a4ea..7c650a39ff 100644 --- a/pkg/hostman/hostutils/hostutils.go +++ b/pkg/hostman/hostutils/hostutils.go @@ -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") } diff --git a/pkg/hostman/monitor/hmp.go b/pkg/hostman/monitor/hmp.go index be04ae2f28..f9dd04fc8b 100644 --- a/pkg/hostman/monitor/hmp.go +++ b/pkg/hostman/monitor/hmp.go @@ -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) +} diff --git a/pkg/hostman/monitor/monitor.go b/pkg/hostman/monitor/monitor.go index 7dc9851105..e2ece46b99 100644 --- a/pkg/hostman/monitor/monitor.go +++ b/pkg/hostman/monitor/monitor.go @@ -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) diff --git a/pkg/hostman/monitor/qmp.go b/pkg/hostman/monitor/qmp.go index c78805cdfd..cf73ac55c6 100644 --- a/pkg/hostman/monitor/qmp.go +++ b/pkg/hostman/monitor/qmp.go @@ -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) +} diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index 8f6bb9da95..824830f64f 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -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 diff --git a/pkg/hostman/service/host_services.go b/pkg/hostman/service/host_services.go index f87b16dc42..b5f8b9f28f 100644 --- a/pkg/hostman/service/host_services.go +++ b/pkg/hostman/service/host_services.go @@ -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) { diff --git a/pkg/hostman/storageman/core.go b/pkg/hostman/storageman/core.go index 186628b3b4..fd7051ebe3 100644 --- a/pkg/hostman/storageman/core.go +++ b/pkg/hostman/storageman/core.go @@ -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 } diff --git a/pkg/hostman/storageman/diskbase.go b/pkg/hostman/storageman/diskbase.go index 9425593fb1..3885549e8d 100644 --- a/pkg/hostman/storageman/diskbase.go +++ b/pkg/hostman/storageman/diskbase.go @@ -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 { diff --git a/pkg/hostman/storageman/diskhandler.go b/pkg/hostman/storageman/diskhandler.go index 093de8ce70..0f12788df6 100644 --- a/pkg/hostman/storageman/diskhandler.go +++ b/pkg/hostman/storageman/diskhandler.go @@ -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 +} diff --git a/pkg/hostman/storageman/disklocal.go b/pkg/hostman/storageman/disklocal.go index 77b89ac87d..21021cc682 100644 --- a/pkg/hostman/storageman/disklocal.go +++ b/pkg/hostman/storageman/disklocal.go @@ -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 +} diff --git a/pkg/hostman/storageman/diskutils.go b/pkg/hostman/storageman/diskutils.go index 4a688e61b6..a982477c87 100644 --- a/pkg/hostman/storageman/diskutils.go +++ b/pkg/hostman/storageman/diskutils.go @@ -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())) diff --git a/pkg/hostman/storageman/imagecachemanager.go b/pkg/hostman/storageman/imagecachemanager.go index 0e7760c592..7011e6087b 100644 --- a/pkg/hostman/storageman/imagecachemanager.go +++ b/pkg/hostman/storageman/imagecachemanager.go @@ -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 } diff --git a/pkg/hostman/storageman/storagebase.go b/pkg/hostman/storageman/storagebase.go index 8e59f218c0..15f1c1ca95 100644 --- a/pkg/hostman/storageman/storagebase.go +++ b/pkg/hostman/storageman/storagebase.go @@ -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_) +} diff --git a/pkg/hostman/storageman/storagehandler.go b/pkg/hostman/storageman/storagehandler.go index a50827e609..8b0724dc0a 100644 --- a/pkg/hostman/storageman/storagehandler.go +++ b/pkg/hostman/storageman/storagehandler.go @@ -32,9 +32,9 @@ func AddStorageHandler(prefix string, app *appsrv.Application) { auth.Authenticate(storageActions)) // TODO - app.AddHandler("POST", - fmt.Sprintf("%s/%s//delete-snapshots", prefix, keyWords), - auth.Authenticate(storageDeleteSnapshots)) + // app.AddHandler("POST", + // fmt.Sprintf("%s/%s//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[""] - 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 diff --git a/pkg/hostman/storageman/storagehelper.go b/pkg/hostman/storageman/storagehelper.go index 5801069f4b..1bef35d379 100644 --- a/pkg/hostman/storageman/storagehelper.go +++ b/pkg/hostman/storageman/storagehelper.go @@ -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 +} diff --git a/pkg/hostman/storageman/storagelocal.go b/pkg/hostman/storageman/storagelocal.go index 34ee4205f8..528bfc738d 100644 --- a/pkg/hostman/storageman/storagelocal.go +++ b/pkg/hostman/storageman/storagelocal.go @@ -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 } diff --git a/pkg/util/bwutils/bwutils.go b/pkg/util/bwutils/bwutils.go new file mode 100644 index 0000000000..007587f474 --- /dev/null +++ b/pkg/util/bwutils/bwutils.go @@ -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 +} diff --git a/pkg/util/cgrouputils/cpuutils.go b/pkg/util/cgrouputils/cpuutils.go index e69de29bb2..00fa7f7529 100644 --- a/pkg/util/cgrouputils/cpuutils.go +++ b/pkg/util/cgrouputils/cpuutils.go @@ -0,0 +1 @@ +package cgrouputils diff --git a/pkg/util/netutils2/netutils.go b/pkg/util/netutils2/netutils.go index 9045f93aa8..d5bd6a0ddf 100644 --- a/pkg/util/netutils2/netutils.go +++ b/pkg/util/netutils2/netutils.go @@ -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 + } +} diff --git a/pkg/util/netutils2/netutils_test.go b/pkg/util/netutils2/netutils_test.go index c51530c97d..3899b08b60 100644 --- a/pkg/util/netutils2/netutils_test.go +++ b/pkg/util/netutils2/netutils_test.go @@ -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) + } + }) + } +} diff --git a/pkg/util/qemuimg/qemuimg.go b/pkg/util/qemuimg/qemuimg.go index 57de61f0f6..ccfdb30c0a 100644 --- a/pkg/util/qemuimg/qemuimg.go +++ b/pkg/util/qemuimg/qemuimg.go @@ -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 +} diff --git a/vendor/yunion.io/x/jsonutils/access.go b/vendor/yunion.io/x/jsonutils/access.go index 86b24ff039..c1f26bf13e 100644 --- a/vendor/yunion.io/x/jsonutils/access.go +++ b/vendor/yunion.io/x/jsonutils/access.go @@ -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 {