instance.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  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 google
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "time"
  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/cloudinit"
  25. "yunion.io/x/pkg/util/encode"
  26. "yunion.io/x/pkg/util/fileutils"
  27. "yunion.io/x/pkg/util/imagetools"
  28. "yunion.io/x/pkg/util/stringutils"
  29. "yunion.io/x/pkg/utils"
  30. billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
  31. api "yunion.io/x/cloudmux/pkg/apis/compute"
  32. "yunion.io/x/cloudmux/pkg/cloudprovider"
  33. "yunion.io/x/cloudmux/pkg/multicloud"
  34. )
  35. const (
  36. METADATA_SSH_KEYS = "ssh-keys"
  37. METADATA_STARTUP_SCRIPT = "startup-script"
  38. METADATA_POWER_SHELL = "sysprep-specialize-script-ps1"
  39. METADATA_STARTUP_SCRIPT_POWER_SHELL = "windows-startup-script-ps1"
  40. )
  41. type AccessConfig struct {
  42. Type string
  43. Name string
  44. NatIP string
  45. NetworkTier string
  46. Kind string
  47. }
  48. type InstanceDisk struct {
  49. Type string
  50. Mode string
  51. Source string
  52. DeviceName string
  53. Index int
  54. Boot bool
  55. AutoDelete bool
  56. Licenses []string
  57. Interface string
  58. GuestOsFeatures []GuestOsFeature
  59. Kind string
  60. }
  61. type ServiceAccount struct {
  62. Email string
  63. scopes []string
  64. }
  65. type SInstanceTag struct {
  66. Items []string
  67. Fingerprint string
  68. }
  69. type SMetadataItem struct {
  70. Key string
  71. Value string
  72. }
  73. type SMetadata struct {
  74. Fingerprint string
  75. Items []SMetadataItem
  76. }
  77. type SInstance struct {
  78. multicloud.SInstanceBase
  79. GoogleTags
  80. host *SHost
  81. SResourceBase
  82. osInfo *imagetools.ImageInfo
  83. CreationTimestamp time.Time
  84. Description string
  85. Tags SInstanceTag
  86. MachineType string
  87. Status string
  88. Zone string
  89. CanIpForward bool
  90. NetworkInterfaces []SNetworkInterface
  91. Disks []InstanceDisk
  92. Metadata SMetadata
  93. ServiceAccounts []ServiceAccount
  94. Scheduling map[string]interface{}
  95. CpuPlatform string
  96. LabelFingerprint string
  97. StartRestricted bool
  98. DeletionProtection bool
  99. Kind string
  100. guestCpus int
  101. memoryMb int
  102. machineType string
  103. }
  104. func (region *SRegion) GetInstances(zone string, maxResults int, pageToken string) ([]SInstance, error) {
  105. instances := []SInstance{}
  106. params := map[string]string{}
  107. if len(zone) == 0 {
  108. return nil, fmt.Errorf("zone params can not be empty")
  109. }
  110. resource := fmt.Sprintf("zones/%s/instances", zone)
  111. return instances, region.List(resource, params, maxResults, pageToken, &instances)
  112. }
  113. func (region *SRegion) GetInstance(id string) (*SInstance, error) {
  114. instance := &SInstance{}
  115. return instance, region.Get("instances", id, instance)
  116. }
  117. func (instance *SInstance) GetHostname() string {
  118. return ""
  119. }
  120. func (instance *SInstance) fetchMachineType() error {
  121. if instance.guestCpus > 0 || instance.memoryMb > 0 || len(instance.machineType) > 0 {
  122. return nil
  123. }
  124. machinetype := SMachineType{}
  125. err := instance.host.zone.region.GetBySelfId(instance.MachineType, &machinetype)
  126. if err != nil {
  127. return err
  128. }
  129. instance.guestCpus = machinetype.GuestCpus
  130. instance.memoryMb = machinetype.MemoryMb
  131. instance.machineType = machinetype.Name
  132. return nil
  133. }
  134. func (self *SInstance) Refresh() error {
  135. instance, err := self.host.zone.region.GetInstance(self.Id)
  136. if err != nil {
  137. return err
  138. }
  139. err = jsonutils.Update(self, instance)
  140. if err != nil {
  141. return err
  142. }
  143. instance.Labels = self.Labels
  144. return nil
  145. }
  146. // PROVISIONING, STAGING, RUNNING, STOPPING, STOPPED, SUSPENDING, SUSPENDED, and TERMINATED.
  147. func (instance *SInstance) GetStatus() string {
  148. switch instance.Status {
  149. case "PROVISIONING":
  150. return api.VM_DEPLOYING
  151. case "STAGING":
  152. return api.VM_STARTING
  153. case "RUNNING":
  154. return api.VM_RUNNING
  155. case "STOPPING":
  156. return api.VM_STOPPING
  157. case "STOPPED":
  158. return api.VM_READY
  159. case "SUSPENDING":
  160. return api.VM_SUSPENDING
  161. case "SUSPENDED":
  162. return api.VM_SUSPEND
  163. case "TERMINATED":
  164. return api.VM_READY
  165. default:
  166. return api.VM_UNKNOWN
  167. }
  168. }
  169. func (ins *SInstance) GetPowerStates() string {
  170. status := ins.GetStatus()
  171. switch status {
  172. case api.VM_READY:
  173. return api.VM_POWER_STATES_OFF
  174. case api.VM_UNKNOWN:
  175. return api.VM_POWER_STATES_OFF
  176. default:
  177. return api.VM_POWER_STATES_ON
  178. }
  179. }
  180. func (instance *SInstance) GetBillingType() string {
  181. return billing_api.BILLING_TYPE_POSTPAID
  182. }
  183. func (instance *SInstance) GetCreatedAt() time.Time {
  184. return instance.CreationTimestamp
  185. }
  186. func (instance *SInstance) GetExpiredAt() time.Time {
  187. return time.Time{}
  188. }
  189. func (instance *SInstance) GetProjectId() string {
  190. return instance.host.zone.region.GetProjectId()
  191. }
  192. func (instance *SInstance) GetIHost() cloudprovider.ICloudHost {
  193. return instance.host
  194. }
  195. func (instance *SInstance) GetIHostId() string {
  196. return instance.host.GetGlobalId()
  197. }
  198. func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
  199. idisks := []cloudprovider.ICloudDisk{}
  200. for _, disk := range instance.Disks {
  201. _disk := &SDisk{}
  202. err := instance.host.zone.region.GetBySelfId(disk.Source, _disk)
  203. if err != nil {
  204. return nil, errors.Wrap(err, "GetDisk")
  205. }
  206. storage, err := instance.host.zone.region.GetStorage(_disk.Type)
  207. if err != nil {
  208. return nil, errors.Wrap(err, "GetStorage")
  209. }
  210. storage.zone = instance.host.zone
  211. _disk.storage = storage
  212. _disk.autoDelete = disk.AutoDelete
  213. _disk.boot = disk.Boot
  214. _disk.index = disk.Index
  215. idisks = append(idisks, _disk)
  216. }
  217. return idisks, nil
  218. }
  219. func (instance *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
  220. nics := []cloudprovider.ICloudNic{}
  221. for i := range instance.NetworkInterfaces {
  222. instance.NetworkInterfaces[i].instance = instance
  223. nics = append(nics, &instance.NetworkInterfaces[i])
  224. }
  225. return nics, nil
  226. }
  227. func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
  228. for _, networkinterface := range instance.NetworkInterfaces {
  229. for _, conf := range networkinterface.AccessConfigs {
  230. if len(conf.NatIP) > 0 {
  231. eips, err := instance.host.zone.region.GetEips(conf.NatIP, 0, "")
  232. if err != nil {
  233. return nil, errors.Wrapf(err, "region.GetEip(%s)", conf.NatIP)
  234. }
  235. if len(eips) == 1 {
  236. eips[0].region = instance.host.zone.region
  237. return &eips[0], nil
  238. }
  239. eip := &SAddress{
  240. region: instance.host.zone.region,
  241. Status: "IN_USE",
  242. Address: conf.NatIP,
  243. instanceId: instance.Id,
  244. }
  245. eip.Id = instance.Id
  246. eip.SelfLink = instance.SelfLink
  247. return eip, nil
  248. }
  249. }
  250. }
  251. return nil, nil
  252. }
  253. func (instance *SInstance) GetVcpuCount() int {
  254. instance.fetchMachineType()
  255. return instance.guestCpus
  256. }
  257. func (instance *SInstance) GetVmemSizeMB() int {
  258. instance.fetchMachineType()
  259. return instance.memoryMb
  260. }
  261. func (instance *SInstance) GetBootOrder() string {
  262. return "cdn"
  263. }
  264. func (instance *SInstance) GetVga() string {
  265. return "std"
  266. }
  267. func (instance *SInstance) GetVdi() string {
  268. return "vnc"
  269. }
  270. func (instance *SInstance) GetOsType() cloudprovider.TOsType {
  271. return cloudprovider.TOsType(instance.getNormalizedOsInfo().OsType)
  272. }
  273. func (instance *SInstance) getValidLicense() string {
  274. for _, disk := range instance.Disks {
  275. if disk.Index == 0 {
  276. for _, license := range disk.Licenses {
  277. if len(license) > 0 {
  278. return license
  279. }
  280. }
  281. }
  282. }
  283. return ""
  284. }
  285. func (instance *SInstance) getNormalizedOsInfo() *imagetools.ImageInfo {
  286. if instance.osInfo != nil {
  287. return instance.osInfo
  288. }
  289. osinfo := imagetools.NormalizeImageInfo(instance.getValidLicense(), "", "", "", "")
  290. instance.osInfo = &osinfo
  291. return instance.osInfo
  292. }
  293. func (instance *SInstance) GetFullOsName() string {
  294. return instance.getValidLicense()
  295. }
  296. func (instance *SInstance) GetBios() cloudprovider.TBiosType {
  297. return cloudprovider.ToBiosType(instance.getNormalizedOsInfo().OsBios)
  298. }
  299. func (instance *SInstance) GetOsArch() string {
  300. return instance.getNormalizedOsInfo().OsArch
  301. }
  302. func (instance *SInstance) GetOsDist() string {
  303. return instance.getNormalizedOsInfo().OsDistro
  304. }
  305. func (instance *SInstance) GetOsVersion() string {
  306. return instance.getNormalizedOsInfo().OsVersion
  307. }
  308. func (instance *SInstance) GetOsLang() string {
  309. return instance.getNormalizedOsInfo().OsLang
  310. }
  311. func (instance *SInstance) GetMachine() string {
  312. return "pc"
  313. }
  314. func (instance *SInstance) GetInstanceType() string {
  315. instance.fetchMachineType()
  316. return instance.machineType
  317. }
  318. func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
  319. return instance.Tags.Items, nil
  320. }
  321. func (instance *SInstance) SetSecurityGroups(ids []string) error {
  322. instance.Tags.Items = ids
  323. return instance.host.zone.region.SetResourceTags(instance.SelfLink, instance.Tags)
  324. }
  325. func (instance *SInstance) GetHypervisor() string {
  326. return api.HYPERVISOR_GOOGLE
  327. }
  328. func (instance *SInstance) StartVM(ctx context.Context) error {
  329. return instance.host.zone.region.StartInstance(instance.SelfLink)
  330. }
  331. func (instance *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
  332. return instance.host.zone.region.StopInstance(instance.SelfLink)
  333. }
  334. func (instance *SInstance) DeleteVM(ctx context.Context) error {
  335. if instance.DeletionProtection {
  336. err := instance.host.zone.region.DisableDeletionProtection(instance.SelfLink)
  337. if err != nil {
  338. return errors.Wrapf(err, "DisableDeletionProtection(%s)", instance.Name)
  339. }
  340. }
  341. return instance.host.zone.region.Delete(instance.SelfLink)
  342. }
  343. func (instance *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
  344. return cloudprovider.ErrNotSupported
  345. }
  346. func (instance *SInstance) UpdateUserData(userData string) error {
  347. items := []SMetadataItem{}
  348. for _, item := range instance.Metadata.Items {
  349. if item.Key != METADATA_STARTUP_SCRIPT && item.Key != METADATA_POWER_SHELL && item.Key != METADATA_STARTUP_SCRIPT_POWER_SHELL {
  350. items = append(items, item)
  351. }
  352. }
  353. if len(userData) > 0 {
  354. items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT, Value: userData})
  355. items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT_POWER_SHELL, Value: userData})
  356. items = append(items, SMetadataItem{Key: METADATA_POWER_SHELL, Value: userData})
  357. }
  358. instance.Metadata.Items = items
  359. return instance.host.zone.region.SetMetadata(instance.SelfLink, instance.Metadata)
  360. }
  361. func (instance *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
  362. diskId, err := instance.host.zone.region.RebuildRoot(instance.Id, opts.ImageId, opts.SysSizeGB)
  363. if err != nil {
  364. return "", errors.Wrap(err, "region.RebuildRoot")
  365. }
  366. deployOpts := &cloudprovider.SInstanceDeployOptions{
  367. Username: opts.Account,
  368. Password: opts.Password,
  369. UserData: opts.UserData,
  370. PublicKey: opts.PublicKey,
  371. }
  372. return diskId, instance.DeployVM(ctx, deployOpts)
  373. }
  374. func (instance *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
  375. conf := cloudinit.SCloudConfig{
  376. SshPwauth: cloudinit.SSH_PASSWORD_AUTH_ON,
  377. }
  378. if len(opts.UserData) > 0 {
  379. config, err := cloudinit.ParseUserData(opts.UserData)
  380. if err == nil {
  381. conf.Merge(config)
  382. }
  383. }
  384. user := cloudinit.NewUser(opts.Username)
  385. if len(opts.Password) > 0 {
  386. user.Password(opts.Password)
  387. }
  388. if len(opts.PublicKey) > 0 {
  389. user.SshKey(opts.PublicKey)
  390. }
  391. if len(opts.Password) > 0 || len(opts.PublicKey) > 0 {
  392. conf.MergeUser(user)
  393. items := []SMetadataItem{}
  394. instance.Refresh()
  395. for _, item := range instance.Metadata.Items {
  396. if item.Key != METADATA_STARTUP_SCRIPT_POWER_SHELL && item.Key != METADATA_STARTUP_SCRIPT {
  397. items = append(items, item)
  398. }
  399. }
  400. items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT_POWER_SHELL, Value: conf.UserDataPowerShell()})
  401. items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT, Value: conf.UserDataScript()})
  402. instance.Metadata.Items = items
  403. return instance.host.zone.region.SetMetadata(instance.SelfLink, instance.Metadata)
  404. }
  405. if opts.DeleteKeypair {
  406. items := []SMetadataItem{}
  407. items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT, Value: cloudinit.CLOUD_SHELL_HEADER + "\nrm -rf /root/.ssh/authorized_keys"})
  408. instance.Refresh()
  409. for _, item := range instance.Metadata.Items {
  410. if item.Key != METADATA_STARTUP_SCRIPT {
  411. items = append(items, item)
  412. }
  413. }
  414. instance.Metadata.Items = items
  415. return instance.host.zone.region.SetMetadata(instance.SelfLink, instance.Metadata)
  416. }
  417. return nil
  418. }
  419. func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
  420. return instance.host.zone.region.ChangeInstanceConfig(instance.SelfLink, instance.host.zone.Name, config.InstanceType, config.Cpu, config.MemoryMB)
  421. }
  422. func (instance *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  423. return nil, cloudprovider.ErrNotImplemented
  424. }
  425. func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error {
  426. return instance.host.zone.region.AttachDisk(instance.SelfLink, diskId, false)
  427. }
  428. func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error {
  429. _disk, err := instance.host.zone.region.GetDisk(diskId)
  430. if err != nil {
  431. if errors.Cause(err) == cloudprovider.ErrNotFound {
  432. return nil
  433. }
  434. return errors.Wrapf(err, "GetDisk(%s)", diskId)
  435. }
  436. for _, disk := range instance.Disks {
  437. if disk.Source == _disk.SelfLink {
  438. return instance.host.zone.region.DetachDisk(instance.SelfLink, disk.DeviceName)
  439. }
  440. }
  441. return nil
  442. }
  443. func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
  444. return cloudprovider.ErrNotSupported
  445. }
  446. func (instance *SInstance) GetError() error {
  447. return nil
  448. }
  449. func getDiskInfo(disk string) (cloudprovider.SDiskInfo, error) {
  450. result := cloudprovider.SDiskInfo{}
  451. diskInfo := strings.Split(disk, ":")
  452. for _, d := range diskInfo {
  453. if utils.IsInStringArray(d, []string{
  454. api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_LOCAL_SSD, api.STORAGE_GOOGLE_PD_BALANCED, api.STORAGE_GOOGLE_PD_EXTREME,
  455. api.STORAGE_GOOGLE_HYPERDISK_THROUGHPUT, api.STORAGE_GOOGLE_HYPERDISK_ML, api.STORAGE_GOOGLE_HYPERDISK_BALANCED, api.STORAGE_GOOGLE_HYPERDISK_EXTREME}) {
  456. result.StorageType = d
  457. } else if memSize, err := fileutils.GetSizeMb(d, 'M', 1024); err == nil {
  458. result.SizeGB = memSize >> 10
  459. } else {
  460. result.Name = d
  461. }
  462. }
  463. if len(result.StorageType) == 0 {
  464. result.StorageType = api.STORAGE_GOOGLE_PD_STANDARD
  465. }
  466. if result.SizeGB == 0 {
  467. return result, fmt.Errorf("missing disk size")
  468. }
  469. return result, nil
  470. }
  471. func (region *SRegion) CreateInstance(zone, name, desc, instanceType string, cpu, memoryMb int, networkId string, ipAddr, imageId string, disks []string) (*SInstance, error) {
  472. if len(instanceType) == 0 && (cpu == 0 || memoryMb == 0) {
  473. return nil, fmt.Errorf("missing instanceType or cpu &memory info")
  474. }
  475. if len(disks) == 0 {
  476. return nil, fmt.Errorf("missing disk info")
  477. }
  478. sysDisk, err := getDiskInfo(disks[0])
  479. if err != nil {
  480. return nil, errors.Wrap(err, "getDiskInfo.sys")
  481. }
  482. dataDisks := []cloudprovider.SDiskInfo{}
  483. for _, d := range disks[1:] {
  484. dataDisk, err := getDiskInfo(d)
  485. if err != nil {
  486. return nil, errors.Wrapf(err, "getDiskInfo(%s)", d)
  487. }
  488. dataDisks = append(dataDisks, dataDisk)
  489. }
  490. conf := &cloudprovider.SManagedVMCreateConfig{
  491. Name: name,
  492. Description: desc,
  493. ExternalImageId: imageId,
  494. Cpu: cpu,
  495. MemoryMB: memoryMb,
  496. ExternalNetworkId: networkId,
  497. IpAddr: ipAddr,
  498. SysDisk: sysDisk,
  499. DataDisks: dataDisks,
  500. }
  501. return region._createVM(zone, conf)
  502. }
  503. func (region *SRegion) _createVM(zone string, desc *cloudprovider.SManagedVMCreateConfig) (*SInstance, error) {
  504. if len(desc.InstanceType) == 0 {
  505. desc.InstanceType = fmt.Sprintf("custom-%d-%d", desc.Cpu, desc.MemoryMB)
  506. }
  507. disks := []map[string]interface{}{}
  508. if len(desc.SysDisk.Name) == 0 {
  509. desc.SysDisk.Name = fmt.Sprintf("vdisk-%s-%d", desc.Name, time.Now().UnixNano())
  510. }
  511. labels := map[string]string{}
  512. for k, v := range desc.Tags {
  513. labels[encode.EncodeGoogleLabel(k)] = encode.EncodeGoogleLabel(v)
  514. }
  515. disks = append(disks, map[string]interface{}{
  516. "boot": true,
  517. "initializeParams": map[string]interface{}{
  518. "diskName": normalizeString(desc.SysDisk.Name),
  519. "sourceImage": desc.ExternalImageId,
  520. "diskSizeGb": desc.SysDisk.SizeGB,
  521. "diskType": fmt.Sprintf("zones/%s/diskTypes/%s", zone, desc.SysDisk.StorageType),
  522. "labels": labels,
  523. },
  524. "autoDelete": true,
  525. })
  526. for _, disk := range desc.DataDisks {
  527. if len(disk.Name) == 0 {
  528. disk.Name = fmt.Sprintf("vdisk-%s-%d", desc.Name, time.Now().UnixNano())
  529. }
  530. disks = append(disks, map[string]interface{}{
  531. "boot": false,
  532. "initializeParams": map[string]interface{}{
  533. "diskName": normalizeString(disk.Name),
  534. "diskSizeGb": disk.SizeGB,
  535. "diskType": fmt.Sprintf("zones/%s/diskTypes/%s", zone, disk.StorageType),
  536. "labels": labels,
  537. },
  538. "autoDelete": true,
  539. })
  540. }
  541. networkInterface := map[string]string{
  542. "subnetwork": desc.ExternalNetworkId,
  543. }
  544. if !strings.HasPrefix(desc.ExternalNetworkId, "projects/") {
  545. vpc, err := region.GetVpc(desc.ExternalNetworkId)
  546. if err != nil {
  547. return nil, errors.Wrap(err, "region.GetNetwork")
  548. }
  549. networkInterface["subnetwork"] = getGlobalId(vpc.SelfLink)
  550. }
  551. if len(desc.IpAddr) > 0 {
  552. networkInterface["networkIp"] = desc.IpAddr
  553. }
  554. params := map[string]interface{}{
  555. "name": normalizeString(desc.NameEn),
  556. "description": desc.Description,
  557. "machineType": fmt.Sprintf("zones/%s/machineTypes/%s", zone, desc.InstanceType),
  558. "networkInterfaces": []map[string]string{
  559. networkInterface,
  560. },
  561. "disks": disks,
  562. }
  563. if len(labels) > 0 {
  564. params["labels"] = labels
  565. }
  566. if len(desc.ExternalSecgroupIds) > 0 {
  567. params["tags"] = map[string][]string{
  568. "items": desc.ExternalSecgroupIds,
  569. }
  570. }
  571. if len(desc.UserData) > 0 {
  572. params["metadata"] = map[string]interface{}{
  573. "items": []struct {
  574. Key string
  575. Value string
  576. }{
  577. {
  578. Key: METADATA_STARTUP_SCRIPT,
  579. Value: desc.UserData,
  580. },
  581. {
  582. Key: METADATA_POWER_SHELL,
  583. Value: desc.UserData,
  584. },
  585. },
  586. }
  587. }
  588. log.Debugf("create google instance params: %s", jsonutils.Marshal(params).String())
  589. instance := &SInstance{}
  590. resource := fmt.Sprintf("zones/%s/instances", zone)
  591. err := region.Insert(resource, jsonutils.Marshal(params), instance)
  592. if err != nil {
  593. return nil, err
  594. }
  595. return instance, nil
  596. }
  597. func (region *SRegion) StartInstance(id string) error {
  598. params := map[string]string{}
  599. return region.Do(id, "start", nil, jsonutils.Marshal(params))
  600. }
  601. func (region *SRegion) StopInstance(id string) error {
  602. params := map[string]string{}
  603. return region.Do(id, "stop", nil, jsonutils.Marshal(params))
  604. }
  605. func (region *SRegion) ResetInstance(id string) error {
  606. params := map[string]string{}
  607. return region.Do(id, "reset", nil, jsonutils.Marshal(params))
  608. }
  609. func (region *SRegion) DisableDeletionProtection(id string) error {
  610. params := map[string]string{
  611. "requestId": stringutils.UUID4(),
  612. "deletionProtection": "false",
  613. }
  614. return region.Do(id, "setDeletionProtection", params, nil)
  615. }
  616. func (region *SRegion) DetachDisk(instanceId, deviceName string) error {
  617. body := map[string]string{}
  618. params := map[string]string{"deviceName": deviceName}
  619. return region.Do(instanceId, "detachDisk", params, jsonutils.Marshal(body))
  620. }
  621. func (instance *SInstance) GetSerialOutput(port int) (string, error) {
  622. return instance.host.zone.region.GetSerialPortOutput(instance.SelfLink, port)
  623. }
  624. func (region *SRegion) GetSerialPortOutput(id string, port int) (string, error) {
  625. _content, content, next := "", "", 0
  626. var err error = nil
  627. for {
  628. _content, next, err = region.getSerialPortOutput(id, port, next)
  629. if err != nil {
  630. return content, err
  631. }
  632. content += _content
  633. if len(_content) == 0 {
  634. break
  635. }
  636. }
  637. return content, nil
  638. }
  639. func (region *SRegion) getSerialPortOutput(id string, port int, start int) (string, int, error) {
  640. resource := fmt.Sprintf("%s/serialPort?port=%d&start=%d", id, port, start)
  641. result := struct {
  642. Contents string
  643. Start int
  644. Next int
  645. }{}
  646. err := region.GetBySelfId(resource, &result)
  647. if err != nil {
  648. return "", result.Next, errors.Wrap(err, "")
  649. }
  650. return result.Contents, result.Next, nil
  651. }
  652. func (self *SRegion) AttachDisk(instanceId, diskId string, boot bool) error {
  653. disk, err := self.GetDisk(diskId)
  654. if err != nil {
  655. return errors.Wrapf(err, "GetDisk(%s)", diskId)
  656. }
  657. body := map[string]interface{}{
  658. "source": disk.SelfLink,
  659. "boot": boot,
  660. }
  661. if boot {
  662. body["autoDelete"] = true
  663. }
  664. params := map[string]string{}
  665. return self.Do(instanceId, "attachDisk", params, jsonutils.Marshal(body))
  666. }
  667. func (region *SRegion) ChangeInstanceConfig(id string, zone string, instanceType string, cpu int, memoryMb int) error {
  668. if len(instanceType) == 0 {
  669. instanceType = fmt.Sprintf("custom-%d-%d", cpu, memoryMb)
  670. }
  671. params := map[string]string{
  672. "machineType": fmt.Sprintf("zones/%s/machineTypes/%s", zone, instanceType),
  673. }
  674. return region.Do(id, "setMachineType", nil, jsonutils.Marshal(params))
  675. }
  676. func (region *SRegion) SetMetadata(id string, metadata SMetadata) error {
  677. return region.Do(id, "setMetadata", nil, jsonutils.Marshal(metadata))
  678. }
  679. func (region *SRegion) SetResourceTags(id string, tags SInstanceTag) error {
  680. return region.Do(id, "setTags", nil, jsonutils.Marshal(tags))
  681. }
  682. func (region *SRegion) SetServiceAccount(id string, email string) error {
  683. body := map[string]interface{}{
  684. "email": email,
  685. "scopes": []string{
  686. "https://www.googleapis.com/auth/devstorage.read_only",
  687. "https://www.googleapis.com/auth/logging.write",
  688. "https://www.googleapis.com/auth/monitoring.write",
  689. "https://www.googleapis.com/auth/servicecontrol",
  690. "https://www.googleapis.com/auth/service.management.readonly",
  691. "https://www.googleapis.com/auth/trace.append",
  692. },
  693. }
  694. return region.Do(id, "setsetServiceAccount", nil, jsonutils.Marshal(body))
  695. }
  696. func (region *SRegion) RebuildRoot(instanceId string, imageId string, sysDiskSizeGb int) (string, error) {
  697. oldDisk, diskType, deviceName := "", api.STORAGE_GOOGLE_PD_STANDARD, ""
  698. instance, err := region.GetInstance(instanceId)
  699. if err != nil {
  700. return "", errors.Wrap(err, "region.GetInstance")
  701. }
  702. for _, disk := range instance.Disks {
  703. if disk.Boot {
  704. oldDisk = disk.Source
  705. deviceName = disk.DeviceName
  706. break
  707. }
  708. }
  709. if len(oldDisk) > 0 {
  710. disk := &SDisk{}
  711. err := region.GetBySelfId(oldDisk, disk)
  712. if err != nil {
  713. return "", errors.Wrap(err, "region.GetDisk")
  714. }
  715. diskType = disk.Type
  716. if sysDiskSizeGb == 0 {
  717. sysDiskSizeGb = disk.SizeGB
  718. }
  719. }
  720. image, err := region.GetImage(imageId)
  721. if err != nil {
  722. return "", errors.Wrapf(err, "GetImage")
  723. }
  724. if image.DiskSizeGb > sysDiskSizeGb {
  725. sysDiskSizeGb = image.DiskSizeGb
  726. }
  727. zone, err := region.GetZone(instance.Zone)
  728. if err != nil {
  729. return "", errors.Wrap(err, "region.GetZone")
  730. }
  731. diskName := fmt.Sprintf("vdisk-%s-%d", instance.Name, time.Now().UnixNano())
  732. disk, err := region.CreateDisk(zone.Name, diskType, &cloudprovider.DiskCreateConfig{
  733. Name: diskName,
  734. SizeGb: sysDiskSizeGb,
  735. ImageId: imageId,
  736. Desc: "create for replace instance system disk",
  737. })
  738. if err != nil {
  739. return "", errors.Wrap(err, "region.CreateDisk.systemDisk")
  740. }
  741. if len(deviceName) > 0 {
  742. err = region.DetachDisk(instance.SelfLink, deviceName)
  743. if err != nil {
  744. defer region.Delete(disk.SelfLink)
  745. return "", errors.Wrap(err, "region.DetachDisk")
  746. }
  747. }
  748. err = region.AttachDisk(instance.SelfLink, disk.Id, true)
  749. if err != nil {
  750. if len(oldDisk) > 0 {
  751. defer region.AttachDisk(instance.SelfLink, oldDisk, true)
  752. }
  753. defer region.Delete(disk.SelfLink)
  754. return "", errors.Wrap(err, "region.AttachDisk.newSystemDisk")
  755. }
  756. if len(oldDisk) > 0 {
  757. defer region.Delete(oldDisk)
  758. }
  759. return disk.GetGlobalId(), nil
  760. }
  761. func (self *SRegion) SaveImage(diskId string, opts *cloudprovider.SaveImageOptions) (*SImage, error) {
  762. params := map[string]interface{}{
  763. "name": normalizeString(opts.Name),
  764. "description": opts.Notes,
  765. "sourceDisk": diskId,
  766. }
  767. image := &SImage{}
  768. err := self.Insert("global/images", jsonutils.Marshal(params), image)
  769. if err != nil {
  770. return nil, errors.Wrapf(err, "Insert")
  771. }
  772. image.storagecache = self.getStoragecache()
  773. return image, nil
  774. }
  775. func (self *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
  776. for i := range self.Disks {
  777. if self.Disks[0].Index == 0 {
  778. image, err := self.host.zone.region.SaveImage(self.Disks[i].Source, opts)
  779. if err != nil {
  780. return nil, errors.Wrapf(err, "SaveImage")
  781. }
  782. return image, nil
  783. }
  784. }
  785. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "no valid system disk found")
  786. }
  787. func (region *SRegion) SetLabels(id string, _labels map[string]string, labelFingerprint string) error {
  788. labels := map[string]string{}
  789. for k, v := range _labels {
  790. labels[encode.EncodeGoogleLabel(k)] = encode.EncodeGoogleLabel(v)
  791. }
  792. params := map[string]interface{}{
  793. "labels": labels,
  794. "labelFingerprint": labelFingerprint,
  795. }
  796. err := region.Do(id, "setLabels", nil, jsonutils.Marshal(params))
  797. if err != nil {
  798. return errors.Wrapf(err, `region.Do(%s, "setLabels", nil, %s)`, id, jsonutils.Marshal(params).String())
  799. }
  800. return nil
  801. }
  802. func (self *SInstance) SetTags(tags map[string]string, replace bool) error {
  803. if !replace {
  804. oldTags, _ := self.GetTags()
  805. for k, v := range oldTags {
  806. if _, ok := tags[k]; !ok {
  807. tags[k] = v
  808. }
  809. }
  810. }
  811. err := self.Refresh()
  812. if err != nil {
  813. return errors.Wrap(err, "self.Refresh()")
  814. }
  815. err = self.host.zone.region.SetLabels(self.SelfLink, tags, self.LabelFingerprint)
  816. if err != nil {
  817. return errors.Wrapf(err, ` self.host.zone.region.SsetLabels()`)
  818. }
  819. return nil
  820. }