google.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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. "strings"
  19. "time"
  20. "yunion.io/x/cloudmux/pkg/cloudprovider"
  21. "yunion.io/x/cloudmux/pkg/multicloud/google"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/util/billing"
  26. "yunion.io/x/pkg/util/osprofile"
  27. "yunion.io/x/pkg/util/rbacscope"
  28. "yunion.io/x/pkg/utils"
  29. api "yunion.io/x/onecloud/pkg/apis/compute"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  32. "yunion.io/x/onecloud/pkg/compute/models"
  33. "yunion.io/x/onecloud/pkg/httperrors"
  34. "yunion.io/x/onecloud/pkg/mcclient"
  35. )
  36. type SGoogleGuestDriver struct {
  37. SManagedVirtualizedGuestDriver
  38. }
  39. func init() {
  40. driver := SGoogleGuestDriver{}
  41. models.RegisterGuestDriver(&driver)
  42. }
  43. func (self *SGoogleGuestDriver) GetComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
  44. keys := models.SComputeResourceKeys{}
  45. keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
  46. keys.CloudEnv = api.CLOUD_ENV_PUBLIC_CLOUD
  47. keys.Provider = api.CLOUD_PROVIDER_GOOGLE
  48. keys.Brand = api.CLOUD_PROVIDER_GOOGLE
  49. keys.Hypervisor = api.HYPERVISOR_GOOGLE
  50. return keys
  51. }
  52. func (self *SGoogleGuestDriver) GetHypervisor() string {
  53. return api.HYPERVISOR_GOOGLE
  54. }
  55. func (self *SGoogleGuestDriver) GetProvider() string {
  56. return api.CLOUD_PROVIDER_GOOGLE
  57. }
  58. func (self *SGoogleGuestDriver) GetInstanceCapability() cloudprovider.SInstanceCapability {
  59. return cloudprovider.SInstanceCapability{
  60. Hypervisor: self.GetHypervisor(),
  61. Provider: self.GetProvider(),
  62. DefaultAccount: cloudprovider.SDefaultAccount{
  63. Linux: cloudprovider.SOsDefaultAccount{
  64. DefaultAccount: api.VM_DEFAULT_LINUX_LOGIN_USER,
  65. },
  66. Windows: cloudprovider.SOsDefaultAccount{
  67. DefaultAccount: api.VM_DEFAULT_WINDOWS_LOGIN_USER,
  68. },
  69. },
  70. Storages: cloudprovider.Storage{
  71. DataDisk: []cloudprovider.StorageInfo{
  72. {StorageType: api.STORAGE_GOOGLE_PD_SSD, MaxSizeGb: 65536, MinSizeGb: 10, StepSizeGb: 1, Resizable: false},
  73. {StorageType: api.STORAGE_GOOGLE_PD_STANDARD, MaxSizeGb: 65536, MinSizeGb: 10, StepSizeGb: 1, Resizable: false},
  74. {StorageType: api.STORAGE_GOOGLE_PD_BALANCED, MaxSizeGb: 65536, MinSizeGb: 10, StepSizeGb: 1, Resizable: false},
  75. {StorageType: api.STORAGE_GOOGLE_PD_EXTREME, MaxSizeGb: 65536, MinSizeGb: 500, StepSizeGb: 1, Resizable: false},
  76. {StorageType: api.STORAGE_GOOGLE_LOCAL_SSD, MaxSizeGb: 375, MinSizeGb: 375, StepSizeGb: 1, Resizable: false},
  77. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_BALANCED, MaxSizeGb: 65536, MinSizeGb: 4, StepSizeGb: 1, Resizable: false},
  78. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_EXTREME, MaxSizeGb: 65536, MinSizeGb: 64, StepSizeGb: 1, Resizable: false},
  79. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_THROUGHPUT, MaxSizeGb: 32768, MinSizeGb: 2048, StepSizeGb: 1, Resizable: false},
  80. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_ML, MaxSizeGb: 65536, MinSizeGb: 4, StepSizeGb: 1, Resizable: false},
  81. },
  82. SysDisk: []cloudprovider.StorageInfo{
  83. {StorageType: api.STORAGE_GOOGLE_PD_SSD, MaxSizeGb: 65536, MinSizeGb: 10, StepSizeGb: 1, Resizable: false},
  84. {StorageType: api.STORAGE_GOOGLE_PD_STANDARD, MaxSizeGb: 65536, MinSizeGb: 10, StepSizeGb: 1, Resizable: false},
  85. {StorageType: api.STORAGE_GOOGLE_PD_BALANCED, MaxSizeGb: 65536, MinSizeGb: 10, StepSizeGb: 1, Resizable: false},
  86. {StorageType: api.STORAGE_GOOGLE_PD_EXTREME, MaxSizeGb: 65536, MinSizeGb: 500, StepSizeGb: 1, Resizable: false},
  87. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_BALANCED, MaxSizeGb: 65536, MinSizeGb: 4, StepSizeGb: 1, Resizable: false},
  88. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_EXTREME, MaxSizeGb: 65536, MinSizeGb: 64, StepSizeGb: 1, Resizable: false},
  89. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_THROUGHPUT, MaxSizeGb: 32768, MinSizeGb: 2048, StepSizeGb: 1, Resizable: false},
  90. {StorageType: api.STORAGE_GOOGLE_HYPERDISK_ML, MaxSizeGb: 65536, MinSizeGb: 4, StepSizeGb: 1, Resizable: false},
  91. },
  92. },
  93. }
  94. }
  95. func (self *SGoogleGuestDriver) GetDefaultSysDiskBackend() string {
  96. return api.STORAGE_GOOGLE_PD_STANDARD
  97. }
  98. func (self *SGoogleGuestDriver) GetMinimalSysDiskSizeGb() int {
  99. return 10
  100. }
  101. func (self *SGoogleGuestDriver) GetStorageTypes() []string {
  102. return []string{
  103. api.STORAGE_GOOGLE_PD_SSD,
  104. api.STORAGE_GOOGLE_PD_STANDARD,
  105. api.STORAGE_GOOGLE_PD_BALANCED,
  106. api.STORAGE_GOOGLE_PD_EXTREME,
  107. api.STORAGE_GOOGLE_LOCAL_SSD,
  108. api.STORAGE_GOOGLE_HYPERDISK_BALANCED,
  109. api.STORAGE_GOOGLE_HYPERDISK_EXTREME,
  110. api.STORAGE_GOOGLE_HYPERDISK_THROUGHPUT,
  111. api.STORAGE_GOOGLE_HYPERDISK_ML,
  112. }
  113. }
  114. func (self *SGoogleGuestDriver) ChooseHostStorage(host *models.SHost, guest *models.SGuest, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
  115. return chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
  116. }
  117. func (self *SGoogleGuestDriver) GetGuestInitialStateAfterCreate() string {
  118. return api.VM_RUNNING
  119. }
  120. func (self *SGoogleGuestDriver) GetDetachDiskStatus() ([]string, error) {
  121. return []string{api.VM_READY, api.VM_RUNNING}, nil
  122. }
  123. func (self *SGoogleGuestDriver) GetAttachDiskStatus() ([]string, error) {
  124. return []string{api.VM_READY, api.VM_RUNNING}, nil
  125. }
  126. func (self *SGoogleGuestDriver) GetRebuildRootStatus() ([]string, error) {
  127. return []string{api.VM_READY}, nil
  128. }
  129. func (self *SGoogleGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
  130. return []string{api.VM_READY}, nil
  131. }
  132. func (self *SGoogleGuestDriver) GetDeployStatus() ([]string, error) {
  133. return []string{api.VM_READY}, nil
  134. }
  135. func (self *SGoogleGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
  136. if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_START_RESIZE_DISK, api.VM_RESIZE_DISK}) {
  137. return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
  138. }
  139. if !utils.IsInStringArray(storage.StorageType, []string{
  140. api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD,
  141. api.STORAGE_GOOGLE_PD_BALANCED, api.STORAGE_GOOGLE_PD_EXTREME,
  142. api.STORAGE_GOOGLE_HYPERDISK_BALANCED, api.STORAGE_GOOGLE_HYPERDISK_EXTREME,
  143. api.STORAGE_GOOGLE_HYPERDISK_THROUGHPUT, api.STORAGE_GOOGLE_HYPERDISK_ML,
  144. }) {
  145. return fmt.Errorf("Cannot resize %s disk", storage.StorageType)
  146. }
  147. return nil
  148. }
  149. func (self *SGoogleGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
  150. input, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input)
  151. if err != nil {
  152. return nil, err
  153. }
  154. if len(input.Networks) > 2 {
  155. return nil, httperrors.NewInputParameterError("cannot support more than 1 nic")
  156. }
  157. localDisk := 0
  158. for i, disk := range input.Disks {
  159. minGB := -1
  160. maxGB := -1
  161. switch disk.Backend {
  162. case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_BALANCED:
  163. minGB = 10
  164. maxGB = 65536
  165. case api.STORAGE_GOOGLE_PD_EXTREME:
  166. minGB = 500
  167. maxGB = 65536
  168. case api.STORAGE_GOOGLE_LOCAL_SSD:
  169. minGB = 375
  170. maxGB = 375
  171. localDisk++
  172. case api.STORAGE_GOOGLE_HYPERDISK_BALANCED, api.STORAGE_GOOGLE_HYPERDISK_ML:
  173. minGB = 4
  174. maxGB = 65536
  175. case api.STORAGE_GOOGLE_HYPERDISK_THROUGHPUT:
  176. minGB = 2048
  177. maxGB = 32768
  178. case api.STORAGE_GOOGLE_HYPERDISK_EXTREME:
  179. minGB = 64
  180. maxGB = 65536
  181. default:
  182. return nil, httperrors.NewInputParameterError("Unknown google storage type %s", disk.Backend)
  183. }
  184. if i == 0 && disk.Backend == api.STORAGE_GOOGLE_LOCAL_SSD {
  185. return nil, httperrors.NewInputParameterError("System disk does not support %s disk", disk.Backend)
  186. }
  187. if disk.SizeMb < minGB*1024 || disk.SizeMb > maxGB*1024 {
  188. return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of %dGB ~ %dGB", disk.Backend, minGB, maxGB)
  189. }
  190. }
  191. if localDisk > 8 {
  192. return nil, httperrors.NewInputParameterError("%s disk cannot exceed 8", api.STORAGE_GOOGLE_LOCAL_SSD)
  193. }
  194. if localDisk > 0 && strings.HasPrefix(input.InstanceType, "e2") {
  195. return nil, httperrors.NewNotSupportedError("%s for %s features are not compatible for creating instance", input.InstanceType, api.STORAGE_GOOGLE_LOCAL_SSD)
  196. }
  197. return input, nil
  198. }
  199. func (self *SGoogleGuestDriver) GetGuestInitialStateAfterRebuild() string {
  200. return api.VM_READY
  201. }
  202. func (self *SGoogleGuestDriver) IsNeedInjectPasswordByCloudInit() bool {
  203. return true
  204. }
  205. // 谷歌云的用户自定义脚本不支持base64加密
  206. func (self *SGoogleGuestDriver) GetUserDataType() string {
  207. return cloudprovider.CLOUD_SHELL_WITHOUT_ENCRYPT
  208. }
  209. func (self *SGoogleGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
  210. ivm, err := guest.GetIVM(ctx)
  211. if err != nil {
  212. return errors.Wrap(err, "GetIVM")
  213. }
  214. result := jsonutils.NewDict()
  215. if ivm.GetStatus() != api.VM_RUNNING {
  216. err := ivm.StartVM(ctx)
  217. if err != nil {
  218. return errors.Wrap(err, "ivm.StartVM")
  219. }
  220. vm := ivm.(*google.SInstance)
  221. updateUserdata := false
  222. for _, item := range vm.Metadata.Items {
  223. if item.Key == google.METADATA_STARTUP_SCRIPT || item.Key == google.METADATA_STARTUP_SCRIPT_POWER_SHELL {
  224. updateUserdata = true
  225. break
  226. }
  227. }
  228. if updateUserdata {
  229. keyword := "Finished running startup scripts"
  230. err = cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) {
  231. output, err := ivm.GetSerialOutput(1)
  232. if err != nil {
  233. return false, errors.Wrap(err, "iVM.GetSerialOutput")
  234. }
  235. log.Debugf("wait for google startup scripts finish")
  236. if strings.Contains(output, keyword) {
  237. log.Debugf("%s", keyword)
  238. return true, nil
  239. }
  240. return false, nil
  241. })
  242. if err != nil {
  243. log.Errorf("failed wait google cloud startup scripts finish err: %v", err)
  244. }
  245. log.Debugf("clean google instance %s(%s) startup-script", guest.Name, guest.Id)
  246. err := ivm.UpdateUserData("")
  247. if err != nil {
  248. log.Errorf("failed to update google userdata")
  249. }
  250. }
  251. guest.SetStatus(ctx, userCred, api.VM_RUNNING, "StartOnHost")
  252. return task.ScheduleRun(result)
  253. }
  254. return guest.SetStatus(ctx, userCred, api.VM_RUNNING, "StartOnHost")
  255. }
  256. func (self *SGoogleGuestDriver) RemoteActionAfterGuestCreated(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM, desc *cloudprovider.SManagedVMCreateConfig) {
  257. keywords := map[string]string{
  258. strings.ToLower(osprofile.OS_TYPE_WINDOWS): "Finished with sysprep specialize phase",
  259. strings.ToLower(osprofile.OS_TYPE_LINUX): "Finished running startup scripts",
  260. }
  261. if keyword, ok := keywords[strings.ToLower(desc.OsType)]; ok {
  262. err := cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) {
  263. output, err := iVM.GetSerialOutput(1)
  264. if err != nil {
  265. return false, errors.Wrap(err, "iVM.GetSerialOutput")
  266. }
  267. log.Debugf("wait for google sysprep finish")
  268. if strings.Contains(output, keyword) {
  269. log.Debugf("%s", keyword)
  270. return true, nil
  271. }
  272. return false, nil
  273. })
  274. if err != nil {
  275. log.Errorf("failed wait google %s error: %v", keyword, err)
  276. }
  277. }
  278. log.Debugf("clean google instance %s(%s) startup-script", guest.Name, guest.Id)
  279. err := iVM.UpdateUserData("")
  280. if err != nil {
  281. log.Errorf("failed to update google userdata")
  282. }
  283. }
  284. func (self *SGoogleGuestDriver) AllowReconfigGuest() bool {
  285. return true
  286. }
  287. func (self *SGoogleGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
  288. return false
  289. }