openstack.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package guestdrivers
  15. import (
  16. "context"
  17. "fmt"
  18. "time"
  19. "yunion.io/x/cloudmux/pkg/cloudprovider"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/util/billing"
  24. "yunion.io/x/pkg/util/rbacscope"
  25. "yunion.io/x/pkg/utils"
  26. api "yunion.io/x/onecloud/pkg/apis/compute"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  28. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  31. "yunion.io/x/onecloud/pkg/compute/models"
  32. "yunion.io/x/onecloud/pkg/compute/options"
  33. "yunion.io/x/onecloud/pkg/httperrors"
  34. "yunion.io/x/onecloud/pkg/mcclient"
  35. scheduler "yunion.io/x/onecloud/pkg/scheduler/options"
  36. )
  37. type SOpenStackGuestDriver struct {
  38. SManagedVirtualizedGuestDriver
  39. }
  40. func init() {
  41. driver := SOpenStackGuestDriver{}
  42. models.RegisterGuestDriver(&driver)
  43. }
  44. func (self *SOpenStackGuestDriver) DoScheduleCPUFilter() bool {
  45. return scheduler.Options.OpenstackSchedulerCPUFilter
  46. }
  47. func (self *SOpenStackGuestDriver) DoScheduleMemoryFilter() bool {
  48. return scheduler.Options.OpenstackSchedulerMemoryFilter
  49. }
  50. func (self *SOpenStackGuestDriver) DoScheduleSKUFilter() bool {
  51. return scheduler.Options.OpenstackSchedulerSKUFilter
  52. }
  53. func (self *SOpenStackGuestDriver) DoScheduleStorageFilter() bool {
  54. return scheduler.Options.OpenstackSchedulerStorageFilter
  55. }
  56. func (self *SOpenStackGuestDriver) GetHypervisor() string {
  57. return api.HYPERVISOR_OPENSTACK
  58. }
  59. func (self *SOpenStackGuestDriver) GetProvider() string {
  60. return api.CLOUD_PROVIDER_OPENSTACK
  61. }
  62. func (self *SOpenStackGuestDriver) GetInstanceCapability() cloudprovider.SInstanceCapability {
  63. return cloudprovider.SInstanceCapability{
  64. Hypervisor: self.GetHypervisor(),
  65. Provider: self.GetProvider(),
  66. DefaultAccount: cloudprovider.SDefaultAccount{
  67. Linux: cloudprovider.SOsDefaultAccount{
  68. DefaultAccount: api.VM_DEFAULT_LINUX_LOGIN_USER,
  69. Changeable: false,
  70. },
  71. Windows: cloudprovider.SOsDefaultAccount{
  72. DefaultAccount: api.VM_DEFAULT_WINDOWS_LOGIN_USER,
  73. Changeable: false,
  74. },
  75. },
  76. }
  77. }
  78. func (self *SOpenStackGuestDriver) GetComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
  79. keys := models.SComputeResourceKeys{}
  80. keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
  81. keys.CloudEnv = api.CLOUD_ENV_PRIVATE_CLOUD
  82. keys.Provider = api.CLOUD_PROVIDER_OPENSTACK
  83. keys.Brand = brand
  84. keys.Hypervisor = api.HYPERVISOR_OPENSTACK
  85. return keys
  86. }
  87. func (self *SOpenStackGuestDriver) IsSupportEip() bool {
  88. return false
  89. }
  90. func (self *SOpenStackGuestDriver) GetDefaultSysDiskBackend() string {
  91. return api.STORAGE_OPENSTACK_NOVA
  92. }
  93. func (self *SOpenStackGuestDriver) GetMinimalSysDiskSizeGb() int {
  94. return options.Options.DefaultDiskSizeMB / 1024
  95. }
  96. func (self *SOpenStackGuestDriver) GetStorageTypes() []string {
  97. storages, _ := models.StorageManager.GetStorageTypesByProvider(self.GetProvider())
  98. return storages
  99. }
  100. func (self *SOpenStackGuestDriver) ChooseHostStorage(host *models.SHost, guest *models.SGuest, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
  101. return chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
  102. }
  103. func (self *SOpenStackGuestDriver) GetDetachDiskStatus() ([]string, error) {
  104. return []string{api.VM_READY, api.VM_RUNNING}, nil
  105. }
  106. func (self *SOpenStackGuestDriver) GetAttachDiskStatus() ([]string, error) {
  107. return []string{api.VM_READY, api.VM_RUNNING}, nil
  108. }
  109. func (self *SOpenStackGuestDriver) GetRebuildRootStatus() ([]string, error) {
  110. return []string{api.VM_READY, api.VM_RUNNING, api.VM_REBUILD_ROOT_FAIL}, nil
  111. }
  112. func (self *SOpenStackGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
  113. return []string{api.VM_READY, api.VM_RUNNING}, nil
  114. }
  115. func (self *SOpenStackGuestDriver) IsNeedInjectPasswordByCloudInit() bool {
  116. return true
  117. }
  118. func (self *SOpenStackGuestDriver) IsWindowsUserDataTypeNeedEncode() bool {
  119. return true
  120. }
  121. func (self *SOpenStackGuestDriver) IsNeedRestartForResetLoginInfo() bool {
  122. return false
  123. }
  124. func (self *SOpenStackGuestDriver) IsRebuildRootSupportChangeImage() bool {
  125. return true
  126. }
  127. func (self *SOpenStackGuestDriver) GetDeployStatus() ([]string, error) {
  128. return []string{api.VM_RUNNING}, nil
  129. }
  130. func (self *SOpenStackGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerCreateEipInput) error {
  131. return httperrors.NewInputParameterError("%s not support create eip, it only support bind eip", self.GetHypervisor())
  132. }
  133. func (self *SOpenStackGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
  134. if !utils.IsInStringArray(guest.Status, []string{api.VM_READY}) {
  135. return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
  136. }
  137. return nil
  138. }
  139. func (self *SOpenStackGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
  140. var err error
  141. input, err = self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input)
  142. if err != nil {
  143. return nil, err
  144. }
  145. if len(input.Networks) >= 2 {
  146. return nil, httperrors.NewInputParameterError("cannot support more than 1 nic")
  147. }
  148. if len(input.Eip) > 0 || input.EipBw > 0 {
  149. return nil, httperrors.NewUnsupportOperationError("%s not support create virtual machine with eip", self.GetHypervisor())
  150. }
  151. for i := 1; i < len(input.Disks); i++ {
  152. disk := input.Disks[i]
  153. if disk.Backend == api.STORAGE_OPENSTACK_NOVA {
  154. return nil, httperrors.NewUnsupportOperationError("data disk not support storage type %s", disk.Backend)
  155. }
  156. }
  157. return input, nil
  158. }
  159. func (self *SOpenStackGuestDriver) GetGuestInitialStateAfterCreate() string {
  160. return api.VM_RUNNING
  161. }
  162. func (self *SOpenStackGuestDriver) attachDisks(ctx context.Context, ihost cloudprovider.ICloudHost, instanceId string, diskIds []string) {
  163. if len(diskIds) == 0 {
  164. return
  165. }
  166. iVM, err := ihost.GetIVMById(instanceId)
  167. if err != nil || iVM == nil {
  168. log.Errorf("cannot find vm %s", instanceId)
  169. return
  170. }
  171. for _, diskId := range diskIds {
  172. err = iVM.AttachDisk(ctx, diskId)
  173. if err != nil {
  174. log.Errorf("failed to attach disk %s", diskId)
  175. }
  176. }
  177. return
  178. }
  179. func (self *SOpenStackGuestDriver) RemoteDeployGuestForRebuildRoot(ctx context.Context, guest *models.SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
  180. iVM, err := ihost.GetIVMById(guest.GetExternalId())
  181. if err != nil || iVM == nil {
  182. return nil, fmt.Errorf("cannot find vm %s(%s)", guest.Id, guest.Name)
  183. }
  184. driver, err := guest.GetDriver()
  185. if err != nil {
  186. return nil, err
  187. }
  188. instanceId := iVM.GetGlobalId()
  189. diskId, err := func() (string, error) {
  190. lockman.LockObject(ctx, guest)
  191. defer lockman.ReleaseObject(ctx, guest)
  192. sysDisk, err := guest.GetSystemDisk()
  193. if err != nil {
  194. return "", errors.Wrap(err, "guest.GetSystemDisk(")
  195. }
  196. storage, err := sysDisk.GetStorage()
  197. if err != nil {
  198. return "", errors.Wrap(err, "sysDisk.GetStorage")
  199. }
  200. if storage.StorageType == api.STORAGE_OPENSTACK_NOVA { //不通过镜像创建磁盘的机器
  201. conf := cloudprovider.SManagedVMRebuildRootConfig{
  202. Account: desc.Account,
  203. ImageId: desc.ExternalImageId,
  204. Password: desc.Password,
  205. PublicKey: desc.PublicKey,
  206. SysSizeGB: desc.SysDisk.SizeGB,
  207. OsType: desc.OsType,
  208. }
  209. return iVM.RebuildRoot(ctx, &conf)
  210. }
  211. iDisks, err := iVM.GetIDisks()
  212. if err != nil {
  213. return "", errors.Wrap(err, "iVM.GetIDisks")
  214. }
  215. detachDisks := []string{}
  216. for i, iDisk := range iDisks {
  217. if i != 0 {
  218. err = iVM.DetachDisk(ctx, iDisk.GetGlobalId())
  219. if err != nil {
  220. return "", errors.Wrap(err, "iVM.DetachDisk")
  221. }
  222. detachDisks = append(detachDisks, iDisk.GetGlobalId())
  223. }
  224. }
  225. var ieip cloudprovider.ICloudEIP
  226. if eip, err := guest.GetElasticIp(); eip != nil {
  227. ieip, err = eip.GetIEip(ctx)
  228. if err != nil {
  229. return "", errors.Wrap(err, "eip.GetIEip")
  230. }
  231. err = ieip.Dissociate()
  232. if err != nil {
  233. return "", errors.Wrap(err, "ieip.Dissociate")
  234. }
  235. } else if err != nil {
  236. return "", err
  237. }
  238. err = iVM.DeleteVM(ctx)
  239. if err != nil {
  240. return "", errors.Wrap(err, "iVM.DeleteVM")
  241. }
  242. err = cloudprovider.WaitDeleted(iVM, time.Second*5, time.Minute*10)
  243. if err != nil {
  244. return "", errors.Wrap(err, "WaitDeleted")
  245. }
  246. desc.DataDisks = []cloudprovider.SDiskInfo{}
  247. iVM, err = ihost.CreateVM(&desc)
  248. if err != nil {
  249. return "", errors.Wrap(err, "ihost.CreateVM")
  250. }
  251. instanceId = iVM.GetGlobalId()
  252. db.SetExternalId(guest, task.GetUserCred(), instanceId)
  253. initialState := driver.GetGuestInitialStateAfterCreate()
  254. log.Debugf("VMrebuildRoot %s new instance, wait status %s ...", iVM.GetGlobalId(), initialState)
  255. cloudprovider.WaitStatus(iVM, initialState, time.Second*5, time.Second*1800)
  256. opts := &cloudprovider.ServerStopOptions{
  257. IsForce: true,
  258. }
  259. iVM.StopVM(ctx, opts)
  260. if ieip != nil {
  261. conf := &cloudprovider.AssociateConfig{
  262. InstanceId: instanceId,
  263. AssociateType: api.EIP_ASSOCIATE_TYPE_SERVER,
  264. }
  265. if err = ieip.Associate(conf); err != nil {
  266. return "", errors.Wrap(err, "eip.Associate")
  267. }
  268. }
  269. self.attachDisks(ctx, ihost, instanceId, detachDisks)
  270. iDisks, err = iVM.GetIDisks()
  271. if err != nil {
  272. return "", errors.Wrapf(err, "iVM.GetIDisks.AfterCreated")
  273. }
  274. for _, iDisk := range iDisks {
  275. return iDisk.GetGlobalId(), nil
  276. }
  277. return "", fmt.Errorf("failed to found new instance system disk")
  278. }()
  279. if err != nil {
  280. return nil, err
  281. }
  282. initialState := driver.GetGuestInitialStateAfterRebuild()
  283. log.Debugf("VMrebuildRoot %s new diskID %s, wait status %s ...", iVM.GetGlobalId(), diskId, initialState)
  284. err = cloudprovider.WaitStatus(iVM, initialState, time.Second*5, time.Second*1800)
  285. if err != nil {
  286. return nil, err
  287. }
  288. log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId())
  289. maxWaitSecs := 300
  290. waited := 0
  291. for {
  292. // hack, wait disk number consistent
  293. idisks, err := iVM.GetIDisks()
  294. if err != nil {
  295. log.Errorf("fail to find VM idisks %s", err)
  296. return nil, err
  297. }
  298. if len(idisks) < len(desc.DataDisks)+1 {
  299. if waited > maxWaitSecs {
  300. log.Errorf("inconsistent disk number, wait timeout, must be something wrong on remote")
  301. return nil, cloudprovider.ErrTimeout
  302. }
  303. log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1)
  304. time.Sleep(time.Second * 5)
  305. waited += 5
  306. } else {
  307. if idisks[0].GetGlobalId() == diskId {
  308. break
  309. }
  310. if waited > maxWaitSecs {
  311. return nil, fmt.Errorf("inconsistent sys disk id after rebuild root")
  312. }
  313. log.Debugf("current system disk id inconsistent %s != %s, try after 5 seconds", idisks[0].GetGlobalId(), diskId)
  314. time.Sleep(time.Second * 5)
  315. waited += 5
  316. }
  317. }
  318. data := fetchIVMinfo(desc, iVM, guest.Id, desc.Account, desc.Password, desc.PublicKey, "rebuild")
  319. return data, nil
  320. }
  321. func (self *SOpenStackGuestDriver) GetGuestInitialStateAfterRebuild() string {
  322. return api.VM_READY
  323. }
  324. func (self *SOpenStackGuestDriver) AllowReconfigGuest() bool {
  325. return true
  326. }
  327. func (self *SOpenStackGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
  328. return false
  329. }
  330. func (self *SOpenStackGuestDriver) IsSupportMigrate() bool {
  331. return true
  332. }
  333. func (self *SOpenStackGuestDriver) IsSupportLiveMigrate() bool {
  334. return true
  335. }