Merge pull request #906 in YUNIONIO/onecloud from ~LIZEXI/onecloud:feature/lzx-server-import to release/2.5.0

* commit '5c0b04973ad5865a0b778ed5cbc5bc9595c4b0d5':
  region: support server import
This commit is contained in:
李泽玺
2019-01-08 22:11:30 +08:00
5 changed files with 265 additions and 4 deletions
+60 -2
View File
@@ -2,14 +2,17 @@ package shell
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/pkg/util/regutils"
)
func init() {
@@ -660,4 +663,59 @@ func init() {
return nil
})
type ServerImportOptions struct {
LOCATION string `help:"Server desc file location, should be desc file or workspace directory"`
HOST string `help:"Host id or name for this server"`
}
R(&ServerImportOptions{}, "server-import", "Import a server by desc file", func(s *mcclient.ClientSession, args *ServerImportOptions) error {
var descFiles []string
err := filepath.Walk(args.LOCATION, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if info.Name() == "desc" {
descFiles = append(descFiles, path)
}
return nil
})
if err != nil {
return fmt.Errorf("Find desc files: %v", err)
}
importF := func(desc string) error {
ret, err := ioutil.ReadFile(desc)
if err != nil {
return fmt.Errorf("Read file %s: %v", desc, err)
}
jsonObj, err := jsonutils.Parse(ret)
if err != nil {
return fmt.Errorf("Parse %s to json: %v", string(ret), err)
}
params := jsonObj.(*jsonutils.JSONDict)
disks, err := params.GetArray("disks")
if err != nil || len(disks) == 0 {
return fmt.Errorf("Desc %s not have disks, skip it", desc)
}
params.Add(jsonutils.NewString(args.HOST), "host_id")
// project may not exists
params.Remove("tenant")
params.Remove("tenant_id")
_, err = modules.Servers.PerformClassAction(s, "import", params)
if err != nil {
return err
}
//printObject(result)
return nil
}
for _, descFile := range descFiles {
if err := importF(descFile); err != nil {
log.Errorf("Import %s error: %v", descFile, err)
}
}
return nil
})
}
+5
View File
@@ -1105,6 +1105,8 @@ type SDiskConfig struct {
Backend string // stroageType
Medium string
ImageProperties map[string]string
DiskId string // import only
}
func parseDiskInfo(ctx context.Context, userCred mcclient.TokenCredential, info jsonutils.JSONObject) (*SDiskConfig, error) {
@@ -1263,6 +1265,9 @@ func (self *SDisk) fetchDiskInfo(diskConfig *SDiskConfig) {
}
self.Nonpersistent = false
}
if len(diskConfig.DiskId) > 0 && utils.IsMatchUUID(diskConfig.DiskId) {
self.Id = diskConfig.DiskId
}
self.DiskFormat = diskConfig.Format
self.DiskSize = diskConfig.SizeMb
}
+184
View File
@@ -13,6 +13,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/billing"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/fileutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/utils"
@@ -2326,3 +2327,186 @@ func (self *SGuest) PerformStreamDisksComplete(ctx context.Context, userCred mcc
}
return nil, nil
}
func (man *SGuestManager) AllowPerformImport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowClassPerform(userCred, man, "import")
}
type SImportNic struct {
Index int `json:"index"`
Bridge string `json:"bridge"`
Domain string `json:"domain"`
Ip string `json:"ip"`
Vlan int `json:"vlan"`
Driver string `json:"driver"`
Masklen int `json:"masklen"`
Virtual bool `json:"virtual"`
Manual bool `json:"manual"`
WireId string `json:"wire_id"`
NetId string `json:"net_id"`
Mac string `json:"mac"`
BandWidth int `json:"bw"`
Dns string `json:"dns"`
Net string `json:"net"`
Interface string `json:"interface"`
Gateway string `json:"gateway"`
Ifname string `json:"ifname"`
}
func (n SImportNic) ToNetConfig(net *SNetwork) *SNetworkConfig {
return &SNetworkConfig{
Network: net.Id,
Wire: net.WireId,
Address: n.Ip,
Mac: n.Mac,
Driver: n.Driver,
BwLimit: n.BandWidth,
}
}
type SImportDisk struct {
Index int `json:"index"`
DiskId string `json:"disk_id"`
Driver string `json:"driver"`
CacheMode string `json:"cache_mode"`
AioMode string `json:"aio_mode"`
SizeMb int `json:"size"`
Format string `json:"format"`
Fs string `json:"fs"`
Mountpoint string `json:"mountpoint"`
Dev string `json:"dev"`
TemplateId string `json:"template_id"`
}
func (d SImportDisk) ToDiskConfig() *SDiskConfig {
ret := &SDiskConfig{
SizeMb: d.SizeMb,
ImageId: d.TemplateId,
Format: d.Format,
Driver: d.Driver,
DiskId: d.DiskId,
Cache: d.CacheMode,
}
if len(d.Mountpoint) > 0 {
ret.Mountpoint = d.Mountpoint
}
if len(d.Fs) > 0 {
ret.Fs = d.Fs
}
return ret
}
type SImportGuestDesc struct {
Id string `json:"uuid"`
Name string `json:"name"`
Nics []SImportNic `json:"nics"`
Disks []SImportDisk `json:"disks"`
Metadata map[string]string `json:"metadata"`
MemSizeMb int `json:"mem"`
Cpu int `json:"cpu"`
TemplateId string `json:"template_id"`
ImagePath string `json:"image_path"`
Vdi string `json:"vdi"`
Hypervisor string `json:"hypervisor"`
HostId string `json:"host"`
BootOrder string `json:"boot_order"`
IsSystem bool `json:"is_system"`
Description string `json:"description"`
}
func (man *SGuestManager) PerformImport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
desc := SImportGuestDesc{}
if err := data.Unmarshal(&desc); err != nil {
return nil, httperrors.NewInputParameterError("Invalid desc: %s", data.String())
}
if len(desc.Id) == 0 {
return nil, httperrors.NewInputParameterError("Server Id is empty")
}
if len(desc.Name) == 0 {
return nil, httperrors.NewInputParameterError("Server Name is empty")
}
if obj, _ := man.FetchByIdOrName(userCred, desc.Id); obj != nil {
return nil, httperrors.NewInputParameterError("Server %s already exists", desc.Id)
}
if err := db.NewNameValidator(man, userCred.GetProjectId(), desc.Name); err != nil {
return nil, err
}
if hostObj, _ := HostManager.FetchByIdOrName(userCred, desc.HostId); hostObj == nil {
return nil, httperrors.NewNotFoundError("Host %s not found", desc.HostId)
} else {
desc.HostId = hostObj.GetId()
}
// 1. create import guest on host
gst, err := man.createImportGuest(ctx, userCred, desc)
if err != nil {
return nil, err
}
// 2. import networks
if err := gst.importNics(ctx, userCred, desc.Nics); err != nil {
return nil, err
}
// 3. import disks
if err := gst.importDisks(ctx, userCred, desc.Disks); err != nil {
return nil, err
}
// 4. set metadata
for k, v := range desc.Metadata {
gst.SetMetadata(ctx, k, v, userCred)
}
return jsonutils.Marshal(gst), nil
}
func (man *SGuestManager) createImportGuest(ctx context.Context, userCred mcclient.TokenCredential, desc SImportGuestDesc) (*SGuest, error) {
model, err := db.NewModelObject(man)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
gst, ok := model.(*SGuest)
if !ok {
return nil, httperrors.NewGeneralError(fmt.Errorf("Can't convert %#v to *SGuest model", model))
}
gst.ProjectId = userCred.GetProjectId()
gst.IsSystem = desc.IsSystem
gst.Id = desc.Id
gst.Name = desc.Name
gst.HostId = desc.HostId
gst.Status = "import"
gst.Hypervisor = desc.Hypervisor
gst.VmemSize = desc.MemSizeMb
gst.VcpuCount = int8(desc.Cpu)
gst.BootOrder = desc.BootOrder
gst.Description = desc.Description
err = man.TableSpec().Insert(gst)
return gst, err
}
func (self *SGuest) importNics(ctx context.Context, userCred mcclient.TokenCredential, nics []SImportNic) error {
if len(nics) == 0 {
return httperrors.NewInputParameterError("Empty import nics")
}
for _, nic := range nics {
net, err := NetworkManager.GetNetworkOfIP(nic.Ip, "", tristate.None)
if err != nil {
return httperrors.NewNotFoundError("Not found network by ip %s", nic.Ip)
}
err = self.attach2NetworkDesc(ctx, userCred, self.GetHost(), nic.ToNetConfig(net), nil)
if err != nil {
return err
}
}
return nil
}
func (self *SGuest) importDisks(ctx context.Context, userCred mcclient.TokenCredential, disks []SImportDisk) error {
if len(disks) == 0 {
return httperrors.NewInputParameterError("Empty import disks")
}
for _, disk := range disks {
disk, err := self.createDiskOnHost(ctx, userCred, self.GetHost(), disk.ToDiskConfig(), nil, true)
if err != nil {
return err
}
disk.SetStatus(userCred, DISK_READY, "")
}
return nil
}
+14
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -95,6 +96,19 @@ func (manager *SIsolatedDeviceManager) AllowCreateItem(ctx context.Context, user
return db.IsAdminAllowCreate(userCred, manager)
}
func (manager *SIsolatedDeviceManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
hostId, _ := data.GetString("host_id")
host := HostManager.FetchHostById(hostId)
if host == nil {
return nil, httperrors.NewNotFoundError("Host %s not found", hostId)
}
if name, _ := data.GetString("name"); len(name) == 0 {
name = fmt.Sprintf("dev_%s_%d", host.GetName(), time.Now().UnixNano())
data.Set("name", jsonutils.NewString(name))
}
return manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data)
}
func (self *SIsolatedDevice) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
return db.IsAdminAllowUpdate(userCred, self)
}
@@ -145,10 +145,10 @@ func (p *NetworkPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []cor
errMsgs := make([]string, 0)
for _, net := range hc.Networks {
if !isMatchServerType(net) {
/*if !isMatchServerType(net) {
errMsgs = append(errMsgs, fmt.Sprintf("%v(%v): server type not matched", net.Name, net.ID))
continue
}
}*/
if !(n.Idx == net.ID || n.Idx == net.Name) {
errMsgs = append(errMsgs, fmt.Sprintf("%v(%v): id/name not matched", net.Name, net.ID))
} else if !(net.IsPublic || net.TenantID == d.OwnerTenantID) {