mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-21 14:19:49 +08:00
Merge branch 'release/2.6.0' of ssh://git.yunion.io/~qiujian/onecloud into hotfix/qj-recode-cloudprovider
This commit is contained in:
+72
-24
@@ -1,21 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"github.com/anacrolix/torrent/storage"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/version"
|
||||
"yunion.io/x/structarg"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/nodeid"
|
||||
"yunion.io/x/onecloud/pkg/util/torrentutils"
|
||||
)
|
||||
@@ -28,7 +32,10 @@ type Options struct {
|
||||
|
||||
Tracker []string `help:"Tracker urls, e.g. http://10.168.222.252:6969/announce or udp://tracker.istole.it:6969"`
|
||||
|
||||
Debug bool `help:"turn on debug"`
|
||||
Debug bool `help:"turn on debug" default:"false"`
|
||||
Verbose bool `help:"verbose mode" default:"false"`
|
||||
|
||||
CallbackURL string `help:"callback notification URL"`
|
||||
}
|
||||
|
||||
func exitSignalHandlers(client *torrent.Client) {
|
||||
@@ -71,13 +78,15 @@ func main() {
|
||||
}
|
||||
|
||||
var mi *metainfo.MetaInfo
|
||||
var rootDir string
|
||||
|
||||
if len(options.Tracker) > 0 {
|
||||
if len(options.Tracker) > 0 && !fileutils2.Exists(options.TORRENT) {
|
||||
// server mode
|
||||
mi, err = torrentutils.GenerateTorrent(root, options.Tracker, options.TORRENT)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to save torrent file %s", err)
|
||||
}
|
||||
rootDir = filepath.Dir(root)
|
||||
|
||||
} else {
|
||||
// client mode, load mi from torrent file
|
||||
@@ -85,28 +94,49 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("fail to open torrent file %s", err)
|
||||
}
|
||||
rootDir = root
|
||||
}
|
||||
|
||||
info, err := mi.UnmarshalInfo()
|
||||
if err != nil {
|
||||
log.Errorf("fail to unmarshalinfo %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
hasher := sha1.New()
|
||||
|
||||
nodeId, err := nodeid.GetNodeId()
|
||||
if err != nil {
|
||||
log.Errorf("fail to generate node id: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Set torrent server as node %s", nodeId)
|
||||
hasher.Write(nodeId)
|
||||
hasher.Write(info.Pieces)
|
||||
|
||||
peerIdStr := fmt.Sprintf("%x", hasher.Sum(nil))
|
||||
|
||||
log.Infof("Set torrent server as node %s", peerIdStr[:20])
|
||||
|
||||
clientConfig := torrent.NewDefaultClientConfig()
|
||||
clientConfig.PeerID = nodeId[:20]
|
||||
clientConfig.PeerID = peerIdStr[:20]
|
||||
clientConfig.Debug = options.Debug
|
||||
clientConfig.Seed = true
|
||||
clientConfig.NoUpload = false
|
||||
if len(options.Tracker) > 0 {
|
||||
// server mode
|
||||
clientConfig.DataDir = path.Dir(root)
|
||||
} else {
|
||||
// client mode
|
||||
clientConfig.DataDir = root
|
||||
}
|
||||
|
||||
log.Infof("To sync torrent files for %s", info.Name)
|
||||
tmpDir := filepath.Join(rootDir, fmt.Sprintf("%s%s", info.Name, ".tmp"))
|
||||
|
||||
os.RemoveAll(tmpDir)
|
||||
os.MkdirAll(tmpDir, 0700)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
clientConfig.DefaultStorage = storage.NewFileWithCustomPathMaker(tmpDir,
|
||||
func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
|
||||
return filepath.Dir(baseDir)
|
||||
},
|
||||
)
|
||||
|
||||
clientConfig.DisableTrackers = false
|
||||
clientConfig.DisablePEX = true
|
||||
clientConfig.NoDHT = true
|
||||
@@ -122,32 +152,50 @@ func main() {
|
||||
|
||||
go exitSignalHandlers(client)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
t, err := client.AddTorrent(mi)
|
||||
if err != nil {
|
||||
log.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-t.GotInfo()
|
||||
<-t.GotInfo()
|
||||
t.DownloadAll()
|
||||
|
||||
files := t.Info().Files
|
||||
log.Debugf("Got Info, start download %d files", len(files))
|
||||
for i := 0; i < len(files); i += 1 {
|
||||
log.Debugf("%d: %s", i, files[i].Path)
|
||||
}
|
||||
|
||||
t.DownloadAll()
|
||||
}()
|
||||
stop := false
|
||||
|
||||
go func() {
|
||||
<-client.Closed()
|
||||
log.Debugf("client closed, exit!")
|
||||
|
||||
os.Exit(0)
|
||||
stop = true
|
||||
}()
|
||||
|
||||
for {
|
||||
finish := false
|
||||
|
||||
for !stop {
|
||||
if t.BytesCompleted() == t.Info().TotalLength() {
|
||||
if !finish {
|
||||
finish = true
|
||||
fmt.Printf("Download complete, takes %d seconds\n", time.Now().Sub(start)/time.Second)
|
||||
if len(options.CallbackURL) > 0 {
|
||||
maxTried := 10
|
||||
for tried := 0; tried < maxTried; tried += 1 {
|
||||
resp, err := http.Post(options.CallbackURL, "", nil)
|
||||
if err == nil && resp.StatusCode < 300 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("callback fail %s", err)
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
respBody, _ := ioutil.ReadAll(resp.Body)
|
||||
log.Errorf("callback response error %s", string(respBody))
|
||||
}
|
||||
time.Sleep(time.Duration(tried+1) * 10 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("\rSeeding.............")
|
||||
} else {
|
||||
fmt.Printf("\rDownload: %.1f%%", float64(t.BytesCompleted())*100.0/float64(t.Info().TotalLength()))
|
||||
|
||||
+64
-20
@@ -5,13 +5,16 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/trace"
|
||||
"yunion.io/x/pkg/util/signalutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appctx"
|
||||
@@ -37,8 +40,8 @@ type Application struct {
|
||||
cors *Cors
|
||||
middlewares []MiddlewareFunc
|
||||
|
||||
// record Http server for handle shotdown
|
||||
server *http.Server
|
||||
isExiting bool
|
||||
idleConnsClosed chan struct{}
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -329,32 +332,73 @@ func (app *Application) initServer(addr string) *http.Server {
|
||||
return s
|
||||
}
|
||||
|
||||
func (app *Application) registerCleanShutdown(s *http.Server, onStop func()) {
|
||||
app.idleConnsClosed = make(chan struct{})
|
||||
|
||||
// dump goroutine stack
|
||||
signalutils.RegisterSignal(func() {
|
||||
utils.DumpAllGoroutineStack(log.Logger().Out)
|
||||
}, syscall.SIGUSR1)
|
||||
|
||||
quitSignals := []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM}
|
||||
signalutils.RegisterSignal(func() {
|
||||
if app.isExiting {
|
||||
log.Infof("Quit signal received!!! clean up in progress, be patient...")
|
||||
return
|
||||
}
|
||||
app.isExiting = true
|
||||
log.Infof("Quit signal received!!! do cleanup...")
|
||||
|
||||
if err := s.Shutdown(context.Background()); err != nil {
|
||||
// Error from closing listeners, or context timeout:
|
||||
log.Errorf("HTTP server Shutdown: %v", err)
|
||||
}
|
||||
if onStop != nil {
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Errorf("app exiting error: %s", r)
|
||||
}
|
||||
}()
|
||||
onStop()
|
||||
}()
|
||||
}
|
||||
close(app.idleConnsClosed)
|
||||
}, quitSignals...)
|
||||
|
||||
signalutils.StartTrap()
|
||||
}
|
||||
|
||||
func (app *Application) waitCleanShutdown() {
|
||||
<-app.idleConnsClosed
|
||||
log.Infof("Service stopped.")
|
||||
}
|
||||
|
||||
func (app *Application) ListenAndServe(addr string) {
|
||||
app.server = app.initServer(addr)
|
||||
err := app.server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Errorf("ListAndServer: %s", err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Application) IsInServe() bool {
|
||||
return app.server != nil
|
||||
}
|
||||
|
||||
func (app *Application) ShutDown(ctx context.Context) error {
|
||||
if app.server != nil {
|
||||
return app.server.Shutdown(ctx)
|
||||
}
|
||||
return fmt.Errorf("Not init http server ??")
|
||||
app.ListenAndServeWithCleanup(addr, nil)
|
||||
}
|
||||
|
||||
func (app *Application) ListenAndServeTLS(addr string, certFile, keyFile string) {
|
||||
app.ListenAndServeTLSWithCleanup(addr, certFile, keyFile, nil)
|
||||
}
|
||||
|
||||
func (app *Application) ListenAndServeWithCleanup(addr string, onStop func()) {
|
||||
app.ListenAndServeTLSWithCleanup(addr, "", "", onStop)
|
||||
}
|
||||
|
||||
func (app *Application) ListenAndServeTLSWithCleanup(addr string, certFile, keyFile string, onStop func()) {
|
||||
s := app.initServer(addr)
|
||||
err := s.ListenAndServeTLS(certFile, keyFile)
|
||||
app.registerCleanShutdown(s, onStop)
|
||||
var err error
|
||||
if len(certFile) == 0 && len(keyFile) == 0 {
|
||||
err = s.ListenAndServe()
|
||||
} else {
|
||||
err = s.ListenAndServeTLS(certFile, keyFile)
|
||||
}
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("ListAndServer fail: %s", err)
|
||||
}
|
||||
app.waitCleanShutdown()
|
||||
}
|
||||
|
||||
func isJsonContentType(r *http.Request) bool {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"yunion.io/x/log"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
|
||||
type BaremetalService struct {
|
||||
service.SServiceBase
|
||||
isExiting bool
|
||||
}
|
||||
|
||||
func New() *BaremetalService {
|
||||
@@ -30,25 +28,9 @@ func (s *BaremetalService) StartService() {
|
||||
app := cloudcommon.InitApp(&o.Options.CommonOptions, false)
|
||||
handler.InitHandlers(app)
|
||||
|
||||
s.RegisterSIGUSR1()
|
||||
s.RegisterQuitSignals(func() {
|
||||
log.Infof("Baremetal agent quit !!!")
|
||||
if s.isExiting {
|
||||
return
|
||||
} else {
|
||||
s.isExiting = true
|
||||
}
|
||||
|
||||
if app.IsInServe() {
|
||||
if err := app.ShutDown(context.Background()); err != nil {
|
||||
log.Errorf("App shutdown err: %v", err)
|
||||
}
|
||||
}
|
||||
cloudcommon.ServeForeverWithCleanup(app, &o.Options.CommonOptions, func() {
|
||||
tasks.OnStop()
|
||||
os.Exit(0)
|
||||
})
|
||||
|
||||
cloudcommon.ServeForever(app, &o.Options.CommonOptions)
|
||||
}
|
||||
|
||||
func (s *BaremetalService) startAgent() {
|
||||
|
||||
@@ -24,6 +24,10 @@ func InitApp(options *CommonOptions, dbAccess bool) *appsrv.Application {
|
||||
}
|
||||
|
||||
func ServeForever(app *appsrv.Application, options *CommonOptions) {
|
||||
ServeForeverWithCleanup(app, options, nil)
|
||||
}
|
||||
|
||||
func ServeForeverWithCleanup(app *appsrv.Application, options *CommonOptions, onStop func()) {
|
||||
AppDBInit(app)
|
||||
addr := net.JoinHostPort(options.Address, strconv.Itoa(options.Port))
|
||||
proto := "http"
|
||||
@@ -47,8 +51,8 @@ func ServeForever(app *appsrv.Application, options *CommonOptions) {
|
||||
if len(options.SslKeyfile) == 0 {
|
||||
log.Fatalf("Missing ssl-keyfile")
|
||||
}
|
||||
app.ListenAndServeTLS(addr, certfile, options.SslKeyfile)
|
||||
app.ListenAndServeTLSWithCleanup(addr, certfile, options.SslKeyfile, onStop)
|
||||
} else {
|
||||
app.ListenAndServe(addr)
|
||||
app.ListenAndServeWithCleanup(addr, onStop)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,9 @@ func (self *SCronJobManager) Start() {
|
||||
}
|
||||
|
||||
func (self *SCronJobManager) Stop() {
|
||||
close(self.stop)
|
||||
if self.stop != nil {
|
||||
close(self.stop)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SCronJobManager) run() {
|
||||
|
||||
@@ -137,6 +137,13 @@ var (
|
||||
Action: PolicyActionGet,
|
||||
Result: rbacutils.OwnerAllow,
|
||||
},
|
||||
{
|
||||
Service: "image",
|
||||
Resource: "images",
|
||||
Action: PolicyActionPerform,
|
||||
Extra: []string{"update-torrent-status"},
|
||||
Result: rbacutils.GuestAllow,
|
||||
},
|
||||
{
|
||||
Service: "log",
|
||||
Resource: "actions",
|
||||
|
||||
@@ -190,7 +190,7 @@ func queryKey(isAdmin bool, userCred mcclient.TokenCredential, service string, r
|
||||
}
|
||||
|
||||
func (manager *SPolicyManager) Allow(isAdmin bool, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult {
|
||||
if manager.cache != nil {
|
||||
if manager.cache != nil && userCred != nil {
|
||||
key := queryKey(isAdmin, userCred, service, resource, action, extra...)
|
||||
val := manager.cache.Get(key)
|
||||
if val != nil {
|
||||
@@ -231,7 +231,12 @@ func (manager *SPolicyManager) allowWithoutCache(isAdmin bool, userCred mcclient
|
||||
log.Warningf("no policies fetched")
|
||||
return rbacutils.Deny
|
||||
}
|
||||
userCredJson := userCred.ToJson()
|
||||
var userCredJson jsonutils.JSONObject
|
||||
if userCred != nil {
|
||||
userCredJson = userCred.ToJson()
|
||||
} else {
|
||||
userCredJson = jsonutils.NewDict()
|
||||
}
|
||||
currentPriv := rbacutils.Deny
|
||||
for _, p := range policies {
|
||||
result := p.Allow(userCredJson, service, resource, action, extra...)
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/signalutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SServiceBase struct{}
|
||||
|
||||
func (s *SServiceBase) RegisterQuitSignals(quitHandler signalutils.Trap) {
|
||||
quitSignals := []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM}
|
||||
signalutils.RegisterSignal(quitHandler, quitSignals...)
|
||||
|
||||
signalutils.StartTrap()
|
||||
}
|
||||
|
||||
func (s *SServiceBase) RegisterSIGUSR1() {
|
||||
// dump goroutine stack
|
||||
signalutils.RegisterSignal(func() {
|
||||
utils.DumpAllGoroutineStack(log.Logger().Out)
|
||||
}, syscall.SIGUSR1)
|
||||
}
|
||||
|
||||
@@ -23,11 +23,12 @@ func StartService() {
|
||||
log.Fatalf("init etcd fail: %s", err)
|
||||
return
|
||||
}
|
||||
defer etcd.CloseDefaultEtcdClient()
|
||||
|
||||
app := cloudcommon.InitApp(commonOpts, false)
|
||||
|
||||
initHandlers(app)
|
||||
|
||||
cloudcommon.ServeForever(app, commonOpts)
|
||||
cloudcommon.ServeForeverWithCleanup(app, commonOpts, func() {
|
||||
etcd.CloseDefaultEtcdClient()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ type ICloudProviderFactory interface {
|
||||
IsPublicCloud() bool
|
||||
IsOnPremise() bool
|
||||
IsSupportPrepaidResources() bool
|
||||
NeedSyncSkuFromCloud() bool
|
||||
}
|
||||
|
||||
type ICloudProvider interface {
|
||||
|
||||
@@ -115,3 +115,7 @@ func (region *SFakeOnPremiseRegion) CreateILoadBalancer(loadbalancer *SLoadbalan
|
||||
func (region *SFakeOnPremiseRegion) CreateILoadBalancerAcl(acl *SLoadbalancerAccessControlList) (ICloudLoadbalancerAcl, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (region *SFakeOnPremiseRegion) GetSkus(zoneId string) ([]ICloudSku, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
type SDiskInfo struct {
|
||||
StorageType string
|
||||
SizeGB int
|
||||
Name string
|
||||
}
|
||||
|
||||
type SManagedVMCreateConfig struct {
|
||||
|
||||
@@ -74,6 +74,8 @@ type ICloudRegion interface {
|
||||
CreateILoadBalancerAcl(acl *SLoadbalancerAccessControlList) (ICloudLoadbalancerAcl, error)
|
||||
CreateILoadBalancerCertificate(cert *SLoadbalancerCertificate) (ICloudLoadbalancerCertificate, error)
|
||||
|
||||
GetSkus(zoneId string) ([]ICloudSku, error)
|
||||
|
||||
GetProvider() string
|
||||
}
|
||||
|
||||
@@ -211,7 +213,7 @@ type ICloudVM interface {
|
||||
GetInstanceType() string
|
||||
|
||||
AssignSecurityGroup(secgroupId string) error
|
||||
AssignSecurityGroups(secgroupIds []string) error
|
||||
SetSecurityGroups(secgroupIds []string) error
|
||||
|
||||
GetHypervisor() string
|
||||
|
||||
@@ -522,3 +524,38 @@ type ICloudLoadbalancerAcl interface {
|
||||
Sync(acl *SLoadbalancerAccessControlList) error
|
||||
Delete() error
|
||||
}
|
||||
|
||||
type ICloudSku interface {
|
||||
ICloudResource
|
||||
|
||||
GetInstanceTypeFamily() string
|
||||
GetInstanceTypeCategory() string
|
||||
|
||||
GetPrepaidStatus() string
|
||||
GetPostpaidStatus() string
|
||||
|
||||
GetCpuCoreCount() int
|
||||
GetMemorySizeMB() int
|
||||
|
||||
GetOsName() string
|
||||
|
||||
GetSysDiskResizable() bool
|
||||
GetSysDiskType() string
|
||||
GetSysDiskMinSizeGB() int
|
||||
GetSysDiskMaxSizeGB() int
|
||||
|
||||
GetAttachedDiskType() string
|
||||
GetAttachedDiskSizeGB() int
|
||||
GetAttachedDiskCount() int
|
||||
|
||||
GetDataDiskTypes() string
|
||||
GetDataDiskMaxCount() int
|
||||
|
||||
GetNicType() string
|
||||
GetNicMaxCount() int
|
||||
|
||||
GetGpuAttachable() bool
|
||||
GetGpuSpec() string
|
||||
GetGpuCount() int
|
||||
GetGpuMaxCount() int
|
||||
}
|
||||
|
||||
@@ -162,6 +162,8 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu
|
||||
if createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
guest.SetExternalId(iVM.GetGlobalId())
|
||||
|
||||
log.Debugf("VMcreated %s, wait status ready ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
|
||||
@@ -133,6 +133,8 @@ func (self *SAwsGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest
|
||||
return nil, createErr
|
||||
}
|
||||
|
||||
guest.SetExternalId(iVM.GetGlobalId())
|
||||
|
||||
log.Debugf("VMcreated %s, wait status running ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
|
||||
@@ -154,11 +154,12 @@ func (self *SAzureGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gue
|
||||
}
|
||||
|
||||
iVM, createErr := ihost.CreateVM(&desc)
|
||||
|
||||
if createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
|
||||
guest.SetExternalId(iVM.GetGlobalId())
|
||||
|
||||
log.Debugf("VMcreated %s, wait status running ...", iVM.GetGlobalId())
|
||||
if err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -123,6 +123,8 @@ func (self *SHuaweiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu
|
||||
if createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
guest.SetExternalId(iVM.GetGlobalId())
|
||||
|
||||
log.Debugf("VMcreated %s, wait status ready ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
|
||||
@@ -49,6 +49,7 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex
|
||||
disk := disks[i].GetDisk()
|
||||
storage := disk.GetStorage()
|
||||
if i == 0 {
|
||||
config.SysDisk.Name = disk.Name
|
||||
config.SysDisk.StorageType = storage.StorageType
|
||||
config.SysDisk.SizeGB = disk.DiskSize / 1024
|
||||
cache := storage.GetStoragecache()
|
||||
@@ -64,6 +65,7 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex
|
||||
dataDisk := cloudprovider.SDiskInfo{
|
||||
SizeGB: disk.DiskSize / 1024,
|
||||
StorageType: storage.StorageType,
|
||||
Name: disk.Name,
|
||||
}
|
||||
config.DataDisks = append(config.DataDisks, dataDisk)
|
||||
}
|
||||
@@ -467,7 +469,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context.
|
||||
}
|
||||
externalIds = append(externalIds, extID)
|
||||
}
|
||||
return nil, iVM.AssignSecurityGroups(externalIds)
|
||||
return nil, iVM.SetSecurityGroups(externalIds)
|
||||
}
|
||||
|
||||
iDisks, err := iVM.GetIDisks()
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
package guestdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
)
|
||||
|
||||
type SOpenStackGuestDriver struct {
|
||||
@@ -29,3 +42,282 @@ func (self *SOpenStackGuestDriver) GetDefaultSysDiskBackend() string {
|
||||
func (self *SOpenStackGuestDriver) GetMinimalSysDiskSizeGb() int {
|
||||
return options.Options.DefaultDiskSizeMB / 1024
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) ChooseHostStorage(host *models.SHost, backend string) *models.SStorage {
|
||||
storages := host.GetAttachedStorages("")
|
||||
for i := 0; i < len(storages); i++ {
|
||||
if storages[i].StorageType == backend {
|
||||
return &storages[i]
|
||||
}
|
||||
}
|
||||
for _, stype := range []string{models.STORAGE_OPENSTACK_ISCSI} {
|
||||
for i := 0; i < len(storages); i++ {
|
||||
if storages[i].StorageType == stype {
|
||||
return &storages[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
||||
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
||||
return []string{models.VM_READY, models.VM_RUNNING, models.VM_REBUILD_ROOT_FAIL}, nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) GetChangeConfigStatus() ([]string, error) {
|
||||
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) GetDeployStatus() ([]string, error) {
|
||||
return []string{models.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
data, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data.Contains("net.0") && data.Contains("net.1") {
|
||||
return nil, httperrors.NewInputParameterError("cannot support more than 1 nic")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams())
|
||||
if err != nil {
|
||||
log.Errorf("GetDeployConfigOnHost error: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Debugf("RequestDeployGuestOnHost: %s", config)
|
||||
|
||||
desc := cloudprovider.SManagedVMCreateConfig{}
|
||||
if err := desc.GetConfig(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
action, err := config.GetString("action")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ihost, err := host.GetIHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if action == "create" {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
iVM, createErr := ihost.CreateVM(&desc)
|
||||
if createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
|
||||
// 避免部署失败后,不能删除openstack平台机器
|
||||
guest.SetExternalId(iVM.GetGlobalId())
|
||||
|
||||
log.Debugf("VMcreated %s, wait status running ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("VMcreated %s, and status is running", iVM.GetGlobalId())
|
||||
|
||||
iVM, err = ihost.GetIVMById(iVM.GetGlobalId())
|
||||
if err != nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cloudprovider.RetryUntil(func() (bool, error) {
|
||||
idisks, err := iVM.GetIDisks()
|
||||
if err != nil {
|
||||
log.Errorf("cannot find vm disks %s", err)
|
||||
return false, err
|
||||
}
|
||||
if len(idisks) == len(desc.DataDisks)+1 {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, "root", desc.Password, action)
|
||||
return data, nil
|
||||
})
|
||||
} else if action == "deploy" {
|
||||
iVM, err := ihost.GetIVMById(guest.GetExternalId())
|
||||
if err != nil || iVM == nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return fmt.Errorf("cannot find vm")
|
||||
}
|
||||
|
||||
params := task.GetParams()
|
||||
log.Debugf("Deploy VM params %s", params.String())
|
||||
|
||||
deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
err := iVM.DeployVM(ctx, desc.Name, desc.Password, desc.PublicKey, deleteKeypair, desc.Description)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, "root", desc.Password, action)
|
||||
return data, nil
|
||||
})
|
||||
} else if action == "rebuild" {
|
||||
|
||||
iVM, err := ihost.GetIVMById(guest.GetExternalId())
|
||||
if err != nil || iVM == nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return fmt.Errorf("cannot find vm")
|
||||
}
|
||||
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
diskId, err := iVM.RebuildRoot(ctx, desc.ExternalImageId, desc.Password, desc.PublicKey, desc.SysDisk.SizeGB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("VMrebuildRoot %s new diskID %s, wait status ready ...", iVM.GetGlobalId(), diskId)
|
||||
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId())
|
||||
|
||||
maxWaitSecs := 300
|
||||
waited := 0
|
||||
|
||||
for {
|
||||
// hack, wait disk number consistent
|
||||
idisks, err := iVM.GetIDisks()
|
||||
if err != nil {
|
||||
log.Errorf("fail to find VM idisks %s", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(idisks) < len(desc.DataDisks)+1 {
|
||||
if waited > maxWaitSecs {
|
||||
log.Errorf("inconsistent disk number, wait timeout, must be something wrong on remote")
|
||||
return nil, cloudprovider.ErrTimeout
|
||||
}
|
||||
log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1)
|
||||
time.Sleep(time.Second * 5)
|
||||
waited += 5
|
||||
} else {
|
||||
if idisks[0].GetGlobalId() != diskId {
|
||||
log.Errorf("system disk id inconsistent %s != %s", idisks[0].GetGlobalId(), diskId)
|
||||
return nil, fmt.Errorf("inconsistent sys disk id after rebuild root")
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, "root", desc.Password, action)
|
||||
|
||||
return data, nil
|
||||
})
|
||||
|
||||
} else {
|
||||
log.Errorf("RequestDeployGuestOnHost: Action %s not supported", action)
|
||||
return fmt.Errorf("Action %s not supported", action)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
ihost, err := host.GetIHost()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iVM, err := ihost.GetIVMById(guest.ExternalId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fwOnly, _ := task.GetParams().Bool("fw_only"); fwOnly {
|
||||
iregion, err := host.GetIRegion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secgroups := guest.GetSecgroups()
|
||||
externalIds := []string{}
|
||||
for _, secgroup := range secgroups {
|
||||
|
||||
lockman.LockRawObject(ctx, "secgroupcache", fmt.Sprintf("%s-normal", guest.SecgrpId))
|
||||
defer lockman.ReleaseRawObject(ctx, "secgroupcache", fmt.Sprintf("%s-normal", guest.SecgrpId))
|
||||
|
||||
secgroupCache := models.SecurityGroupCacheManager.Register(ctx, task.GetUserCred(), secgroup.Id, "normal", host.GetRegion().Id, host.ManagerId)
|
||||
if secgroupCache == nil {
|
||||
return nil, fmt.Errorf("failed to registor secgroupCache for secgroup: %s", secgroup.Id)
|
||||
}
|
||||
extID, err := iregion.SyncSecurityGroup(secgroupCache.ExternalId, "", secgroup.Name, secgroup.Description, secgroup.GetSecRules(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = secgroupCache.SetExternalId(extID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
externalIds = append(externalIds, extID)
|
||||
}
|
||||
return nil, iVM.SetSecurityGroups(externalIds)
|
||||
}
|
||||
|
||||
iDisks, err := iVM.GetIDisks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disks := make([]models.SDisk, 0)
|
||||
for _, guestdisk := range guest.GetDisks() {
|
||||
disk := guestdisk.GetDisk()
|
||||
disks = append(disks, *disk)
|
||||
}
|
||||
|
||||
added := make([]models.SDisk, 0)
|
||||
commondb := make([]models.SDisk, 0)
|
||||
commonext := make([]cloudprovider.ICloudDisk, 0)
|
||||
removed := make([]cloudprovider.ICloudDisk, 0)
|
||||
|
||||
if err := compare.CompareSets(disks, iDisks, &added, &commondb, &commonext, &removed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, disk := range removed {
|
||||
if err := iVM.DetachDisk(ctx, disk.GetId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, disk := range added {
|
||||
if err := iVM.AttachDisk(ctx, disk.ExternalId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) AllowReconfigGuest() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -181,6 +181,7 @@ func (self *SQcloudGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu
|
||||
if createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
guest.SetExternalId(iVM.GetGlobalId())
|
||||
|
||||
log.Debugf("VMcreated %s, wait status running ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
|
||||
@@ -350,7 +351,7 @@ func (self *SQcloudGuestDriver) RequestSyncConfigOnHost(ctx context.Context, gue
|
||||
}
|
||||
externalIds = append(externalIds, extID)
|
||||
}
|
||||
return nil, iVM.AssignSecurityGroups(externalIds)
|
||||
return nil, iVM.SetSecurityGroups(externalIds)
|
||||
}
|
||||
|
||||
iDisks, err := iVM.GetIDisks()
|
||||
|
||||
@@ -16,3 +16,11 @@ func init() {
|
||||
func (self *SOpenStackHostDriver) GetHostType() string {
|
||||
return models.HOST_TYPE_OPENSTACK
|
||||
}
|
||||
|
||||
func (self *SOpenStackHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *SOpenStackHostDriver) GetStoragecacheQuota(host *models.SHost) int {
|
||||
return 100
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ var PUBLIC_CLOUD_HYPERVISORS = []string{
|
||||
HYPERVISOR_AZURE,
|
||||
HYPERVISOR_QCLOUD,
|
||||
HYPERVISOR_HUAWEI,
|
||||
HYPERVISOR_OPENSTACK,
|
||||
}
|
||||
|
||||
// var HYPERVISORS = []string{HYPERVISOR_ALIYUN}
|
||||
@@ -2909,9 +2910,9 @@ func (self *SGuest) GetDeployConfigOnHost(ctx context.Context, userCred mcclient
|
||||
registerVpcId := vpc.ExternalId
|
||||
externalVpcId := vpc.ExternalId
|
||||
switch self.Hypervisor {
|
||||
case HYPERVISOR_ALIYUN, HYPERVISOR_AWS, HYPERVISOR_OPENSTACK, HYPERVISOR_HUAWEI:
|
||||
case HYPERVISOR_ALIYUN, HYPERVISOR_AWS, HYPERVISOR_HUAWEI:
|
||||
break
|
||||
case HYPERVISOR_QCLOUD:
|
||||
case HYPERVISOR_QCLOUD, HYPERVISOR_OPENSTACK:
|
||||
registerVpcId = "normal"
|
||||
case HYPERVISOR_AZURE:
|
||||
registerVpcId, externalVpcId = "normal", "normal"
|
||||
|
||||
@@ -9,9 +9,11 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
@@ -644,6 +646,15 @@ func (manager *SServerSkuManager) GetSkuCountByRegion(regionId string) int {
|
||||
return q.Count()
|
||||
}
|
||||
|
||||
func (manager *SServerSkuManager) GetSkuCountByZone(zoneId string) []SServerSku {
|
||||
skus := []SServerSku{}
|
||||
q := manager.Query().Equals("zone_id", zoneId)
|
||||
if err := db.FetchModelObjects(manager, q, &skus); err != nil {
|
||||
log.Errorf("failed to get skus by zoneId %s error: %v", zoneId, err)
|
||||
}
|
||||
return skus
|
||||
}
|
||||
|
||||
// 删除表中zone not found的记录
|
||||
func (manager *SServerSkuManager) PendingDeleteInvalidSku() error {
|
||||
sq := ZoneManager.Query("id").Distinct().SubQuery()
|
||||
@@ -670,3 +681,99 @@ func (manager *SServerSkuManager) PendingDeleteInvalidSku() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SServerSkuManager) SyncCloudSkusByRegion(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, zone *SZone, skus []cloudprovider.ICloudSku) compare.SyncResult {
|
||||
syncResult := compare.SyncResult{}
|
||||
dbSkus := manager.GetSkuCountByZone(zone.Id)
|
||||
|
||||
removed := []SServerSku{}
|
||||
commondb := []SServerSku{}
|
||||
commonext := []cloudprovider.ICloudSku{}
|
||||
added := []cloudprovider.ICloudSku{}
|
||||
|
||||
if err := compare.CompareSets(dbSkus, skus, &removed, &commondb, &commonext, &added); err != nil {
|
||||
syncResult.Error(err)
|
||||
return syncResult
|
||||
}
|
||||
for i := 0; i < len(removed); i++ {
|
||||
if err := removed[i].ValidateDeleteCondition(ctx); err == nil {
|
||||
removed[i].Delete(ctx, userCred)
|
||||
}
|
||||
syncResult.Delete()
|
||||
}
|
||||
|
||||
for i := 0; i < len(commondb); i++ {
|
||||
err := commondb[i].syncWithCloudSku(ctx, userCred, commonext[i], zone, provider)
|
||||
if err != nil {
|
||||
syncResult.UpdateError(err)
|
||||
} else {
|
||||
syncResult.Update()
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(added); i++ {
|
||||
err := manager.newFromCloudSku(ctx, userCred, added[i], zone, provider)
|
||||
if err != nil {
|
||||
syncResult.AddError(err)
|
||||
} else {
|
||||
syncResult.Add()
|
||||
}
|
||||
}
|
||||
return syncResult
|
||||
}
|
||||
|
||||
func (self *SServerSku) constructSku(extSku cloudprovider.ICloudSku) {
|
||||
self.InstanceTypeFamily = extSku.GetInstanceTypeFamily()
|
||||
self.InstanceTypeCategory = extSku.GetInstanceTypeCategory()
|
||||
|
||||
self.PrepaidStatus = extSku.GetPrepaidStatus()
|
||||
self.PostpaidStatus = extSku.GetPostpaidStatus()
|
||||
|
||||
self.CpuCoreCount = extSku.GetCpuCoreCount()
|
||||
self.MemorySizeMB = extSku.GetMemorySizeMB()
|
||||
|
||||
self.OsName = extSku.GetOsName()
|
||||
|
||||
self.SysDiskResizable = extSku.GetSysDiskResizable()
|
||||
self.SysDiskType = extSku.GetSysDiskType()
|
||||
self.SysDiskMinSizeGB = extSku.GetSysDiskMinSizeGB()
|
||||
self.SysDiskMaxSizeGB = extSku.GetSysDiskMaxSizeGB()
|
||||
|
||||
self.AttachedDiskType = extSku.GetAttachedDiskType()
|
||||
self.AttachedDiskSizeGB = extSku.GetAttachedDiskSizeGB()
|
||||
self.AttachedDiskCount = extSku.GetAttachedDiskCount()
|
||||
|
||||
self.DataDiskTypes = extSku.GetDataDiskTypes()
|
||||
self.DataDiskMaxCount = extSku.GetDataDiskMaxCount()
|
||||
|
||||
self.NicType = extSku.GetNicType()
|
||||
self.NicMaxCount = extSku.GetNicMaxCount()
|
||||
|
||||
self.GpuAttachable = extSku.GetGpuAttachable()
|
||||
self.GpuSpec = extSku.GetGpuSpec()
|
||||
self.GpuCount = extSku.GetGpuCount()
|
||||
self.GpuMaxCount = extSku.GetGpuMaxCount()
|
||||
self.Name = extSku.GetName()
|
||||
}
|
||||
|
||||
func (self *SServerSku) syncWithCloudSku(ctx context.Context, userCred mcclient.TokenCredential, extSku cloudprovider.ICloudSku, zone *SZone, provider *SCloudprovider) error {
|
||||
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
|
||||
self.constructSku(extSku)
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (manager *SServerSkuManager) newFromCloudSku(ctx context.Context, userCred mcclient.TokenCredential, extSku cloudprovider.ICloudSku, zone *SZone, provider *SCloudprovider) error {
|
||||
region := zone.GetRegion()
|
||||
sku := &SServerSku{
|
||||
CloudregionId: region.Id,
|
||||
ZoneId: zone.Id,
|
||||
Provider: provider.Provider,
|
||||
}
|
||||
sku.constructSku(extSku)
|
||||
sku.Name = extSku.GetName()
|
||||
sku.ExternalId = extSku.GetGlobalId()
|
||||
sku.SetModelManager(manager)
|
||||
return manager.TableSpec().Insert(sku)
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ var (
|
||||
STORAGE_GP2_SSD, STORAGE_IO1_SSD, STORAGE_ST1_HDD, STORAGE_SC1_HDD, STORAGE_STANDARD_HDD,
|
||||
STORAGE_LOCAL_BASIC, STORAGE_LOCAL_SSD, STORAGE_CLOUD_BASIC, STORAGE_CLOUD_PREMIUM,
|
||||
STORAGE_HUAWEI_SSD, STORAGE_HUAWEI_SAS, STORAGE_HUAWEI_SATA,
|
||||
STORAGE_OPENSTACK_ISCSI,
|
||||
}
|
||||
|
||||
STORAGE_LIMITED_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_NAS, STORAGE_RBD, STORAGE_NFS}
|
||||
|
||||
@@ -710,7 +710,7 @@ func (self *SZone) getMaxDataDiskCount() int {
|
||||
}
|
||||
|
||||
func (manager *SZoneManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
regionStr := jsonutils.GetAnyString(query, []string{"region", "region_id", "cloudregion", "cloudregion_id"})
|
||||
regionStr := jsonutils.GetAnyString(data, []string{"region", "region_id", "cloudregion", "cloudregion_id"})
|
||||
var regionId string
|
||||
if len(regionStr) > 0 {
|
||||
regionObj, err := CloudregionManager.FetchByIdOrName(nil, regionStr)
|
||||
|
||||
@@ -159,7 +159,9 @@ func syncPublicCloudProviderInfo(ctx context.Context, provider *models.SCloudpro
|
||||
|
||||
localZones, remoteZones := syncRegionZones(ctx, provider, task, &localRegions[i], remoteRegions[i])
|
||||
|
||||
syncRegionSkus(ctx, provider, task, &localRegions[i])
|
||||
if !driver.GetFactory().NeedSyncSkuFromCloud() {
|
||||
syncRegionSkus(ctx, provider, task, &localRegions[i])
|
||||
}
|
||||
|
||||
syncRegionVPCs(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange)
|
||||
|
||||
@@ -177,6 +179,10 @@ func syncPublicCloudProviderInfo(ctx context.Context, provider *models.SCloudpro
|
||||
if len(newPairs) > 0 {
|
||||
storageCachePairs = append(storageCachePairs, newPairs...)
|
||||
}
|
||||
|
||||
if driver.GetFactory().NeedSyncSkuFromCloud() {
|
||||
syncRegionSkusFromCloud(ctx, provider, task, &localZones[i], remoteRegions[i], remoteZones[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
syncRegionSnapshots(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange)
|
||||
@@ -329,6 +335,24 @@ func syncLoadbalancerBackends(ctx context.Context, provider *models.SCloudprovid
|
||||
}
|
||||
}
|
||||
|
||||
func syncRegionSkusFromCloud(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localZone *models.SZone, remoteRegion cloudprovider.ICloudRegion, remoteZone cloudprovider.ICloudZone) {
|
||||
skus, err := remoteRegion.GetSkus(remoteZone.GetId())
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("GetSkus for zone %s failed %v", localZone.Name, err)
|
||||
log.Errorf(msg)
|
||||
logSyncFailed(provider, task, msg)
|
||||
return
|
||||
}
|
||||
|
||||
result := models.ServerSkuManager.SyncCloudSkusByRegion(ctx, task.GetUserCred(), provider, localZone, skus)
|
||||
msg := result.Result()
|
||||
log.Infof("SyncCloudSkusByRegion for zone %s result: %s", localZone.Name, msg)
|
||||
if result.IsError() {
|
||||
logSyncFailed(provider, task, msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func syncRegionSkus(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion) {
|
||||
if localRegion == nil {
|
||||
log.Debugf("local region is nil skipped.")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package hostman
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"yunion.io/x/log"
|
||||
@@ -23,8 +22,6 @@ import (
|
||||
|
||||
type SHostService struct {
|
||||
service.SServiceBase
|
||||
|
||||
isExiting bool
|
||||
}
|
||||
|
||||
func (host *SHostService) StartService() {
|
||||
@@ -37,28 +34,6 @@ func (host *SHostService) StartService() {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
host.RegisterSIGUSR1()
|
||||
host.RegisterQuitSignals(func() { // register quit handler
|
||||
if host.isExiting {
|
||||
return
|
||||
} else {
|
||||
host.isExiting = true
|
||||
}
|
||||
|
||||
if app.IsInServe() {
|
||||
if err := app.ShutDown(context.Background()); err != nil {
|
||||
log.Errorln(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
hostinfo.Stop()
|
||||
storageman.Stop()
|
||||
hostmetrics.Stop()
|
||||
guestman.Stop()
|
||||
hostutils.GetWorkManager().Stop()
|
||||
os.Exit(0)
|
||||
})
|
||||
|
||||
if err := storageman.Init(hostInstance); err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
@@ -82,20 +57,13 @@ func (host *SHostService) StartService() {
|
||||
cronManager.AddJob2(
|
||||
"CleanRecycleDiskFiles", 1, 3, 0, 0, storageman.CleanRecycleDiskfiles, false)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if !host.isExiting {
|
||||
log.Fatalf("%s", r)
|
||||
} else {
|
||||
log.Errorln(r)
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
cloudcommon.ServeForever(app, &options.HostOptions.CommonOptions)
|
||||
}()
|
||||
select {} // for quit handler
|
||||
cloudcommon.ServeForeverWithCleanup(app, &options.HostOptions.CommonOptions, func() {
|
||||
hostinfo.Stop()
|
||||
storageman.Stop()
|
||||
hostmetrics.Stop()
|
||||
guestman.Stop()
|
||||
hostutils.GetWorkManager().Stop()
|
||||
})
|
||||
}
|
||||
|
||||
func (host *SHostService) initHandlers(app *appsrv.Application) {
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
t "github.com/anacrolix/torrent"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -89,10 +87,12 @@ func (self *SImageSubformat) DoConvert(image *SImage) error {
|
||||
log.Errorf("fail to convert image torrent %s", err)
|
||||
return err
|
||||
}
|
||||
err = self.seedTorrent()
|
||||
if err != nil {
|
||||
log.Errorf("fail to seed torrent %s", err)
|
||||
return err
|
||||
if options.Options.EnableTorrentService {
|
||||
err = self.seedTorrent(image.Id)
|
||||
if err != nil {
|
||||
log.Errorf("fail to seed torrent %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// log.Infof("Start seeding...")
|
||||
return nil
|
||||
@@ -206,10 +206,10 @@ func (self *SImageSubformat) getLocalTorrentLocation() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SImageSubformat) seedTorrent() error {
|
||||
func (self *SImageSubformat) seedTorrent(imageId string) error {
|
||||
file := self.getLocalTorrentLocation()
|
||||
log.Debugf("add torrent %s to seed...", file)
|
||||
return torrent.AddTorrent(file)
|
||||
return torrent.SeedTorrent(file, imageId, self.Format)
|
||||
}
|
||||
|
||||
func (self *SImageSubformat) StopTorrent() {
|
||||
@@ -237,14 +237,6 @@ func (self *SImageSubformat) RemoveFiles() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SImageSubformat) getTorrent() *t.Torrent {
|
||||
torrentPath := self.getLocalTorrentLocation()
|
||||
if len(torrentPath) > 0 {
|
||||
return torrent.GetTorrent(torrentPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SImageSubformatDetails struct {
|
||||
Format string
|
||||
|
||||
@@ -258,7 +250,6 @@ type SImageSubformatDetails struct {
|
||||
TorrentStatus string
|
||||
|
||||
TorrentSeeding bool
|
||||
TorrentStats t.TorrentStats
|
||||
}
|
||||
|
||||
func (self *SImageSubformat) GetDetails() SImageSubformatDetails {
|
||||
@@ -273,14 +264,11 @@ func (self *SImageSubformat) GetDetails() SImageSubformatDetails {
|
||||
details.TorrentChecksum = self.TorrentChecksum
|
||||
details.TorrentStatus = self.TorrentStatus
|
||||
|
||||
t := self.getTorrent()
|
||||
|
||||
if t != nil {
|
||||
if t.BytesMissing() == 0 {
|
||||
details.TorrentSeeding = true
|
||||
}
|
||||
details.TorrentStats = t.Stats()
|
||||
filePath := self.getLocalTorrentLocation()
|
||||
if len(filePath) > 0 {
|
||||
details.TorrentSeeding = torrent.GetTorrentSeeding(filePath)
|
||||
}
|
||||
|
||||
return details
|
||||
}
|
||||
|
||||
@@ -342,3 +330,10 @@ func (self *SImageSubformat) checkStatus(useFast bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SImageSubformat) SetStatusSeeding(seeding bool) {
|
||||
filePath := self.getLocalTorrentLocation()
|
||||
if len(filePath) > 0 {
|
||||
torrent.SetTorrentSeeding(filePath, seeding)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,7 +899,7 @@ func (self *SImage) StopTorrents() {
|
||||
func (self *SImage) seedTorrents() {
|
||||
subimgs := ImageSubformatManager.GetAllSubImages(self.Id)
|
||||
for i := 0; i < len(subimgs); i += 1 {
|
||||
subimgs[i].seedTorrent()
|
||||
subimgs[i].seedTorrent(self.Id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1046,7 +1046,7 @@ func (self *SImage) DoCheckStatus(ctx context.Context, userCred mcclient.TokenCr
|
||||
if needConvert {
|
||||
log.Infof("Image %s is active and need convert", self.Name)
|
||||
self.StartImageConvertTask(ctx, userCred, "")
|
||||
} else {
|
||||
} else if options.Options.EnableTorrentService {
|
||||
self.seedTorrents()
|
||||
}
|
||||
}
|
||||
@@ -1081,3 +1081,20 @@ func (self *SImage) PerformMarkPublicProtected(
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SImage) AllowPerformUpdateTorrentStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SImage) PerformUpdateTorrentStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
formatStr, _ := query.GetString("format")
|
||||
if len(formatStr) == 0 {
|
||||
return nil, httperrors.NewInputParameterError("missing parameter format")
|
||||
}
|
||||
subimg := ImageSubformatManager.FetchSubImage(self.Id, formatStr)
|
||||
if subimg == nil {
|
||||
return nil, httperrors.NewResourceNotFoundError("format %s not found", formatStr)
|
||||
}
|
||||
subimg.SetStatusSeeding(true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ type SImageOptions struct {
|
||||
EnableTorrentService bool `help:"Enable torrent service" default:"false"`
|
||||
|
||||
TargetImageFormats []string `help:"target image formats that the system will automatically convert to" default:"qcow2,vmdk,vhd"`
|
||||
|
||||
TorrentClientPath string `help:"path to torrent executable" default:"/opt/yunion/bin/torrent"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -62,35 +62,37 @@ func StartService() {
|
||||
log.Infof("Auth complete!!")
|
||||
})
|
||||
|
||||
trackers := torrent.GetTrackers()
|
||||
if len(trackers) == 0 {
|
||||
log.Errorf("no valid torrent-tracker")
|
||||
return
|
||||
}
|
||||
|
||||
cloudcommon.InitDB(dbOpts)
|
||||
defer cloudcommon.CloseDB()
|
||||
|
||||
app := cloudcommon.InitApp(commonOpts, true)
|
||||
initHandlers(app)
|
||||
|
||||
if opts.EnableTorrentService {
|
||||
err := torrent.InitTorrentClient()
|
||||
if err != nil {
|
||||
log.Errorf("fail to initialize torrent client: %s", err)
|
||||
return
|
||||
}
|
||||
torrent.InitTorrentHandler(app)
|
||||
defer torrent.CloseTorrentClient()
|
||||
}
|
||||
|
||||
if !db.CheckSync(opts.AutoSyncTable) {
|
||||
log.Fatalf("database schema not in sync!")
|
||||
}
|
||||
|
||||
models.InitDB()
|
||||
|
||||
models.CheckImages()
|
||||
go models.CheckImages()
|
||||
|
||||
cron := cronman.GetCronJobManager(true)
|
||||
cron.AddJob1("CleanPendingDeleteImages", time.Duration(options.Options.PendingDeleteCheckSeconds)*time.Second, models.ImageManager.CleanPendingDeleteImages)
|
||||
|
||||
cron.Start()
|
||||
defer cron.Stop()
|
||||
|
||||
cloudcommon.ServeForever(app, commonOpts)
|
||||
cloudcommon.ServeForeverWithCleanup(app, commonOpts, func() {
|
||||
cloudcommon.CloseDB()
|
||||
|
||||
cron.Stop()
|
||||
|
||||
if options.Options.EnableTorrentService {
|
||||
torrent.StopTorrents()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,28 +1,52 @@
|
||||
package torrent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/image/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/nodeid"
|
||||
"yunion.io/x/onecloud/pkg/util/sysutils"
|
||||
)
|
||||
|
||||
type STorrentProcessState struct {
|
||||
process *os.Process
|
||||
seeding bool
|
||||
}
|
||||
|
||||
var (
|
||||
torrentClient *torrent.Client
|
||||
torrentTable = make(map[string]*torrent.Torrent)
|
||||
torrentTable = make(map[string]*STorrentProcessState)
|
||||
seedTaskWorkerMan *appsrv.SWorkerManager
|
||||
)
|
||||
|
||||
const (
|
||||
TORRENT_TRACKER_SERVICE = "torrent-tracker"
|
||||
)
|
||||
|
||||
func init() {
|
||||
seedTaskWorkerMan = appsrv.NewWorkerManager("seedTaskWorkerManager", 1, 1024, false)
|
||||
}
|
||||
|
||||
func (stat *STorrentProcessState) StopAndWait() error {
|
||||
err := stat.process.Kill()
|
||||
if err != nil {
|
||||
log.Errorf("kill error %s", err)
|
||||
return err
|
||||
}
|
||||
_, err = stat.process.Wait()
|
||||
if err != nil {
|
||||
log.Errorf("wait error %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetTrackers() []string {
|
||||
urls, err := auth.GetServiceURLs("torrent-tracker", options.Options.Region, "", "")
|
||||
urls, err := auth.GetServiceURLs(TORRENT_TRACKER_SERVICE, options.Options.Region, "", "")
|
||||
if err != nil {
|
||||
log.Errorf("fail to get torrent-tracker")
|
||||
return nil
|
||||
@@ -30,93 +54,62 @@ func GetTrackers() []string {
|
||||
return urls
|
||||
}
|
||||
|
||||
func InitTorrentClient() error {
|
||||
urls := GetTrackers()
|
||||
if len(urls) == 0 {
|
||||
log.Errorf("no valid torrent-tracker")
|
||||
return fmt.Errorf("no valid torrent-tracker")
|
||||
}
|
||||
|
||||
nodeId, err := nodeid.GetNodeId()
|
||||
if err != nil {
|
||||
log.Errorf("fail to generate node id: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Set torrent server as node %s", nodeId)
|
||||
|
||||
clientConfig := torrent.NewDefaultClientConfig()
|
||||
clientConfig.PeerID = nodeId[:20]
|
||||
clientConfig.Debug = false
|
||||
clientConfig.Seed = true
|
||||
clientConfig.NoUpload = false
|
||||
clientConfig.DataDir = options.Options.FilesystemStoreDatadir
|
||||
clientConfig.DisableTrackers = false
|
||||
clientConfig.DisablePEX = true
|
||||
clientConfig.NoDHT = true
|
||||
|
||||
client, err := torrent.NewClient(clientConfig)
|
||||
if err != nil {
|
||||
log.Errorf("error creating client: %s", err)
|
||||
return err
|
||||
}
|
||||
torrentClient = client
|
||||
|
||||
log.Infof("torrent client initialized")
|
||||
|
||||
func SeedTorrent(torrentpath string, imageId, format string) error {
|
||||
seedTaskWorkerMan.Run(func() {
|
||||
log.Infof("Start seed %s ...", torrentpath)
|
||||
err := seedTorrent(torrentpath, imageId, format)
|
||||
if err == nil {
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}, nil, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitTorrentHandler(app *appsrv.Application) {
|
||||
app.AddDefaultHandler("GET", "/torrent_stats", TorrentStatsHandler, "torrent_stats")
|
||||
}
|
||||
|
||||
func TorrentStatsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
if torrentClient != nil {
|
||||
torrentClient.WriteStatus(w)
|
||||
}
|
||||
}
|
||||
|
||||
func CloseTorrentClient() {
|
||||
if torrentClient != nil {
|
||||
torrentClient.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func AddTorrent(filepath string) error {
|
||||
if torrentClient == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
mi, err := metainfo.LoadFromFile(filepath)
|
||||
func seedTorrent(torrentpath string, imageId, format string) error {
|
||||
url, err := auth.GetServiceURL("image", options.Options.Region, "", "public")
|
||||
if err != nil {
|
||||
log.Errorf("fail to open torrent file %s", err)
|
||||
return err
|
||||
}
|
||||
t, err := torrentClient.AddTorrent(mi)
|
||||
args := []string{
|
||||
options.Options.TorrentClientPath,
|
||||
options.Options.FilesystemStoreDatadir,
|
||||
torrentpath,
|
||||
"--callback-url",
|
||||
fmt.Sprintf("%s/images/%s/update-torrent-status?format=%s", url, imageId, format),
|
||||
}
|
||||
proc, err := sysutils.Start(false, args...)
|
||||
if err != nil {
|
||||
log.Errorf("AddTorrent fail %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
torrentTable[filepath] = t
|
||||
|
||||
<-t.GotInfo()
|
||||
t.DownloadAll()
|
||||
|
||||
torrentTable[torrentpath] = &STorrentProcessState{
|
||||
process: proc,
|
||||
seeding: false,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetTorrent(filepath string) *torrent.Torrent {
|
||||
func SetTorrentSeeding(filepath string, seeding bool) {
|
||||
if _, ok := torrentTable[filepath]; ok {
|
||||
torrentTable[filepath].seeding = seeding
|
||||
}
|
||||
}
|
||||
|
||||
func GetTorrentSeeding(filepath string) bool {
|
||||
if t, ok := torrentTable[filepath]; ok {
|
||||
return t
|
||||
return t.seeding
|
||||
}
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
func RemoveTorrent(filepath string) {
|
||||
if t, ok := torrentTable[filepath]; ok {
|
||||
t.Drop()
|
||||
t.StopAndWait()
|
||||
delete(torrentTable, filepath)
|
||||
}
|
||||
}
|
||||
|
||||
func StopTorrents() {
|
||||
for k := range torrentTable {
|
||||
torrentTable[k].StopAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
var (
|
||||
GuestToken = mcclient.SSimpleToken{
|
||||
User: "guest",
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
AUTH_TOKEN = appctx.AppContextKey("X_AUTH_TOKEN")
|
||||
)
|
||||
@@ -23,24 +29,26 @@ func Authenticate(f appsrv.FilterHandler) appsrv.FilterHandler {
|
||||
func AuthenticateWithDelayDecision(f appsrv.FilterHandler, delayDecision bool) appsrv.FilterHandler {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
tokenStr := r.Header.Get(mcclient.AUTH_TOKEN)
|
||||
var token mcclient.TokenCredential
|
||||
if len(tokenStr) == 0 {
|
||||
log.Errorf("no auth_token found!")
|
||||
if !delayDecision {
|
||||
httperrors.UnauthorizedError(w, "Unauthorized")
|
||||
return
|
||||
}
|
||||
}
|
||||
token, err := Verify(tokenStr)
|
||||
if err != nil {
|
||||
log.Errorf("Verify token failed: %s", err)
|
||||
if !delayDecision {
|
||||
httperrors.UnauthorizedError(w, "InvalidToken")
|
||||
return
|
||||
token = &GuestToken
|
||||
} else {
|
||||
var err error
|
||||
token, err = Verify(tokenStr)
|
||||
if err != nil {
|
||||
log.Errorf("Verify token failed: %s", err)
|
||||
if !delayDecision {
|
||||
httperrors.UnauthorizedError(w, "InvalidToken")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if token != nil {
|
||||
ctx = context.WithValue(ctx, AUTH_TOKEN, token)
|
||||
}
|
||||
ctx = context.WithValue(ctx, AUTH_TOKEN, token)
|
||||
|
||||
if taskId := r.Header.Get(mcclient.TASK_ID); taskId != "" {
|
||||
ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_TASK_ID, taskId)
|
||||
|
||||
@@ -896,8 +896,8 @@ func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return self.host.zone.region.AssignSecurityGroup(secgroupId, self.InstanceId)
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
return self.host.zone.region.AssignSecurityGroups(secgroupIds, self.InstanceId)
|
||||
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
return self.host.zone.region.SetSecurityGroups(secgroupIds, self.InstanceId)
|
||||
}
|
||||
|
||||
func (self *SInstance) GetBillingType() string {
|
||||
|
||||
@@ -38,6 +38,10 @@ func (self *SAliyunProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SAliyunProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SAliyunProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
accessKeyID, _ := data.GetString("access_key_id")
|
||||
if len(accessKeyID) == 0 {
|
||||
|
||||
@@ -888,3 +888,7 @@ func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAc
|
||||
}
|
||||
return iAcl, region.AddAccessControlListEntry(aclId, acl.Entrys)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -501,10 +501,10 @@ func (self *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.Secur
|
||||
}
|
||||
|
||||
func (self *SRegion) AssignSecurityGroup(secgroupId, instanceId string) error {
|
||||
return self.AssignSecurityGroups([]string{secgroupId}, instanceId)
|
||||
return self.SetSecurityGroups([]string{secgroupId}, instanceId)
|
||||
}
|
||||
|
||||
func (self *SRegion) AssignSecurityGroups(secgroupIds []string, instanceId string) error {
|
||||
func (self *SRegion) SetSecurityGroups(secgroupIds []string, instanceId string) error {
|
||||
params := map[string]string{"InstanceId": instanceId}
|
||||
for _, secgroupId := range secgroupIds {
|
||||
params["SecurityGroupId"] = secgroupId
|
||||
|
||||
@@ -283,10 +283,10 @@ func (self *SInstance) GetMachine() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return self.AssignSecurityGroups([]string{secgroupId})
|
||||
return self.SetSecurityGroups([]string{secgroupId})
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
ids := []*string{}
|
||||
for i := 0; i < len(secgroupIds); i++ {
|
||||
ids = append(ids, &secgroupIds[i])
|
||||
|
||||
@@ -37,6 +37,10 @@ func (self *SAwsProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SAwsProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SAwsProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
accessKeyID, _ := data.GetString("access_key_id")
|
||||
if len(accessKeyID) == 0 {
|
||||
|
||||
@@ -499,3 +499,7 @@ func (region *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbala
|
||||
func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ type assignProperties struct {
|
||||
NetworkSecurityGroup SubResource `json:"networkSecurityGroup,omitempty"`
|
||||
}
|
||||
|
||||
func (self *SClassicInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
func (self *SClassicInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
|
||||
@@ -1020,7 +1020,7 @@ func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return self.host.zone.region.AssiginSecurityGroup(self.ID, secgroupId)
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ func (self *SAzureProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SAzureProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SAzureProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
directoryID, _ := data.GetString("directory_id")
|
||||
if len(directoryID) == 0 {
|
||||
|
||||
@@ -551,3 +551,7 @@ func (region *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbala
|
||||
func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ func (self *SESXiProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SESXiProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SESXiProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
username, _ := data.GetString("username")
|
||||
if len(username) == 0 {
|
||||
|
||||
@@ -528,7 +528,7 @@ func (dc *SVirtualMachine) ChangeConfig2(ctx context.Context, instanceType strin
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SVirtualMachine) AssignSecurityGroups(secgroupIds []string) error {
|
||||
func (self *SVirtualMachine) SetSecurityGroups(secgroupIds []string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"sort"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
@@ -398,10 +399,10 @@ func (self *SInstance) GetMachine() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return self.AssignSecurityGroups([]string{secgroupId})
|
||||
return self.SetSecurityGroups([]string{secgroupId})
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
currentSecgroups, err := self.host.zone.region.GetInstanceSecrityGroupIds(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -37,6 +37,10 @@ func (self *SHuaweiProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHuaweiProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHuaweiProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
accessKeyID, _ := data.GetString("access_key_id")
|
||||
if len(accessKeyID) == 0 {
|
||||
|
||||
@@ -641,3 +641,7 @@ func (region *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbala
|
||||
func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -171,20 +171,20 @@ func getLinux() ([]string, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func GetNodeId() (string, error) {
|
||||
func GetNodeId() ([]byte, error) {
|
||||
var f func() ([]string, error)
|
||||
if runtime.GOOS == "linux" {
|
||||
f = getLinux
|
||||
} else {
|
||||
return "", fmt.Errorf("Unsupported OS")
|
||||
return nil, fmt.Errorf("Unsupported OS")
|
||||
}
|
||||
|
||||
ret, e := f()
|
||||
if e != nil || len(ret) < 2 {
|
||||
log.Debugf("service info %s", ret)
|
||||
return "", fmt.Errorf("Fail to generate Node ID")
|
||||
return nil, fmt.Errorf("Fail to generate Node ID")
|
||||
}
|
||||
|
||||
sn := md5.Sum([]byte(ret[0] + ret[1]))
|
||||
return fmt.Sprintf("%x", sn), nil
|
||||
return sn[0:], nil
|
||||
}
|
||||
|
||||
+50
-20
@@ -2,6 +2,7 @@ package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -104,7 +105,7 @@ func (disk *SDisk) GetMetadata() *jsonutils.JSONDict {
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDisks(category string) ([]SDisk, error) {
|
||||
_, resp, err := region.CinderGet("/volumes/detail", "", nil)
|
||||
_, resp, err := region.CinderList("/volumes/detail", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -126,11 +127,11 @@ func (disk *SDisk) GetId() string {
|
||||
}
|
||||
|
||||
func (disk *SDisk) Delete(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return disk.storage.zone.region.DeleteDisk(disk.ID)
|
||||
}
|
||||
|
||||
func (disk *SDisk) Resize(ctx context.Context, sizeMb int64) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return disk.storage.zone.region.ResizeDisk(disk.ID, sizeMb)
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetName() string {
|
||||
@@ -179,8 +180,8 @@ func (disk *SDisk) Refresh() error {
|
||||
return jsonutils.Update(disk, new)
|
||||
}
|
||||
|
||||
func (disk *SDisk) ResizeDisk(newSize int64) error {
|
||||
return disk.storage.zone.region.ResizeDisk(disk.ID, newSize)
|
||||
func (disk *SDisk) ResizeDisk(sizeMb int64) error {
|
||||
return disk.storage.zone.region.ResizeDisk(disk.ID, sizeMb)
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetDiskFormat() string {
|
||||
@@ -226,8 +227,22 @@ func (disk *SDisk) GetMountpoint() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (disk *SRegion) CreateDisk(zoneId string, category string, name string, sizeGb int, desc string) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) CreateDisk(zoneName string, category string, name string, sizeGb int, desc string) (*SDisk, error) {
|
||||
params := map[string]map[string]interface{}{
|
||||
"volume": {
|
||||
"size": sizeGb,
|
||||
"volume_type": category,
|
||||
"name": name,
|
||||
"description": desc,
|
||||
"availability_zone": zoneName,
|
||||
},
|
||||
}
|
||||
_, resp, err := region.CinderCreate("/volumes", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disk := &SDisk{}
|
||||
return disk, resp.Unmarshal(disk, "volume")
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDisk(diskId string) (*SDisk, error) {
|
||||
@@ -239,20 +254,39 @@ func (region *SRegion) GetDisk(diskId string) (*SDisk, error) {
|
||||
return disk, resp.Unmarshal(disk, "volume")
|
||||
}
|
||||
|
||||
func (disk *SRegion) DeleteDisk(diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) DeleteDisk(diskId string) error {
|
||||
_, err := region.CinderDelete("/volumes/"+diskId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (disk *SRegion) ResizeDisk(diskId string, sizeMb int64) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) ResizeDisk(diskId string, sizeMb int64) error {
|
||||
params := map[string]map[string]interface{}{
|
||||
"os-extend": {
|
||||
"new_size": sizeMb / 1024,
|
||||
},
|
||||
}
|
||||
_, _, err := region.CinderAction(fmt.Sprintf("/volumes/%s/action", diskId), "", jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (disk *SRegion) ResetDisk(diskId, snapshotId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) ResetDisk(diskId, snapshotId string) error {
|
||||
//目前测试接口不能使用
|
||||
return cloudprovider.ErrNotSupported
|
||||
// params := map[string]map[string]interface{}{
|
||||
// "revert": {
|
||||
// "snapshot_id": snapshotId,
|
||||
// },
|
||||
// }
|
||||
// _, _, err := region.CinderAction(fmt.Sprintf("/volumes/%s/action", diskId), "3.40", jsonutils.Marshal(params))
|
||||
// return err
|
||||
}
|
||||
|
||||
func (disk *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
snapshot, err := disk.storage.zone.region.CreateSnapshot(disk.ID, name, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snapshot, cloudprovider.WaitStatus(snapshot, models.SNAPSHOT_READY, time.Second*5, time.Minute*5)
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
@@ -264,7 +298,7 @@ func (disk *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
}
|
||||
|
||||
func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
|
||||
return "", disk.storage.zone.region.ResetDisk(disk.ID, snapshotId)
|
||||
return disk.ID, disk.storage.zone.region.ResetDisk(disk.ID, snapshotId)
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetBillingType() string {
|
||||
@@ -280,9 +314,5 @@ func (disk *SDisk) GetAccessPath() string {
|
||||
}
|
||||
|
||||
func (disk *SDisk) Rebuild(ctx context.Context) error {
|
||||
return disk.storage.zone.region.RebuildDisk(disk.ID)
|
||||
}
|
||||
|
||||
func (region *SRegion) RebuildDisk(diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SFlavor struct {
|
||||
region *SRegion
|
||||
ID string
|
||||
Disk int
|
||||
Ephemeral int
|
||||
ExtraSpecs ExtraSpecs
|
||||
OriginalName string
|
||||
Name string
|
||||
RAM int
|
||||
Swap string
|
||||
Vcpus int8
|
||||
}
|
||||
|
||||
func (region *SRegion) GetFlavors() ([]SFlavor, error) {
|
||||
_, resp, err := region.List("compute", "/flavors/detail", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flavors := []SFlavor{}
|
||||
return flavors, resp.Unmarshal(&flavors, "flavors")
|
||||
}
|
||||
|
||||
func (region *SRegion) GetFlavor(flavorId string) (*SFlavor, error) {
|
||||
_, resp, err := region.Get("compute", "/flavors/"+flavorId, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flavor := &SFlavor{region: region}
|
||||
return flavor, resp.Unmarshal(flavor, "flavor")
|
||||
}
|
||||
|
||||
func (region *SRegion) syncFlavor(name string, cpu, memoryMb, diskGB int) (string, error) {
|
||||
flavors, err := region.GetFlavors()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(name) > 0 {
|
||||
for _, flavor := range flavors {
|
||||
if flavor.GetName() == name {
|
||||
return flavor.ID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cpu == 0 && memoryMb == 0 {
|
||||
return "", fmt.Errorf("failed to find instance type %s", name)
|
||||
}
|
||||
|
||||
for _, flavor := range flavors {
|
||||
if flavor.GetCpuCoreCount() == cpu && flavor.GetMemorySizeMB() == memoryMb {
|
||||
return flavor.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(name) == 0 {
|
||||
suffix := ""
|
||||
for i := 0; i < 10; i++ {
|
||||
switch cpu {
|
||||
case 1:
|
||||
suffix = "tiny"
|
||||
case 2, 3:
|
||||
suffix = "small"
|
||||
case 4, 6:
|
||||
suffix = "medium"
|
||||
case 8:
|
||||
suffix = "large"
|
||||
default:
|
||||
suffix = "xlarge"
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := region.GetFlavor(fmt.Sprintf("m%d.%s", i, suffix)); err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
name = fmt.Sprintf("m%d.%s", i, suffix)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(name) == 0 {
|
||||
return "", fmt.Errorf("failed to find uniq flavor name for cpu %d memory %d", cpu, memoryMb)
|
||||
}
|
||||
}
|
||||
|
||||
flavor, err := region.CreateFlavor(name, cpu, memoryMb, diskGB)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return flavor.ID, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateFlavor(name string, cpu int, memoryMb int, diskGB int) (*SFlavor, error) {
|
||||
params := map[string]map[string]interface{}{
|
||||
"flavor": {
|
||||
"name": name,
|
||||
"ram": memoryMb,
|
||||
"vcpus": cpu,
|
||||
"disk": diskGB,
|
||||
},
|
||||
}
|
||||
_, resp, err := region.Post("compute", "/flavors", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flavor := &SFlavor{}
|
||||
return flavor, resp.Unmarshal(flavor, "flavor")
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteFlavor(flavorId string) error {
|
||||
_, err := region.Delete("compute", "/flavors/"+flavorId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) Refresh() error {
|
||||
new, err := flavor.region.GetFlavor(flavor.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(flavor, new)
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetName() string {
|
||||
if len(flavor.OriginalName) > 0 {
|
||||
return flavor.OriginalName
|
||||
}
|
||||
return flavor.Name
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetStatus() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetId() string {
|
||||
return flavor.ID
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetGlobalId() string {
|
||||
return flavor.ID
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetInstanceTypeFamily() string {
|
||||
return flavor.GetName()
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetInstanceTypeCategory() string {
|
||||
return flavor.GetName()
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetPrepaidStatus() string {
|
||||
return models.SkuStatusSoldout
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetPostpaidStatus() string {
|
||||
return models.SkuStatusAvailable
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetCpuCoreCount() int {
|
||||
return int(flavor.Vcpus)
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetMemorySizeMB() int {
|
||||
return flavor.RAM
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetOsName() string {
|
||||
return "Any"
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetSysDiskResizable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetSysDiskType() string {
|
||||
return "iscsi"
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetSysDiskMinSizeGB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetSysDiskMaxSizeGB() int {
|
||||
return flavor.Disk
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetAttachedDiskType() string {
|
||||
return "iscsi"
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetAttachedDiskSizeGB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetAttachedDiskCount() int {
|
||||
return 6
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetDataDiskTypes() string {
|
||||
return "iscsi"
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetDataDiskMaxCount() int {
|
||||
return 6
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetNicType() string {
|
||||
return "vpc"
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetNicMaxCount() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetGpuAttachable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetGpuSpec() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetGpuCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (flavor *SFlavor) GetGpuMaxCount() int {
|
||||
return 0
|
||||
}
|
||||
+129
-1
@@ -1,9 +1,16 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/version"
|
||||
)
|
||||
|
||||
const (
|
||||
VOLUME_TYPES_API_VERSION = "2.67"
|
||||
)
|
||||
|
||||
type CpuInfo struct {
|
||||
@@ -116,7 +123,128 @@ func (host *SHost) GetIVMById(gid string) (cloudprovider.ICloudVM, error) {
|
||||
}
|
||||
|
||||
func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
network, err := host.zone.region.GetNetwork(desc.ExternalNetworkId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secgroups := []map[string]string{}
|
||||
|
||||
for _, secgroupId := range desc.ExternalSecgroupIds {
|
||||
secgroups = append(secgroups, map[string]string{"name": secgroupId})
|
||||
}
|
||||
|
||||
image, err := host.zone.region.GetImage(desc.ExternalImageId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storage, err := host.zone.getStorageByCategory(desc.SysDisk.StorageType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sysDiskSizeGB := image.Size / 1024 / 1024
|
||||
if desc.SysDisk.SizeGB < sysDiskSizeGB {
|
||||
desc.SysDisk.SizeGB = sysDiskSizeGB
|
||||
}
|
||||
|
||||
_, maxVersion, _ := host.zone.region.GetVersion("compute")
|
||||
|
||||
BlockDeviceMappingV2 := []map[string]interface{}{
|
||||
{
|
||||
"boot_index": 0,
|
||||
"uuid": desc.ExternalImageId,
|
||||
"source_type": "image",
|
||||
"destination_type": "volume",
|
||||
"volume_size": desc.SysDisk.SizeGB,
|
||||
"delete_on_termination": true,
|
||||
},
|
||||
}
|
||||
|
||||
if version.GE(maxVersion, VOLUME_TYPES_API_VERSION) {
|
||||
BlockDeviceMappingV2[0]["volume_type"] = storage.Name
|
||||
}
|
||||
|
||||
var _disk *SDisk
|
||||
for _, disk := range desc.DataDisks {
|
||||
storage, err = host.zone.getStorageByCategory(disk.StorageType)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
_disk, err = host.zone.region.CreateDisk(host.zone.ZoneName, storage.Name, "", disk.SizeGB, disk.Name)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
mapping := map[string]interface{}{
|
||||
"source_type": "volume",
|
||||
"destination_type": "volume",
|
||||
"delete_on_termination": true,
|
||||
"uuid": _disk.ID,
|
||||
}
|
||||
|
||||
BlockDeviceMappingV2 = append(BlockDeviceMappingV2, mapping)
|
||||
}
|
||||
if err != nil {
|
||||
for _, blockMap := range BlockDeviceMappingV2 {
|
||||
if blockMap["source_type"] == "volume" {
|
||||
if uuid, ok := blockMap["uuid"].(string); ok {
|
||||
host.zone.region.DeleteDisk(uuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := map[string]map[string]interface{}{
|
||||
"server": {
|
||||
"name": desc.Name,
|
||||
"adminPass": desc.Password,
|
||||
//"description": desc.Description,
|
||||
"accessIPv4": desc.IpAddr,
|
||||
"availability_zone": fmt.Sprintf("%s:%s", host.zone.ZoneName, host.GetName()),
|
||||
"networks": []map[string]string{
|
||||
{
|
||||
"uuid": network.NetworkID,
|
||||
"fixed_ip": desc.IpAddr,
|
||||
},
|
||||
},
|
||||
"security_groups": secgroups,
|
||||
"user_data": desc.UserData,
|
||||
"imageRef": desc.ExternalImageId,
|
||||
"block_device_mapping_v2": BlockDeviceMappingV2,
|
||||
},
|
||||
}
|
||||
|
||||
flavorId, err := host.zone.region.syncFlavor(desc.InstanceType, desc.Cpu, desc.MemoryMB, desc.SysDisk.SizeGB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params["server"]["flavorRef"] = flavorId
|
||||
|
||||
if len(desc.PublicKey) > 0 {
|
||||
keypairName, err := host.zone.region.syncKeypair(desc.Name, desc.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params["server"]["key_name"] = keypairName
|
||||
}
|
||||
|
||||
_, resp, err := host.zone.region.Post("compute", "/servers", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverId, err := resp.GetString("server", "id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance, err := host.zone.region.GetInstance(serverId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance.host = host
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (host *SHost) GetEnabled() bool {
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
QUEUED = "queued" // The Image service reserved an image ID for the image in the catalog but did not yet upload any image data.
|
||||
SAVING = "saving" // The Image service is in the process of saving the raw data for the image into the backing store.
|
||||
ACTIVE = "active" // The image is active and ready for consumption in the Image service.
|
||||
KILLED = "killed" // An image data upload error occurred.
|
||||
DELETED = "deleted" // The Image service retains information about the image but the image is no longer available for use.
|
||||
PENDING_DELETE = "pending_delete" // Similar to the deleted status. An image in this state is not recoverable.
|
||||
DEACTIVATED = "deactivated" // The image data is not available for use.
|
||||
UPLOADING = "uploading" // Data has been staged as part of the interoperable image import process. It is not yet available for use. (Since Image API 2.6)
|
||||
IMPORTING = "importing" // The image data is being processed as part of the interoperable image import process, but is not yet available for use. (Since Image API 2.6)
|
||||
)
|
||||
|
||||
type SImage struct {
|
||||
storageCache *SStoragecache
|
||||
|
||||
Status string
|
||||
Name string
|
||||
Tags []string
|
||||
ContainerFormat string
|
||||
CreatedAt time.Time
|
||||
DiskFormat string
|
||||
UpdatedAt time.Time
|
||||
Visibility string
|
||||
Self string
|
||||
MinDisk int
|
||||
Protected bool
|
||||
ID string
|
||||
File string
|
||||
Checksum string
|
||||
OsHashAlgo string
|
||||
OsHashValue string
|
||||
OsHidden bool
|
||||
Owner string
|
||||
Size int
|
||||
MinRAM int
|
||||
Schema string
|
||||
VirtualSize int
|
||||
visibility string
|
||||
}
|
||||
|
||||
func (region *SRegion) GetImages(name string, status string, imageIds []string) ([]SImage, error) {
|
||||
params := url.Values{}
|
||||
if utils.IsInStringArray(status, []string{QUEUED, SAVING, ACTIVE, KILLED, DELETED, PENDING_DELETE, DEACTIVATED, UPLOADING, IMPORTING}) {
|
||||
params.Add("status", status)
|
||||
}
|
||||
if len(name) > 0 {
|
||||
params.Add("name", name)
|
||||
}
|
||||
if len(imageIds) > 0 {
|
||||
params.Add("id", "in:"+strings.Join(imageIds, ","))
|
||||
}
|
||||
_, resp, err := region.List("image", "/v2/images?"+params.Encode(), "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
images := []SImage{}
|
||||
return images, resp.Unmarshal(&images, "images")
|
||||
}
|
||||
|
||||
func (image *SImage) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (image *SImage) GetId() string {
|
||||
return image.ID
|
||||
}
|
||||
|
||||
func (image *SImage) GetName() string {
|
||||
return image.Name
|
||||
}
|
||||
|
||||
func (image *SImage) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (image *SImage) GetGlobalId() string {
|
||||
return image.ID
|
||||
}
|
||||
|
||||
func (image *SImage) Delete(ctx context.Context) error {
|
||||
return image.storageCache.region.DeleteImage(image.ID)
|
||||
}
|
||||
|
||||
func (image *SImage) GetStatus() string {
|
||||
switch image.Status {
|
||||
case QUEUED, SAVING, UPLOADING, IMPORTING:
|
||||
return models.CACHED_IMAGE_STATUS_CACHING
|
||||
case ACTIVE:
|
||||
return models.CACHED_IMAGE_STATUS_READY
|
||||
case DELETED, DEACTIVATED, PENDING_DELETE, KILLED:
|
||||
return models.CACHED_IMAGE_STATUS_CACHE_FAILED
|
||||
default:
|
||||
return models.CACHED_IMAGE_STATUS_CACHE_FAILED
|
||||
}
|
||||
}
|
||||
|
||||
func (image *SImage) GetImageStatus() string {
|
||||
switch image.Status {
|
||||
case QUEUED, SAVING, UPLOADING, IMPORTING:
|
||||
return cloudprovider.IMAGE_STATUS_SAVING
|
||||
case ACTIVE:
|
||||
return cloudprovider.IMAGE_STATUS_ACTIVE
|
||||
case DELETED, DEACTIVATED, PENDING_DELETE:
|
||||
return cloudprovider.IMAGE_STATUS_DELETED
|
||||
case KILLED:
|
||||
return cloudprovider.IMAGE_STATUS_KILLED
|
||||
default:
|
||||
return cloudprovider.IMAGE_STATUS_DELETED
|
||||
}
|
||||
}
|
||||
|
||||
func (image *SImage) Refresh() error {
|
||||
new, err := image.storageCache.region.GetImage(image.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(image, new)
|
||||
}
|
||||
|
||||
func (image *SImage) GetImageType() string {
|
||||
switch image.Visibility {
|
||||
case "public":
|
||||
return cloudprovider.CachedImageTypeSystem
|
||||
default:
|
||||
return cloudprovider.CachedImageTypeCustomized
|
||||
}
|
||||
}
|
||||
|
||||
func (image *SImage) GetSize() int64 {
|
||||
return int64(image.Size)
|
||||
}
|
||||
|
||||
func (image *SImage) GetOsType() string {
|
||||
return "Linux"
|
||||
}
|
||||
|
||||
func (image *SImage) GetOsDist() string {
|
||||
return "Linux"
|
||||
}
|
||||
|
||||
func (image *SImage) GetOsVersion() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (image *SImage) GetOsArch() string {
|
||||
return "x86_64"
|
||||
}
|
||||
|
||||
func (image *SImage) GetMinOsDiskSizeGb() int {
|
||||
return 50
|
||||
}
|
||||
|
||||
func (image *SImage) GetImageFormat() string {
|
||||
return image.DiskFormat
|
||||
}
|
||||
|
||||
func (image *SImage) GetCreateTime() time.Time {
|
||||
return image.CreatedAt
|
||||
}
|
||||
|
||||
func (region *SRegion) GetImage(imageId string) (*SImage, error) {
|
||||
images, err := region.GetImages("", "", []string{imageId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
if len(images) > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
return &images[0], nil
|
||||
}
|
||||
|
||||
func (image *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return image.storageCache
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteImage(imageId string) error {
|
||||
_, err := region.Delete("image", "/v2/images/"+imageId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) GetImageStatus(imageId string) (string, error) {
|
||||
image, err := region.GetImage(imageId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return image.Status, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetImageByName(name string) (*SImage, error) {
|
||||
images, err := region.GetImages(name, "", []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &images[0], nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateImage(imageName string) (*SImage, error) {
|
||||
params := map[string]string{
|
||||
"container_format": "bare",
|
||||
"disk_format": "vmdk",
|
||||
"name": imageName,
|
||||
}
|
||||
|
||||
_, resp, err := region.Post("image", "/v2/images", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image := &SImage{}
|
||||
return image, resp.Unmarshal(image)
|
||||
}
|
||||
+170
-43
@@ -10,6 +10,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -52,17 +53,6 @@ type ExtraSpecs struct {
|
||||
MemPageSize int `json:"hw:mem_page_size,omitempty"`
|
||||
}
|
||||
|
||||
type SFlavor struct {
|
||||
ID string
|
||||
Disk int
|
||||
Ephemeral int
|
||||
ExtraSpecs ExtraSpecs
|
||||
OriginalName string
|
||||
RAM int
|
||||
Swap string
|
||||
Vcpus int8
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
ID string
|
||||
Links []Link
|
||||
@@ -137,7 +127,7 @@ func (region *SRegion) GetSecurityGroupsByInstance(instanceId string) ([]Securit
|
||||
|
||||
func (region *SRegion) GetInstances(zoneName string, hostName string) ([]SInstance, error) {
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
_, resp, err := region.Get("compute", "/servers/detail", maxVersion, nil)
|
||||
_, resp, err := region.List("compute", "/servers/detail", maxVersion, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -212,19 +202,18 @@ func (instance *SInstance) IsEmulated() bool {
|
||||
|
||||
func (instance *SInstance) fetchFlavor() error {
|
||||
if len(instance.Flavor.ID) > 0 && instance.Flavor.Vcpus == 0 {
|
||||
_, resp, err := instance.host.zone.region.Get("compute", "/flavors/"+instance.Flavor.ID, "", nil)
|
||||
flavor, err := instance.host.zone.region.GetFlavor(instance.Flavor.ID)
|
||||
if err != nil {
|
||||
log.Errorf("fetch instance %s flavor error: %v", instance.Name, err)
|
||||
return err
|
||||
}
|
||||
return resp.Unmarshal(&instance.Flavor, "flavor")
|
||||
instance.Flavor = *flavor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetInstanceType() string {
|
||||
instance.fetchFlavor()
|
||||
return instance.Flavor.OriginalName
|
||||
return instance.Flavor.GetName()
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
@@ -329,7 +318,16 @@ func (instance *SInstance) Refresh() error {
|
||||
}
|
||||
|
||||
func (instance *SInstance) UpdateVM(ctx context.Context, name string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if instance.Name != name {
|
||||
params := map[string]map[string]string{
|
||||
"server": {
|
||||
"name": name,
|
||||
},
|
||||
}
|
||||
_, _, err := instance.host.zone.region.Update("compute", "/servers/"+instance.ID, "", jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetHypervisor() string {
|
||||
@@ -337,11 +335,17 @@ func (instance *SInstance) GetHypervisor() string {
|
||||
}
|
||||
|
||||
func (instance *SInstance) StartVM(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if err := instance.host.zone.region.StartVM(instance.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(instance, models.VM_RUNNING, 10*time.Second, 8*time.Minute)
|
||||
}
|
||||
|
||||
func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if err := instance.host.zone.region.StopVM(instance.ID, isForce); err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(instance, models.VM_RUNNING, 10*time.Second, 8*time.Minute)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) {
|
||||
@@ -372,19 +376,45 @@ func (instance *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
|
||||
}
|
||||
|
||||
func (instance *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.DeployVM(instance.ID, name, password, publicKey, deleteKeypair, description)
|
||||
}
|
||||
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
return "", instance.host.zone.region.ReplaceSystemDisk(instance.ID, imageId, passwd, publicKey, sysSizeGB)
|
||||
|
||||
}
|
||||
|
||||
func (instance *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if instance.GetVcpuCount() != int8(ncpu) || instance.GetVmemSizeMB() != vmem {
|
||||
flavorId, err := instance.host.zone.region.syncFlavor("", ncpu, vmem, 40)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.host.zone.region.ChangeConfig(instance, flavorId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if instance.GetInstanceType() != instanceType {
|
||||
flavorId, err := instance.host.zone.region.syncFlavor(instanceType, 0, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return instance.host.zone.region.ChangeConfig(instance, flavorId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) ChangeConfig(instance *SInstance, flavorId string) error {
|
||||
params := map[string]map[string]string{
|
||||
"resize": {
|
||||
"flavorRef": flavorId,
|
||||
},
|
||||
}
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
_, _, err := region.Post("compute", fmt.Sprintf("/servers/%s/action", instance.ID), maxVersion, jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
||||
@@ -401,40 +431,86 @@ func (region *SRegion) CreateInstance(name string, imageId string, instanceType
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) doStartVM(instanceId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) instanceOperation(instanceId, operate string) error {
|
||||
params := jsonutils.Marshal(map[string]string{operate: ""})
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
_, _, err := region.Post("compute", fmt.Sprintf("/servers/%s/action", instanceId), maxVersion, params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) doStopVM(instanceId string, isForce bool) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return region.instanceOperation(instanceId, "os-stop")
|
||||
}
|
||||
|
||||
func (region *SRegion) doDeleteVM(instanceId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return region.instanceOperation(instanceId, "forceDelete")
|
||||
}
|
||||
|
||||
func (region *SRegion) StartVM(instanceId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return region.instanceOperation(instanceId, "os-start")
|
||||
}
|
||||
|
||||
func (region *SRegion) StopVM(instanceId string, isForce bool) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return region.doStopVM(instanceId, isForce)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteVM(instanceId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
instance, err := region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
return nil
|
||||
}
|
||||
log.Errorf("failed to get instance %s %v", instanceId, err)
|
||||
return err
|
||||
}
|
||||
status := instance.GetStatus()
|
||||
log.Debugf("Instance status on delete is %s", status)
|
||||
if status != models.VM_READY {
|
||||
log.Warningf("DeleteVM: vm status is %s expect %s", status, models.VM_READY)
|
||||
}
|
||||
return region.doDeleteVM(instanceId)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeployVM(instanceId string, name string, password string, keypairName string, deleteKeypair bool, description string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if len(password) > 0 {
|
||||
params := map[string]map[string]string{
|
||||
"changePassword": {
|
||||
"adminPass": password,
|
||||
},
|
||||
}
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
_, _, err := region.Post("compute", fmt.Sprintf("/servers/%s/action", instanceId), maxVersion, jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) DeleteVM(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.DeleteVM(instance.ID)
|
||||
}
|
||||
|
||||
func (region *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, keypairName string, sysDiskSizeGB int) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, publicKey string, sysDiskSizeGB int) error {
|
||||
params := map[string]map[string]string{
|
||||
"rebuild": {
|
||||
"imageRef": imageId,
|
||||
},
|
||||
}
|
||||
|
||||
if len(publicKey) > 0 {
|
||||
keypairName, err := region.syncKeypair(instanceId, publicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params["rebuild"]["key_name"] = keypairName
|
||||
}
|
||||
|
||||
if len(passwd) > 0 {
|
||||
params["rebuild"]["adminPass"] = passwd
|
||||
}
|
||||
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
_, _, err := region.Post("compute", fmt.Sprintf("/servers/%s/action", instanceId), maxVersion, jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error {
|
||||
@@ -446,27 +522,78 @@ func (region *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanc
|
||||
}
|
||||
|
||||
func (region *SRegion) DetachDisk(instanceId string, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
_, err := region.Delete("compute", fmt.Sprintf("/servers/%s/os-volume_attachments/%s", instanceId, diskId), "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) AttachDisk(instanceId string, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
params := map[string]map[string]string{
|
||||
"volumeAttachment": {
|
||||
"volumeId": diskId,
|
||||
},
|
||||
}
|
||||
_, _, err := region.Post("compute", fmt.Sprintf("/servers/%s/os-volume_attachments", instanceId), "", jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (instance *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
secgroup, err := instance.host.zone.region.GetSecurityGroup(secgroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := map[string]map[string]string{
|
||||
"addSecurityGroup": {
|
||||
"name": secgroup.Name,
|
||||
},
|
||||
}
|
||||
_, _, err = instance.host.zone.region.Post("compute", fmt.Sprintf("/servers/%s/action", instance.ID), "", jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (instance *SInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (instance *SInstance) RevokeSecurityGroup(secgroupId string) error {
|
||||
secgroup, err := instance.host.zone.region.GetSecurityGroup(secgroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := map[string]map[string]string{
|
||||
"removeSecurityGroup": {
|
||||
"name": secgroup.Name,
|
||||
},
|
||||
}
|
||||
_, _, err = instance.host.zone.region.Post("compute", fmt.Sprintf("/servers/%s/action", instance.ID), "", jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (instance *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
secgroups, err := instance.host.zone.region.GetSecurityGroupsByInstance(instance.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
originIds := []string{}
|
||||
for _, secgroup := range secgroups {
|
||||
if !utils.IsInStringArray(secgroup.ID, secgroupIds) {
|
||||
if err := instance.RevokeSecurityGroup(secgroup.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
originIds = append(originIds, secgroup.ID)
|
||||
}
|
||||
for _, secgroupId := range secgroupIds {
|
||||
if !utils.IsInStringArray(secgroupId, originIds) {
|
||||
if err := instance.AssignSecurityGroup(secgroupId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetBillingType() string {
|
||||
return models.BILLING_TYPE_PREPAID
|
||||
return models.BILLING_TYPE_POSTPAID
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetExpiredAt() time.Time {
|
||||
@@ -482,9 +609,9 @@ func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid stri
|
||||
}
|
||||
|
||||
func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (region *SRegion) RenewInstances(instanceId []string, bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/util/version"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SKeypair struct {
|
||||
Fingerprint string
|
||||
Name string
|
||||
Type string
|
||||
PublicKey string
|
||||
}
|
||||
|
||||
type SKeyPair struct {
|
||||
Keypair SKeypair
|
||||
}
|
||||
|
||||
func (region *SRegion) GetKeypairs() ([]SKeyPair, error) {
|
||||
_, resp, err := region.List("compute", "/os-keypairs", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keypairs := []SKeyPair{}
|
||||
return keypairs, resp.Unmarshal(&keypairs, "keypairs")
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateKeypair(name, publicKey, Type string) (*SKeyPair, error) {
|
||||
if len(Type) > 0 && !utils.IsInStringArray(Type, []string{"ssh", "x509"}) {
|
||||
return nil, fmt.Errorf("only support ssh or x509 type")
|
||||
}
|
||||
params := map[string]map[string]string{
|
||||
"keypair": {
|
||||
"name": name,
|
||||
"public_key": publicKey,
|
||||
},
|
||||
}
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
if len(Type) > 0 && version.GE(maxVersion, "2.2") {
|
||||
params["keypair"]["type"] = Type
|
||||
}
|
||||
_, resp, err := region.Post("compute", "/os-keypairs", maxVersion, jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keypair := &SKeyPair{}
|
||||
return keypair, resp.Unmarshal(keypair)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteKeypair(name string) error {
|
||||
_, err := region.Delete("compute", "/os-keypairs/"+name, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) GetKeypair(name string) (*SKeyPair, error) {
|
||||
_, resp, err := region.Get("compute", "/os-keypairs/"+name, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keypair := &SKeyPair{}
|
||||
return keypair, resp.Unmarshal(keypair)
|
||||
}
|
||||
|
||||
func (region *SRegion) syncKeypair(namePrefix, publicKey string) (string, error) {
|
||||
keypairs, err := region.GetKeypairs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, keypair := range keypairs {
|
||||
if keypair.Keypair.PublicKey == publicKey {
|
||||
return keypair.Keypair.Name, nil
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
name := fmt.Sprintf("%s-%d", namePrefix, i)
|
||||
if _, err := region.GetKeypair(name); err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
keypair, err := region.CreateKeypair(name, publicKey, "ssh")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return keypair.Keypair.Name, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("failed to find uniq name for keypair")
|
||||
}
|
||||
@@ -73,8 +73,9 @@ func (network *SNetwork) Delete() error {
|
||||
return network.wire.zone.region.DeleteNetwork(network.ID)
|
||||
}
|
||||
|
||||
func (network *SRegion) DeleteNetwork(networkId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) DeleteNetwork(networkId string) error {
|
||||
_, err := region.Delete("network", "/v2.0/subnets/"+networkId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (network *SNetwork) GetIWire() cloudprovider.ICloudWire {
|
||||
@@ -126,7 +127,7 @@ func (region *SRegion) GetNetwork(networkId string) (*SNetwork, error) {
|
||||
}
|
||||
|
||||
func (region *SRegion) GetNetworks(vpcId string) ([]SNetwork, error) {
|
||||
_, resp, err := region.Get("network", "/v2.0/subnets", "", nil)
|
||||
_, resp, err := region.List("network", "/v2.0/subnets", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -152,6 +153,19 @@ func (network *SNetwork) Refresh() error {
|
||||
return jsonutils.Update(network, new)
|
||||
}
|
||||
|
||||
func (network *SRegion) CreateNetwork(zoneId string, vpcId string, name string, cidr string, desc string) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) CreateNetwork(vpcId string, name string, cidr string, desc string) (string, error) {
|
||||
params := map[string]map[string]interface{}{
|
||||
"subnet": {
|
||||
"name": name,
|
||||
"network_id": vpcId,
|
||||
"cidr": cidr,
|
||||
"description": desc,
|
||||
"ip_version": 4,
|
||||
},
|
||||
}
|
||||
_, resp, err := region.Post("network", "/v2.0/subnets", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.GetString("subnet", "id")
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package openstack
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -68,7 +69,37 @@ func (cli *SOpenStackClient) Request(region, service, method string, url string,
|
||||
}
|
||||
ctx := context.Background()
|
||||
session := cli.client.NewSession(ctx, region, "", "internal", cli.tokenCredential, "")
|
||||
return session.JSONRequest(service, "", httputils.THttpMethod(method), url, header, body)
|
||||
header, resp, err := session.JSONRequest(service, "", httputils.THttpMethod(method), url, header, body)
|
||||
if err != nil && body != nil {
|
||||
uri, _ := session.GetServiceURL(service, "")
|
||||
log.Errorf("microversion %s url: %s, params: %s", microversion, uri+url, body.PrettyString())
|
||||
}
|
||||
return header, resp, err
|
||||
}
|
||||
|
||||
func (cli *SOpenStackClient) RawRequest(region, service, method string, url string, microversion string, body jsonutils.JSONObject) (*http.Response, error) {
|
||||
header := http.Header{}
|
||||
if len(microversion) > 0 {
|
||||
header.Set("X-Openstack-Nova-API-Version", microversion)
|
||||
}
|
||||
ctx := context.Background()
|
||||
session := cli.client.NewSession(ctx, region, "", "internal", cli.tokenCredential, "")
|
||||
data := strings.NewReader("")
|
||||
if body != nil {
|
||||
data = strings.NewReader(body.String())
|
||||
}
|
||||
return session.RawRequest(service, "", httputils.THttpMethod(method), url, header, data)
|
||||
}
|
||||
|
||||
func (cli *SOpenStackClient) StreamRequest(region, service, method string, url string, microversion string, body io.Reader) (*http.Response, error) {
|
||||
header := http.Header{}
|
||||
if len(microversion) > 0 {
|
||||
header.Set("X-Openstack-Nova-API-Version", microversion)
|
||||
}
|
||||
header.Set("Content-Type", "application/octet-stream")
|
||||
ctx := context.Background()
|
||||
session := cli.client.NewSession(ctx, region, "", "internal", cli.tokenCredential, "")
|
||||
return session.RawRequest(service, "", httputils.THttpMethod(method), url, header, body)
|
||||
}
|
||||
|
||||
func (cli *SOpenStackClient) getVersion(region string, service string) (string, string, error) {
|
||||
|
||||
@@ -60,7 +60,7 @@ func (region *SRegion) GetPorts(macAddress string) ([]SPort, error) {
|
||||
params.Set("mac_address", macAddress)
|
||||
}
|
||||
url := fmt.Sprintf("%s?%s", base, params.Encode())
|
||||
_, resp, err := region.Get("network", url, "", nil)
|
||||
_, resp, err := region.List("network", url, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ func (self *SOpenStackProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SOpenStackProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SOpenStackProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
projectName, _ := data.GetString("project_name")
|
||||
if len(projectName) == 0 {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/util/version"
|
||||
)
|
||||
|
||||
type SQuota struct {
|
||||
FixedIps int
|
||||
Floatingips int
|
||||
Networks int
|
||||
Port int
|
||||
RbacPolicy int
|
||||
Router int
|
||||
SecurityGroups int
|
||||
SecurityGroupRules int
|
||||
}
|
||||
|
||||
func (region *SRegion) GetQuota() (*SQuota, error) {
|
||||
_, resp, err := region.Get("compute", "/os-quota-sets/"+region.client.tokenCredential.GetTenantId(), "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quota := &SQuota{}
|
||||
return quota, resp.Unmarshal(quota, "quota_set")
|
||||
}
|
||||
|
||||
func (region *SRegion) SetQuota(quota *SQuota) error {
|
||||
_, maxVersion, _ := region.GetVersion("compute")
|
||||
params := map[string]map[string]interface{}{
|
||||
"quota_set": {
|
||||
"force": "True",
|
||||
},
|
||||
}
|
||||
|
||||
if version.GE(maxVersion, "2.35") {
|
||||
if quota.Floatingips > 0 {
|
||||
params["quota_set"]["floating_ips"] = quota.Floatingips
|
||||
}
|
||||
|
||||
if quota.SecurityGroups > 0 {
|
||||
params["quota_set"]["security_group"] = quota.SecurityGroups
|
||||
}
|
||||
|
||||
if quota.SecurityGroupRules > 0 {
|
||||
params["quota_set"]["security_group_rules"] = quota.SecurityGroupRules
|
||||
}
|
||||
|
||||
if quota.FixedIps > 0 {
|
||||
params["quota_set"]["fixed_ips"] = quota.FixedIps
|
||||
}
|
||||
|
||||
if quota.Networks > 0 {
|
||||
params["quota_set"]["networks"] = quota.Networks
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_, _, err := region.Update("compute", "/os-quota-sets/"+region.client.tokenCredential.GetTenantId(), maxVersion, jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package openstack
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
type SRegion struct {
|
||||
@@ -63,7 +65,25 @@ func (region *SRegion) Refresh() error {
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
params := map[string]map[string]string{
|
||||
"network": {
|
||||
"name": name,
|
||||
"description": desc,
|
||||
},
|
||||
}
|
||||
_, resp, err := region.Post("network", "/v2.0/networks", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = region.fetchInfrastructure()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vpcId, err := resp.GetString("network", "id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return region.GetIVpcById(vpcId)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
@@ -173,7 +193,7 @@ func (region *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error)
|
||||
}
|
||||
|
||||
func (region *SRegion) fetchZones() error {
|
||||
_, resp, err := region.Get("compute", "/os-availability-zone", "", jsonutils.NewDict())
|
||||
_, resp, err := region.List("compute", "/os-availability-zone", "", jsonutils.NewDict())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -227,24 +247,107 @@ func (region *SRegion) fetchInfrastructure() error {
|
||||
}
|
||||
|
||||
func (region *SRegion) Get(service, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
return region.client.Request(region.Name, service, "GET", url, microversion, body)
|
||||
if strings.HasSuffix(url, "/") {
|
||||
return nil, nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
header, resp, err := region.client.Request(region.Name, service, "GET", url, microversion, body)
|
||||
if err != nil {
|
||||
if jsonErr, ok := err.(*httputils.JSONClientError); ok {
|
||||
if jsonErr.Code == 404 || strings.HasSuffix(jsonErr.Class, "NotFound") {
|
||||
return nil, nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return header, resp, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) List(service, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
header, resp, err := region.client.Request(region.Name, service, "GET", url, microversion, body)
|
||||
if err != nil {
|
||||
if jsonErr, ok := err.(*httputils.JSONClientError); ok {
|
||||
if jsonErr.Code == 404 || strings.HasSuffix(jsonErr.Class, "NotFound") {
|
||||
return nil, nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return header, resp, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) Post(service, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
return region.client.Request(region.Name, service, "POST", url, microversion, body)
|
||||
}
|
||||
|
||||
func (region *SRegion) CinderGet(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
func (region *SRegion) Update(service, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
return region.client.Request(region.Name, service, "PUT", url, microversion, body)
|
||||
}
|
||||
|
||||
func (region *SRegion) Delete(service, url string, microversion string) (*http.Response, error) {
|
||||
return region.client.RawRequest(region.Name, service, "DELETE", url, microversion, nil)
|
||||
}
|
||||
|
||||
func (region *SRegion) CinderList(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
for _, service := range []string{"volumev3", "volumev2", "volume"} {
|
||||
header, resp, err := region.Get(service, url, microversion, body)
|
||||
if err == nil {
|
||||
return header, resp, nil
|
||||
}
|
||||
log.Debugf("failed to list %s by service %s error: %v, try another", url, service, err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to get %s by cinder service", url)
|
||||
}
|
||||
|
||||
func (region *SRegion) CinderGet(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
if strings.HasSuffix(url, "/") {
|
||||
return nil, nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
for _, service := range []string{"volumev3", "volumev2", "volume"} {
|
||||
header, resp, err := region.Get(service, url, microversion, body)
|
||||
if err == nil || err == cloudprovider.ErrNotFound {
|
||||
return header, resp, err
|
||||
}
|
||||
log.Debugf("failed to get %s by service %s error: %v, try another", url, service, err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to get %s by cinder service", url)
|
||||
}
|
||||
|
||||
func (region *SRegion) CinderCreate(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
for _, service := range []string{"volumev3", "volumev2", "volume"} {
|
||||
header, resp, err := region.Post(service, url, microversion, body)
|
||||
if err == nil {
|
||||
return header, resp, nil
|
||||
}
|
||||
log.Debugf("failed to create %s by service %s error: %v, try another", url, service, err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to create %s by cinder service", url)
|
||||
}
|
||||
|
||||
func (region *SRegion) CinderDelete(url string, microversion string) (*http.Response, error) {
|
||||
if strings.HasSuffix(url, "/") {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
for _, service := range []string{"volumev3", "volumev2", "volume"} {
|
||||
resp, err := region.Delete(service, url, microversion)
|
||||
if err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
log.Debugf("failed to delete %s by service %s error: %v, try another", url, service, err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to delete %s by cinder service", url)
|
||||
}
|
||||
|
||||
func (region *SRegion) CinderAction(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
for _, service := range []string{"volumev3", "volumev2", "volume"} {
|
||||
header, resp, err := region.Post(service, url, microversion, body)
|
||||
if err == nil {
|
||||
return header, resp, nil
|
||||
}
|
||||
log.Debugf("failed to operate %s by service %s error: %v, try another", url, service, err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to operate %s by cinder service", url)
|
||||
}
|
||||
|
||||
func (region *SRegion) ProjectId() string {
|
||||
return region.client.tokenCredential.GetProjectId()
|
||||
}
|
||||
@@ -270,7 +373,7 @@ func (region *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) {
|
||||
@@ -316,3 +419,16 @@ func (region *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbala
|
||||
func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
|
||||
flavors, err := region.GetFlavors()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iskus := make([]cloudprovider.ICloudSku, len(flavors))
|
||||
for i := 0; i < len(flavors); i++ {
|
||||
flavors[i].region = region
|
||||
iskus[i] = &flavors[i]
|
||||
}
|
||||
return iskus, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
"yunion.io/x/pkg/utils"
|
||||
@@ -45,6 +48,20 @@ type SSecurityGroup struct {
|
||||
TenantID string
|
||||
}
|
||||
|
||||
type SecurigyGroupRuleSet []SSecurityGroupRule
|
||||
|
||||
func (v SecurigyGroupRuleSet) Len() int {
|
||||
return len(v)
|
||||
}
|
||||
|
||||
func (v SecurigyGroupRuleSet) Swap(i, j int) {
|
||||
v[i], v[j] = v[j], v[i]
|
||||
}
|
||||
|
||||
func (v SecurigyGroupRuleSet) Less(i, j int) bool {
|
||||
return strings.Compare(v[i].String(), v[j].String()) <= 0
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSecurityGroup(secgroupId string) (*SSecurityGroup, error) {
|
||||
_, resp, err := region.Get("network", "/v2.0/security-groups/"+secgroupId, "", nil)
|
||||
if err != nil {
|
||||
@@ -55,7 +72,7 @@ func (region *SRegion) GetSecurityGroup(secgroupId string) (*SSecurityGroup, err
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSecurityGroups() ([]SSecurityGroup, error) {
|
||||
_, resp, err := region.Get("network", "/v2.0/security-groups", "", nil)
|
||||
_, resp, err := region.List("network", "/v2.0/security-groups", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -172,14 +189,165 @@ func (secgroup *SSecurityGroup) Refresh() error {
|
||||
return jsonutils.Update(secgroup, new)
|
||||
}
|
||||
|
||||
func (secgroup *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) {
|
||||
if len(secgroupId) > 0 {
|
||||
_, err := region.GetSecurityGroup(secgroupId)
|
||||
if err != nil {
|
||||
if err != cloudprovider.ErrNotFound {
|
||||
return "", err
|
||||
}
|
||||
secgroupId = ""
|
||||
}
|
||||
}
|
||||
if len(secgroupId) == 0 {
|
||||
secgroups, err := region.GetSecurityGroups()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
secgroupNames := []string{}
|
||||
for _, secgroup := range secgroups {
|
||||
secgroupNames = append(secgroupNames, strings.ToLower(secgroup.Name))
|
||||
}
|
||||
|
||||
uniqName := strings.ToLower(name)
|
||||
if utils.IsInStringArray(uniqName, secgroupNames) {
|
||||
for i := 0; i < 20; i++ {
|
||||
uniqName = fmt.Sprintf("%s-%d", strings.ToLower(name), i)
|
||||
if !utils.IsInStringArray(uniqName, secgroupNames) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Errorf("create secgroup %s", uniqName)
|
||||
secgroup, err := region.CreateSecurityGroup(uniqName, desc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
secgroupId = secgroup.ID
|
||||
}
|
||||
return region.syncSecgroupRules(secgroupId, rules)
|
||||
}
|
||||
|
||||
func (region *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.SecurityRule) (string, error) {
|
||||
secgroup, err := region.GetSecurityGroup(secgroupId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sort.Sort(secrules.SecurityRuleSet(rules))
|
||||
sort.Sort(SecurigyGroupRuleSet(secgroup.SecurityGroupRules))
|
||||
|
||||
delSecgroupRuleIds := []string{}
|
||||
addSecgroupRules := []secrules.SecurityRule{}
|
||||
addSecgroupRuleStrings := []string{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(rules) || j < len(secgroup.SecurityGroupRules) {
|
||||
if i < len(rules) && j < len(secgroup.SecurityGroupRules) {
|
||||
secruleStr := secgroup.SecurityGroupRules[j].String()
|
||||
ruleStr := rules[i].String()
|
||||
cmp := strings.Compare(secruleStr, ruleStr)
|
||||
if cmp == 0 {
|
||||
i++
|
||||
j++
|
||||
} else if cmp > 0 {
|
||||
delSecgroupRuleIds = append(delSecgroupRuleIds, secgroup.SecurityGroupRules[j].ID)
|
||||
j++
|
||||
} else {
|
||||
if !utils.IsInStringArray(ruleStr, addSecgroupRuleStrings) {
|
||||
addSecgroupRules = append(addSecgroupRules, rules[i])
|
||||
addSecgroupRuleStrings = append(addSecgroupRuleStrings, ruleStr)
|
||||
}
|
||||
i++
|
||||
}
|
||||
} else if i >= len(rules) {
|
||||
delSecgroupRuleIds = append(delSecgroupRuleIds, secgroup.SecurityGroupRules[j].ID)
|
||||
j++
|
||||
} else if j >= len(secgroup.SecurityGroupRules) {
|
||||
ruleStr := rules[i].String()
|
||||
if !utils.IsInStringArray(ruleStr, addSecgroupRuleStrings) {
|
||||
addSecgroupRules = append(addSecgroupRules, rules[i])
|
||||
addSecgroupRuleStrings = append(addSecgroupRuleStrings, ruleStr)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
for _, ruleId := range delSecgroupRuleIds {
|
||||
if err := region.delSecurityGroupRule(ruleId); err != nil {
|
||||
log.Errorf("delSecurityGroupRule error %v", err)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(addSecgroupRules); i++ {
|
||||
if err := region.addSecurityGroupRules(secgroupId, &addSecgroupRules[i]); err != nil {
|
||||
log.Errorf("addSecurityGroupRule error %v", rules[i])
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return secgroupId, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) delSecurityGroupRule(ruleId string) error {
|
||||
_, err := region.Delete("network", "/v2.0/security-group-rules/"+ruleId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) addSecurityGroupRules(secgroupId string, rule *secrules.SecurityRule) error {
|
||||
direction := "ingress"
|
||||
if rule.Direction == secrules.SecurityRuleEgress {
|
||||
direction = "egress"
|
||||
}
|
||||
|
||||
if rule.Protocol == secrules.PROTO_ANY {
|
||||
rule.Protocol = "0"
|
||||
}
|
||||
|
||||
params := map[string]map[string]interface{}{
|
||||
"security_group_rule": {
|
||||
"direction": direction,
|
||||
"protocol": rule.Protocol,
|
||||
"security_group_id": secgroupId,
|
||||
"remote_ip_prefix": rule.IPNet.String(),
|
||||
},
|
||||
}
|
||||
if len(rule.Ports) > 0 {
|
||||
for _, port := range rule.Ports {
|
||||
params["security_group_rule"]["port_range_max"] = port
|
||||
params["security_group_rule"]["port_range_min"] = port
|
||||
_, _, err := region.Post("network", "/v2.0/security-group-rules", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if rule.PortEnd > 0 && rule.PortStart > 0 {
|
||||
params["security_group_rule"]["port_range_min"] = rule.PortStart
|
||||
params["security_group_rule"]["port_range_max"] = rule.PortEnd
|
||||
}
|
||||
_, _, err := region.Post("network", "/v2.0/security-group-rules", "", jsonutils.Marshal(params))
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteSecurityGroup(vpcId, secGroupId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
_, err := region.Delete("network", "/v2.0/security-groups/"+secGroupId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateSecurityGroup(name, description string) (*SSecurityGroup, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
params := map[string]map[string]interface{}{
|
||||
"security_group": {
|
||||
"name": name,
|
||||
"description": description,
|
||||
},
|
||||
}
|
||||
_, resp, err := region.Post("network", "/v2.0/security-groups", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secgroup := &SSecurityGroup{}
|
||||
return secgroup, resp.Unmarshal(secgroup, "security_group")
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskShowOptions struct {
|
||||
ID string `help:"Storage type for disk"`
|
||||
type DiskOptions struct {
|
||||
ID string `help:"ID of disk"`
|
||||
}
|
||||
|
||||
shellutils.R(&DiskShowOptions{}, "disk-show", "Show disk", func(cli *openstack.SRegion, args *DiskShowOptions) error {
|
||||
shellutils.R(&DiskOptions{}, "disk-show", "Show disk", func(cli *openstack.SRegion, args *DiskOptions) error {
|
||||
disk, err := cli.GetDisk(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -30,4 +30,43 @@ func init() {
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DiskOptions{}, "disk-delete", "Delete disk", func(cli *openstack.SRegion, args *DiskOptions) error {
|
||||
return cli.DeleteDisk(args.ID)
|
||||
})
|
||||
|
||||
type DiskCreateOptions struct {
|
||||
ZONE string `help:"Zone name"`
|
||||
CATEGORY string `help:"Disk category"`
|
||||
NAME string `help:"Disk Name"`
|
||||
SIZE int `help:"Disk Size GB"`
|
||||
Desc string `help:"Description of disk"`
|
||||
}
|
||||
shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disk", func(cli *openstack.SRegion, args *DiskCreateOptions) error {
|
||||
disk, err := cli.CreateDisk(args.ZONE, args.CATEGORY, args.NAME, args.SIZE, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskResetOptions struct {
|
||||
DISK string `help:"ID of disk"`
|
||||
SNAPSHOT string `help:"ID of snapshot"`
|
||||
}
|
||||
|
||||
shellutils.R(&DiskResetOptions{}, "disk-reset", "Reset disk", func(cli *openstack.SRegion, args *DiskResetOptions) error {
|
||||
return cli.ResetDisk(args.DISK, args.SNAPSHOT)
|
||||
})
|
||||
|
||||
type DiskResizeOptions struct {
|
||||
DISK string `help:"ID of disk"`
|
||||
SIZE int64 `help:"Disk size GB"`
|
||||
}
|
||||
|
||||
shellutils.R(&DiskResizeOptions{}, "disk-resize", "Resize disk", func(cli *openstack.SRegion, args *DiskResizeOptions) error {
|
||||
return cli.ResizeDisk(args.DISK, args.SIZE*1024)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/openstack"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type FlavorkListOptions struct {
|
||||
}
|
||||
shellutils.R(&FlavorkListOptions{}, "flavor-list", "List flavors", func(cli *openstack.SRegion, args *FlavorkListOptions) error {
|
||||
flavors, err := cli.GetFlavors()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(flavors, 0, 0, 0, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type FlavorOptions struct {
|
||||
ID string `help:"ID of flavor"`
|
||||
}
|
||||
|
||||
shellutils.R(&FlavorOptions{}, "flavor-show", "Show flavor", func(cli *openstack.SRegion, args *FlavorOptions) error {
|
||||
flavor, err := cli.GetFlavor(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(flavor)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&FlavorOptions{}, "flavor-delete", "Delete flavor", func(cli *openstack.SRegion, args *FlavorOptions) error {
|
||||
return cli.DeleteFlavor(args.ID)
|
||||
})
|
||||
|
||||
type FlavorCreateOptions struct {
|
||||
NAME string `help:"Name of flavor"`
|
||||
CPU int `help:"Core num of cpu"`
|
||||
MEMORY_MB int `help:"Memory of flavor"`
|
||||
DISK int `help:"Disk size of flavor"`
|
||||
}
|
||||
|
||||
shellutils.R(&FlavorCreateOptions{}, "flavor-create", "Create flavor", func(cli *openstack.SRegion, args *FlavorCreateOptions) error {
|
||||
flavor, err := cli.CreateFlavor(args.NAME, args.CPU, args.MEMORY_MB, args.DISK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(flavor)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/openstack"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ImageListOptions struct {
|
||||
Name string
|
||||
Ids []string
|
||||
Status string
|
||||
}
|
||||
shellutils.R(&ImageListOptions{}, "image-list", "List images", func(cli *openstack.SRegion, args *ImageListOptions) error {
|
||||
images, err := cli.GetImages(args.Name, args.Status, args.Ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(images, 0, 0, 0, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageOptions struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
shellutils.R(&ImageOptions{}, "image-show", "Show image", func(cli *openstack.SRegion, args *ImageOptions) error {
|
||||
image, err := cli.GetImages("", "", []string{args.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image[0])
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&ImageOptions{}, "image-delete", "Delete image", func(cli *openstack.SRegion, args *ImageOptions) error {
|
||||
return cli.DeleteImage(args.ID)
|
||||
})
|
||||
|
||||
type ImageCreateOptions struct {
|
||||
NAME string
|
||||
}
|
||||
|
||||
shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *openstack.SRegion, args *ImageCreateOptions) error {
|
||||
image, err := cli.CreateImage(args.NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
@@ -42,4 +42,23 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceDeployOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
Password string `help:"Instance password"`
|
||||
Name string `help:"Instance name"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDeployOptions{}, "instance-deploy", "Deploy instance", func(cli *openstack.SRegion, args *InstanceDeployOptions) error {
|
||||
return cli.DeployVM(args.ID, args.Name, args.Password, "", false, "")
|
||||
})
|
||||
|
||||
type InstanceChangeConfigOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
FLAVOR_ID string `help:"Flavor ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Change instance config", func(cli *openstack.SRegion, args *InstanceChangeConfigOptions) error {
|
||||
return cli.ChageConfig(args.ID, args.FLAVOR_ID)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/openstack"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type KeypairListOptions struct {
|
||||
}
|
||||
shellutils.R(&KeypairListOptions{}, "keypair-list", "List keypairs", func(cli *openstack.SRegion, args *KeypairListOptions) error {
|
||||
keypairs, err := cli.GetKeypairs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(keypairs, 0, 0, 0, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type KeypairCreateOptions struct {
|
||||
NAME string
|
||||
PublicKey string
|
||||
Type string `help:"keypair type" choices:"ssh|x509"`
|
||||
}
|
||||
|
||||
shellutils.R(&KeypairCreateOptions{}, "keypair-create", "Create keypair", func(cli *openstack.SRegion, args *KeypairCreateOptions) error {
|
||||
keypair, err := cli.CreateKeypair(args.NAME, args.PublicKey, args.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(keypair)
|
||||
return nil
|
||||
})
|
||||
|
||||
type KeypairOptions struct {
|
||||
NAME string `help:"Keypair name"`
|
||||
}
|
||||
|
||||
shellutils.R(&KeypairOptions{}, "keypair-show", "Show keypair", func(cli *openstack.SRegion, args *KeypairOptions) error {
|
||||
keypair, err := cli.GetKeypair(args.NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(keypair)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&KeypairOptions{}, "keypair-delete", "Delete keypair", func(cli *openstack.SRegion, args *KeypairOptions) error {
|
||||
return cli.DeleteKeypair(args.NAME)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/openstack"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type QuotaOptions struct {
|
||||
}
|
||||
shellutils.R(&QuotaOptions{}, "quota-show", "Show quota", func(cli *openstack.SRegion, args *QuotaOptions) error {
|
||||
quota, err := cli.GetQuota()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(quota)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&openstack.SQuota{}, "quota-set", "Set quota", func(cli *openstack.SRegion, args *openstack.SQuota) error {
|
||||
return cli.SetQuota(args)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -29,4 +29,18 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type SecurityGroupCreateOptions struct {
|
||||
NAME string `help:"Name of security group"`
|
||||
Desc string `help:"Description of security group"`
|
||||
}
|
||||
|
||||
shellutils.R(&SecurityGroupCreateOptions{}, "security-group-create", "Create security group", func(cli *openstack.SRegion, args *SecurityGroupCreateOptions) error {
|
||||
secgroup, err := cli.CreateSecurityGroup(args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(secgroup)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type SnapshotShowOptions struct {
|
||||
type SnapshotOptions struct {
|
||||
ID string `help:"ID of snapshot"`
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotShowOptions{}, "snapshot-show", "Show snapshot", func(cli *openstack.SRegion, args *SnapshotShowOptions) error {
|
||||
shellutils.R(&SnapshotOptions{}, "snapshot-show", "Show snapshot", func(cli *openstack.SRegion, args *SnapshotOptions) error {
|
||||
snapshot, err := cli.GetISnapshotById(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -31,4 +31,23 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&SnapshotOptions{}, "snapshot-delete", "Delete snapshot", func(cli *openstack.SRegion, args *SnapshotOptions) error {
|
||||
return cli.DeleteSnapshot(args.ID)
|
||||
})
|
||||
|
||||
type SnapshotCreateOptions struct {
|
||||
DISKID string `help:"Disk ID"`
|
||||
Name string `help:"Disk Name"`
|
||||
Desc string `help:"Disk description"`
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "Create snapshot", func(cli *openstack.SRegion, args *SnapshotCreateOptions) error {
|
||||
snapshot, err := cli.CreateSnapshot(args.DISKID, args.Name, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(snapshot)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func (region *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloud
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshot := SSnapshot{}
|
||||
snapshot := SSnapshot{region: region}
|
||||
if err := resp.Unmarshal(&snapshot, "snapshot"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func (snapshot *SSnapshot) Refresh() error {
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSnapshots(diskId string) ([]cloudprovider.ICloudSnapshot, error) {
|
||||
_, resp, err := region.CinderGet("/snapshots/detail", "", nil)
|
||||
_, resp, err := region.CinderList("/snapshots/detail", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -121,6 +121,9 @@ func (snapshot *SSnapshot) GetGlobalId() string {
|
||||
}
|
||||
|
||||
func (snapshot *SSnapshot) GetName() string {
|
||||
if len(snapshot.Name) == 0 {
|
||||
return snapshot.ID
|
||||
}
|
||||
return snapshot.Name
|
||||
}
|
||||
|
||||
@@ -139,10 +142,24 @@ func (snapshot *SSnapshot) GetDiskType() string {
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
|
||||
func (snapshot *SRegion) DeleteSnapshot(snapshotId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) DeleteSnapshot(snapshotId string) error {
|
||||
_, err := region.CinderDelete("/snapshots/"+snapshotId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (snapshot *SRegion) CreateSnapshot(diskId, name, desc string) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (region *SRegion) CreateSnapshot(diskId, name, desc string) (*SSnapshot, error) {
|
||||
params := map[string]map[string]interface{}{
|
||||
"snapshot": {
|
||||
"volume_id": diskId,
|
||||
"name": name,
|
||||
"description": desc,
|
||||
"force": true,
|
||||
},
|
||||
}
|
||||
_, resp, err := region.CinderCreate("/snapshots", "", jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshot := &SSnapshot{region: region}
|
||||
return snapshot, resp.Unmarshal(snapshot, "snapshot")
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package openstack
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
@@ -94,7 +96,13 @@ func (storage *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
}
|
||||
|
||||
func (storage *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
disk, err := storage.zone.region.CreateDisk(storage.zone.ZoneName, storage.Name, name, sizeGb, desc)
|
||||
if err != nil {
|
||||
log.Errorf("createDisk fail %v", err)
|
||||
return nil, err
|
||||
}
|
||||
disk.storage = storage
|
||||
return disk, cloudprovider.WaitStatus(disk, models.DISK_READY, time.Second*5, time.Minute*5)
|
||||
}
|
||||
|
||||
func (storage *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) {
|
||||
|
||||
@@ -3,11 +3,18 @@ package openstack
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/image/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
|
||||
type SStoragecache struct {
|
||||
@@ -49,7 +56,16 @@ func (cache *SStoragecache) GetManagerId() string {
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) fetchImages() error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
images, err := cache.region.GetImages("", ACTIVE, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.iimages = make([]cloudprovider.ICloudImage, len(images))
|
||||
for i := 0; i < len(images); i++ {
|
||||
images[i].storageCache = cache
|
||||
cache.iimages[i] = &images[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) {
|
||||
@@ -62,7 +78,12 @@ func (cache *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) {
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
image, err := cache.region.GetImage(extId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image.storageCache = cache
|
||||
return image, nil
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) GetPath() string {
|
||||
@@ -70,11 +91,62 @@ func (cache *SStoragecache) GetPath() string {
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist, osVersion string, extId string, isForce bool) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
if len(extId) > 0 {
|
||||
log.Debugf("UploadImage: Image external ID exists %s", extId)
|
||||
|
||||
statsu, err := cache.region.GetImageStatus(extId)
|
||||
if err != nil {
|
||||
log.Errorf("GetImageStatus error %s", err)
|
||||
}
|
||||
if statsu == ACTIVE && !isForce {
|
||||
return extId, nil
|
||||
}
|
||||
}
|
||||
log.Debugf("UploadImage: no external ID")
|
||||
return cache.uploadImage(ctx, userCred, imageId, osArch, osType, osDist, osVersion, isForce)
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) uploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, isForce bool) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (cache *SStoragecache) uploadImage(ctx context.Context, userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist, osVersion string, isForce bool) (string, error) {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region, "")
|
||||
|
||||
meta, reader, err := modules.Images.Download(s, imageId, string(qemuimg.VMDK), false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Infof("meta data %s", meta)
|
||||
|
||||
imageBaseName := imageId
|
||||
if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' {
|
||||
imageBaseName = fmt.Sprintf("img%s", imageId)
|
||||
}
|
||||
imageName := imageBaseName
|
||||
nameIdx := 1
|
||||
|
||||
for {
|
||||
_, err = cache.region.GetImageByName(imageName)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
break
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx)
|
||||
nameIdx++
|
||||
}
|
||||
|
||||
image, err := cache.region.CreateImage(imageName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
image.storageCache = cache
|
||||
|
||||
_, err = cache.region.client.StreamRequest(cache.region.Name, "image", "PUT", fmt.Sprintf("/v2/images/%s/file", image.ID), "", reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return image.ID, cloudprovider.WaitStatus(image, models.CACHED_IMAGE_STATUS_READY, 15*time.Second, 3600*time.Second)
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
|
||||
@@ -96,7 +96,8 @@ func (vpc *SVpc) Delete() error {
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteVpc(vpcId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
_, err := region.Delete("network", "/v2.0/networks/"+vpcId, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (vpc *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
|
||||
@@ -191,7 +192,7 @@ func (region *SRegion) GetVpc(vpcId string) (*SVpc, error) {
|
||||
}
|
||||
|
||||
func (region *SRegion) GetVpcs() ([]SVpc, error) {
|
||||
_, resp, err := region.Get("network", "/v2.0/networks", "", nil)
|
||||
_, resp, err := region.List("network", "/v2.0/networks", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
@@ -56,7 +57,13 @@ func (wire *SWire) GetBandwidth() int {
|
||||
}
|
||||
|
||||
func (wire *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
networkId, err := wire.zone.region.CreateNetwork(wire.vpc.ID, name, cidr, desc)
|
||||
if err != nil {
|
||||
log.Errorf("CreateNetwork error %s", err)
|
||||
return nil, err
|
||||
}
|
||||
wire.inetworks = nil
|
||||
return wire.GetINetworkById(networkId)
|
||||
}
|
||||
|
||||
func (wire *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -76,7 +77,7 @@ func (zone *SZone) getStorageByCategory(category string) (*SStorage, error) {
|
||||
}
|
||||
for i := 0; i < len(storages); i++ {
|
||||
storage := storages[i].(*SStorage)
|
||||
if storage.Name == category {
|
||||
if strings.ToLower(storage.Name) == strings.ToLower(category) {
|
||||
return storage, nil
|
||||
}
|
||||
}
|
||||
@@ -94,7 +95,7 @@ func (zone *SZone) fetchStorages() error {
|
||||
zone.istorages = []cloudprovider.ICloudStorage{}
|
||||
|
||||
for _, service := range []string{"volumev3", "volumev2", "volume"} {
|
||||
_, resp, err := zone.region.Get(service, "/types", "", nil)
|
||||
_, resp, err := zone.region.List(service, "/types", "", nil)
|
||||
if err == nil {
|
||||
storages := []SStorage{}
|
||||
if err := resp.Unmarshal(&storages, "volume_types"); err != nil {
|
||||
@@ -142,7 +143,7 @@ func (zone *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
|
||||
hosts := []SHost{}
|
||||
_, maxVersion, err := zone.region.GetVersion("compute")
|
||||
if err == nil && version.GE(maxVersion, HYPERVISORS_VERSION) {
|
||||
_, resp, err := zone.region.Get("compute", "/os-hypervisors/detail", maxVersion, nil)
|
||||
_, resp, err := zone.region.List("compute", "/os-hypervisors/detail", maxVersion, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,7 +157,7 @@ func (zone *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
|
||||
return ihosts, nil
|
||||
}
|
||||
|
||||
_, resp, err := zone.region.Get("compute", "/os-hosts", "", nil)
|
||||
_, resp, err := zone.region.List("compute", "/os-hosts", "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -749,7 +749,7 @@ func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
|
||||
return self.host.zone.region.instanceOperation(self.InstanceId, "ModifyInstancesAttribute", params)
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroups(secgroupIds []string) error {
|
||||
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
params := map[string]string{}
|
||||
for i := 0; i < len(secgroupIds); i++ {
|
||||
params[fmt.Sprintf("SecurityGroups.%d", i)] = secgroupIds[i]
|
||||
|
||||
@@ -44,6 +44,10 @@ func (self *SQcloudProviderFactory) IsSupportPrepaidResources() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SQcloudProviderFactory) NeedSyncSkuFromCloud() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SQcloudProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
|
||||
appID, _ := data.GetString("app_id")
|
||||
if len(appID) == 0 {
|
||||
|
||||
@@ -627,3 +627,7 @@ func (region *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbala
|
||||
func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package sysutils
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -231,3 +233,20 @@ func GetSerialPorts(lines []string) []string {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func Start(closeFd bool, args ...string) (p *os.Process, err error) {
|
||||
if args[0], err = exec.LookPath(args[0]); err == nil {
|
||||
var procAttr os.ProcAttr
|
||||
if closeFd {
|
||||
procAttr.Files = []*os.File{nil, nil, nil}
|
||||
} else {
|
||||
procAttr.Files = []*os.File{os.Stdin,
|
||||
os.Stdout, os.Stderr}
|
||||
}
|
||||
p, err := os.StartProcess(args[0], args, &procAttr)
|
||||
if err == nil {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ func StartService() {
|
||||
}
|
||||
|
||||
cloudcommon.InitDB(dbOpts)
|
||||
defer cloudcommon.CloseDB()
|
||||
|
||||
app := cloudcommon.InitApp(commonOpts, true)
|
||||
yunionconf.InitHandlers(app)
|
||||
@@ -38,7 +37,9 @@ func StartService() {
|
||||
if db.CheckSync(opts.AutoSyncTable) {
|
||||
err := models.InitDB()
|
||||
if err == nil {
|
||||
cloudcommon.ServeForever(app, commonOpts)
|
||||
cloudcommon.ServeForeverWithCleanup(app, commonOpts, func() {
|
||||
cloudcommon.CloseDB()
|
||||
})
|
||||
} else {
|
||||
log.Errorf("InitDB fail: %s", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user