Merge branch 'release/2.6.0' of ssh://git.yunion.io/~qiujian/onecloud into hotfix/qj-recode-cloudprovider

This commit is contained in:
Qiu Jian
2019-02-18 23:21:56 +08:00
82 changed files with 2622 additions and 392 deletions
+72 -24
View File
@@ -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
View File
@@ -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 -19
View File
@@ -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() {
+6 -2
View File
@@ -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)
}
}
+3 -1
View File
@@ -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() {
+7
View File
@@ -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",
+7 -2
View File
@@ -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...)
-23
View File
@@ -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)
}
+3 -2
View File
@@ -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()
})
}
+1
View File
@@ -33,6 +33,7 @@ type ICloudProviderFactory interface {
IsPublicCloud() bool
IsOnPremise() bool
IsSupportPrepaidResources() bool
NeedSyncSkuFromCloud() bool
}
type ICloudProvider interface {
+4
View File
@@ -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
}
+1
View File
@@ -11,6 +11,7 @@ import (
type SDiskInfo struct {
StorageType string
SizeGB int
Name string
}
type SManagedVMCreateConfig struct {
+38 -1
View File
@@ -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
}
+2
View File
@@ -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 {
+2
View File
@@ -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 {
+2 -1
View File
@@ -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
+2
View File
@@ -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 {
+3 -1
View File
@@ -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()
+292
View File
@@ -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
}
+2 -1
View File
@@ -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()
+8
View File
@@ -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
}
+3 -2
View File
@@ -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"
+107
View File
@@ -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)
}
+1
View File
@@ -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}
+1 -1
View File
@@ -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.")
+7 -39
View File
@@ -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) {
+19 -24
View File
@@ -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)
}
}
+19 -2
View File
@@ -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
}
+2
View File
@@ -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 (
+16 -14
View File
@@ -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()
}
})
}
+71 -78
View File
@@ -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()
}
}
+18 -10
View File
@@ -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)
+2 -2
View File
@@ -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 {
+4
View File
@@ -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 {
+4
View File
@@ -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
}
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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])
+4
View File
@@ -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 {
+4
View File
@@ -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
}
+1 -1
View File
@@ -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
}
+1 -1
View File
@@ -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
}
+4
View File
@@ -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 {
+4
View File
@@ -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
}
+4
View File
@@ -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 {
+1 -1
View File
@@ -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
}
+3 -2
View File
@@ -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
+4
View File
@@ -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 {
+4
View File
@@ -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
}
+4 -4
View File
@@ -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
View File
@@ -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
}
+244
View File
@@ -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
View File
@@ -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 {
+230
View File
@@ -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
View File
@@ -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
}
+92
View File
@@ -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")
}
+19 -5
View File
@@ -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")
}
+33 -2
View File
@@ -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) {
+1 -1
View File
@@ -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
}
+4
View File
@@ -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 {
+61
View File
@@ -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
}
+121 -5
View File
@@ -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
}
+173 -5
View File
@@ -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")
}
+42 -3
View File
@@ -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)
})
}
+53
View File
@@ -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
})
}
+53
View File
@@ -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
})
}
+19
View File
@@ -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)
})
}
+52
View File
@@ -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)
})
}
+24
View File
@@ -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)
})
}
+14
View File
@@ -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
})
}
+21 -2
View File
@@ -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
})
}
+23 -6
View File
@@ -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")
}
+9 -1
View File
@@ -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) {
+77 -5
View File
@@ -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) {
+3 -2
View File
@@ -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
}
+8 -1
View File
@@ -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) {
+5 -4
View File
@@ -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
}
+1 -1
View File
@@ -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]
+4
View File
@@ -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 {
+4
View File
@@ -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
}
+19
View File
@@ -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
}
+3 -2
View File
@@ -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)
}