instance.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. // Copyright 2023 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 volcengine
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "time"
  20. billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
  21. api "yunion.io/x/cloudmux/pkg/apis/compute"
  22. "yunion.io/x/cloudmux/pkg/cloudprovider"
  23. "yunion.io/x/cloudmux/pkg/multicloud"
  24. "yunion.io/x/jsonutils"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/util/billing"
  27. "yunion.io/x/pkg/util/imagetools"
  28. "yunion.io/x/pkg/util/osprofile"
  29. "yunion.io/x/pkg/utils"
  30. )
  31. const (
  32. InstanceStatusCreating = "CREATING"
  33. InstanceStatusRunning = "RUNNING"
  34. InstanceStatusStopping = "STOPPING"
  35. InstanceStatusStopped = "STOPPED"
  36. InstanceStatusRebooting = "REBOOTING"
  37. InstanceStatusStarting = "STARTING"
  38. InstanceStatusRebuilding = "REBUILDING"
  39. InstanceStatusResizing = "RESIZING"
  40. InstanceStatusError = "ERROR"
  41. InstanceStatusDeleting = "DELETING"
  42. )
  43. type SSecurityGroupIds []string
  44. type SRdmaIPAddress []string
  45. type SInstance struct {
  46. multicloud.SInstanceBase
  47. VolcEngineTags
  48. host *SHost
  49. osInfo *imagetools.ImageInfo
  50. CreatedAt time.Time
  51. UpdatedAt time.Time
  52. InstanceId string
  53. ZoneId string
  54. ImageId string
  55. Status string
  56. InstanceName string
  57. Description string
  58. Hostname string
  59. VpcId string
  60. InstanceTypeId string
  61. Cpus int
  62. MemorySize int
  63. OsName string
  64. OsType string
  65. NetworkInterfaces []SNetworkInterface
  66. RdmaIpAddress SRdmaIPAddress
  67. KeyPairName string
  68. KeyPairId string
  69. InstanceChargeType string
  70. StoppedMode string
  71. SpotStrategy string
  72. DeploymentSetId string
  73. EipAddress SEipAddress
  74. ExpiredAt time.Time
  75. Uuid string
  76. ProjectName string
  77. }
  78. func billingCycle2Params(bc *billing.SBillingCycle, params map[string]string) error {
  79. if bc.GetMonths() > 0 {
  80. params["PeriodUnit"] = "Month"
  81. params["Period"] = fmt.Sprintf("%d", bc.GetMonths())
  82. } else if bc.GetWeeks() > 0 {
  83. params["PeriodUnit"] = "Week"
  84. params["Period"] = fmt.Sprintf("%d", bc.GetWeeks())
  85. // renew by week is not currently supported
  86. return fmt.Errorf("invalid renew time period %s", bc.String())
  87. } else {
  88. return fmt.Errorf("invalid renew time period %s", bc.String())
  89. }
  90. return nil
  91. }
  92. func (instance *SInstance) UpdatePassword(passwd string) error {
  93. params := make(map[string]string)
  94. params["Password"] = passwd
  95. return instance.host.zone.region.modifyInstanceAttribute(instance.InstanceId, params)
  96. }
  97. func (instance *SInstance) UpdateUserData(userData string) error {
  98. params := make(map[string]string)
  99. params["UserData"] = userData
  100. return instance.host.zone.region.modifyInstanceAttribute(instance.InstanceId, params)
  101. }
  102. func (instance *SInstance) GetUserData() (string, error) {
  103. params := make(map[string]string)
  104. params["InstanceId"] = instance.InstanceId
  105. body, err := instance.host.zone.region.ecsRequest("DescribeUserData", params)
  106. if err != nil {
  107. return "", errors.Wrapf(err, "GetUserData")
  108. }
  109. userData, err := body.GetString("UserData")
  110. if err != nil {
  111. return "", errors.Wrapf(err, "GetUserData")
  112. }
  113. return userData, nil
  114. }
  115. func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) {
  116. instances, err := region.GetInstances("", []string{instanceId})
  117. if err != nil {
  118. return nil, err
  119. }
  120. for i := range instances {
  121. if instances[i].InstanceId == instanceId {
  122. return &instances[i], nil
  123. }
  124. }
  125. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", instanceId)
  126. }
  127. func (region *SRegion) GetInstances(zoneId string, ids []string) ([]SInstance, error) {
  128. params := make(map[string]string)
  129. params["MaxResults"] = "100"
  130. if len(zoneId) > 0 {
  131. params["ZoneId"] = zoneId
  132. }
  133. for index, id := range ids {
  134. key := fmt.Sprintf("InstanceIds.%d", index+1)
  135. params[key] = id
  136. }
  137. ret := []SInstance{}
  138. for {
  139. resp, err := region.ecsRequest("DescribeInstances", params)
  140. if err != nil {
  141. return nil, errors.Wrapf(err, "DescribeInstances")
  142. }
  143. part := struct {
  144. Instances []SInstance
  145. NextToken string
  146. }{}
  147. err = resp.Unmarshal(&part)
  148. if err != nil {
  149. return nil, err
  150. }
  151. ret = append(ret, part.Instances...)
  152. if len(part.NextToken) == 0 || len(part.Instances) == 0 {
  153. break
  154. }
  155. params["NextToken"] = part.NextToken
  156. }
  157. return ret, nil
  158. }
  159. func (instance *SInstance) GetIHost() cloudprovider.ICloudHost {
  160. return instance.host
  161. }
  162. func (instance *SInstance) GetIHostId() string {
  163. return instance.host.GetGlobalId()
  164. }
  165. func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
  166. disks, err := instance.host.zone.region.GetDisks(instance.InstanceId, "", "", nil)
  167. if err != nil {
  168. return nil, err
  169. }
  170. idisks := make([]cloudprovider.ICloudDisk, len(disks))
  171. for i := 0; i < len(disks); i += 1 {
  172. storage := &SStorage{zone: instance.host.zone, storageType: disks[i].VolumeType}
  173. disks[i].storage = storage
  174. idisks[i] = &disks[i]
  175. }
  176. return idisks, nil
  177. }
  178. func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
  179. if len(instance.EipAddress.AllocationId) > 0 {
  180. return instance.host.zone.region.GetEip(instance.EipAddress.AllocationId)
  181. }
  182. for _, nic := range instance.NetworkInterfaces {
  183. if len(nic.AssociatedElasticIp.EipAddress) > 0 {
  184. eip := SEipAddress{region: instance.host.zone.region}
  185. eip.region = instance.host.zone.region
  186. eip.EipAddress = nic.AssociatedElasticIp.EipAddress
  187. eip.InstanceId = instance.InstanceId
  188. eip.AllocationId = instance.InstanceId
  189. return &eip, nil
  190. }
  191. }
  192. return nil, cloudprovider.ErrNotFound
  193. }
  194. func (instance *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
  195. ret := []cloudprovider.ICloudNic{}
  196. nics, err := instance.host.zone.region.GetNetworkInterfaces("", instance.InstanceId)
  197. if err != nil {
  198. return nil, err
  199. }
  200. for i := range nics {
  201. nics[i].region = instance.host.zone.region
  202. ret = append(ret, &nics[i])
  203. }
  204. return ret, nil
  205. }
  206. func (instance *SInstance) GetId() string {
  207. return instance.InstanceId
  208. }
  209. func (instance *SInstance) GetName() string {
  210. if len(instance.InstanceName) > 0 {
  211. return instance.InstanceName
  212. }
  213. return instance.InstanceId
  214. }
  215. func (instance *SInstance) GetHostname() string {
  216. return instance.Hostname
  217. }
  218. func (instance *SInstance) GetGlobalId() string {
  219. return instance.InstanceId
  220. }
  221. func (instance *SInstance) GetInstanceType() string {
  222. return instance.InstanceTypeId
  223. }
  224. func (instance *SInstance) SetTags(tags map[string]string, replace bool) error {
  225. _tags, err := instance.GetTags()
  226. if err != nil {
  227. return errors.Wrapf(err, "GetTags")
  228. }
  229. keys, upperKeys := []string{}, []string{}
  230. for k := range tags {
  231. keys = append(keys, k)
  232. upperKeys = append(upperKeys, strings.ToUpper(k))
  233. }
  234. if replace {
  235. if len(tags) > 0 {
  236. removeKeys := []string{}
  237. for k := range _tags {
  238. if !utils.IsInStringArray(k, keys) {
  239. removeKeys = append(removeKeys, k)
  240. }
  241. }
  242. if len(removeKeys) > 0 {
  243. err := instance.host.zone.region.UntagResources(instance.InstanceId, "instance", removeKeys)
  244. if err != nil {
  245. return errors.Wrapf(err, "UntagResource")
  246. }
  247. }
  248. }
  249. } else {
  250. removeKeys := []string{}
  251. for k := range _tags {
  252. if !utils.IsInStringArray(k, keys) && utils.IsInStringArray(strings.ToUpper(k), upperKeys) {
  253. removeKeys = append(removeKeys, k)
  254. }
  255. }
  256. if len(removeKeys) > 0 {
  257. err := instance.host.zone.region.UntagResources(instance.InstanceId, "instance", removeKeys)
  258. if err != nil {
  259. return errors.Wrapf(err, "UntagResource")
  260. }
  261. }
  262. }
  263. return instance.host.zone.region.TagResources(instance.InstanceId, "instance", tags)
  264. }
  265. func (region *SRegion) TagResources(resId, resType string, tags map[string]string) error {
  266. params := map[string]string{
  267. "ResourceIds.1": resId,
  268. "ResourceType": resType,
  269. }
  270. idx := 1
  271. for k, v := range tags {
  272. params[fmt.Sprintf("Tags.%d.Key", idx)] = k
  273. params[fmt.Sprintf("Tags.%d.Value", idx)] = v
  274. idx++
  275. }
  276. _, err := region.ecsRequest("TagResources", params)
  277. if err != nil {
  278. return errors.Wrapf(err, "TagResources")
  279. }
  280. return nil
  281. }
  282. func (region *SRegion) UntagResources(resId, resType string, tags []string) error {
  283. params := map[string]string{
  284. "ResourceIds.1": resId,
  285. "ResourceType": resType,
  286. }
  287. idx := 1
  288. for _, tag := range tags {
  289. params[fmt.Sprintf("TagKeys.%d", idx)] = tag
  290. idx++
  291. }
  292. _, err := region.ecsRequest("UntagResources", params)
  293. if err != nil {
  294. return errors.Wrapf(err, "UntagResources")
  295. }
  296. return nil
  297. }
  298. func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
  299. nics, err := instance.host.zone.region.GetNetworkInterfaces("", instance.InstanceId)
  300. if err != nil {
  301. return nil, err
  302. }
  303. for _, nic := range nics {
  304. if len(nic.SecurityGroupIds) > 0 {
  305. return nic.SecurityGroupIds, nil
  306. }
  307. }
  308. return []string{}, nil
  309. }
  310. func (instance *SInstance) GetVcpuCount() int {
  311. return instance.Cpus
  312. }
  313. func (instance *SInstance) GetVmemSizeMB() int {
  314. return instance.MemorySize
  315. }
  316. func (instance *SInstance) GetBootOrder() string {
  317. return "dcn"
  318. }
  319. func (instance *SInstance) GetVga() string {
  320. return "std"
  321. }
  322. func (instance *SInstance) GetVdi() string {
  323. return "vnc"
  324. }
  325. func (ins *SInstance) getNormalizedOsInfo() *imagetools.ImageInfo {
  326. if ins.osInfo == nil {
  327. osInfo := imagetools.NormalizeImageInfo(ins.OsName, "", ins.OsType, "", "")
  328. ins.osInfo = &osInfo
  329. }
  330. return ins.osInfo
  331. }
  332. func (instance *SInstance) GetOsType() cloudprovider.TOsType {
  333. return cloudprovider.TOsType(osprofile.NormalizeOSType(instance.OsType))
  334. }
  335. func (instance *SInstance) GetFullOsName() string {
  336. return instance.OsName
  337. }
  338. func (ins *SInstance) GetBios() cloudprovider.TBiosType {
  339. return cloudprovider.ToBiosType(ins.getNormalizedOsInfo().OsBios)
  340. }
  341. func (ins *SInstance) GetOsArch() string {
  342. return ins.getNormalizedOsInfo().OsArch
  343. }
  344. func (ins *SInstance) GetOsDist() string {
  345. return ins.getNormalizedOsInfo().OsDistro
  346. }
  347. func (ins *SInstance) GetOsVersion() string {
  348. return ins.getNormalizedOsInfo().OsVersion
  349. }
  350. func (ins *SInstance) GetOsLang() string {
  351. return ins.getNormalizedOsInfo().OsLang
  352. }
  353. func (instance *SInstance) GetMachine() string {
  354. return "pc"
  355. }
  356. func (instance *SInstance) GetStatus() string {
  357. switch instance.Status {
  358. case InstanceStatusRunning:
  359. return api.VM_RUNNING
  360. case InstanceStatusStarting:
  361. return api.VM_STARTING
  362. case InstanceStatusStopping:
  363. return api.VM_STOPPING
  364. case InstanceStatusStopped:
  365. return api.VM_READY
  366. case InstanceStatusDeleting:
  367. return api.VM_DELETING
  368. default:
  369. return api.VM_UNKNOWN
  370. }
  371. }
  372. func (instance *SInstance) Refresh() error {
  373. ins, err := instance.host.zone.region.GetInstance(instance.InstanceId)
  374. if err != nil {
  375. return err
  376. }
  377. return jsonutils.Update(instance, ins)
  378. }
  379. func (instance *SInstance) GetHypervisor() string {
  380. return api.HYPERVISOR_VOLCENGINE
  381. }
  382. func (instance *SInstance) GetCreatedAt() time.Time {
  383. return instance.CreatedAt
  384. }
  385. func (instance *SInstance) GetExpiredAt() time.Time {
  386. if instance.InstanceChargeType != "PostPaid" {
  387. return instance.ExpiredAt
  388. }
  389. return time.Time{}
  390. }
  391. func (instance *SInstance) GetBillingType() string {
  392. if instance.InstanceChargeType == "PostPaid" {
  393. return billing_api.BILLING_TYPE_POSTPAID
  394. }
  395. return billing_api.BILLING_TYPE_PREPAID
  396. }
  397. func (instance *SInstance) SetSecurityGroups(secgroupIds []string) error {
  398. for _, nic := range instance.NetworkInterfaces {
  399. return instance.host.zone.region.ModifyNetworkInterfaceAttributes(nic.NetworkInterfaceId, secgroupIds)
  400. }
  401. return nil
  402. }
  403. func (self *SRegion) ModifyNetworkInterfaceAttributes(id string, secgroupIds []string) error {
  404. params := map[string]string{
  405. "NetworkInterfaceId": id,
  406. }
  407. for i, id := range secgroupIds {
  408. params[fmt.Sprintf("SecurityGroupIds.%d", i+1)] = id
  409. }
  410. _, err := self.vpcRequest("ModifyNetworkInterfaceAttributes", params)
  411. return err
  412. }
  413. func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
  414. if config.InstanceType == "nil" {
  415. return errors.Wrapf(cloudprovider.ErrInputParameter, "InstanceType")
  416. }
  417. return instance.host.zone.region.ChangeConfig(instance.InstanceId, config.InstanceType)
  418. }
  419. func (instance *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  420. url, err := instance.host.zone.region.DescribeInstanceVncUrl(instance.InstanceId)
  421. if err != nil {
  422. return nil, err
  423. }
  424. protocol := api.HYPERVISOR_VOLCENGINE
  425. ret := &cloudprovider.ServerVncOutput{
  426. Url: strings.TrimPrefix(url, "wss://"),
  427. Protocol: protocol,
  428. InstanceId: instance.InstanceId,
  429. Region: instance.host.zone.region.RegionId,
  430. InstanceName: instance.InstanceName,
  431. Hypervisor: api.HYPERVISOR_VOLCENGINE,
  432. }
  433. return ret, nil
  434. }
  435. func (self *SRegion) DescribeInstanceVncUrl(id string) (string, error) {
  436. params := map[string]string{
  437. "InstanceId": id,
  438. }
  439. resp, err := self.ecsRequest("DescribeInstanceVncUrl", params)
  440. if err != nil {
  441. return "", err
  442. }
  443. return resp.GetString("VncUrl")
  444. }
  445. func (instance *SInstance) StartVM(ctx context.Context) error {
  446. err := instance.host.zone.region.StartVM(instance.InstanceId)
  447. return err
  448. }
  449. func (instance *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
  450. err := instance.host.zone.region.StopVM(instance.InstanceId, opts.IsForce, opts.StopCharging)
  451. if err != nil {
  452. return err
  453. }
  454. return cloudprovider.WaitStatus(instance, api.VM_READY, 10*time.Second, 300*time.Second)
  455. }
  456. func (instance *SInstance) DeleteVM(ctx context.Context) error {
  457. return instance.host.zone.region.DeleteVM(instance.InstanceId)
  458. }
  459. func (instance *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
  460. return instance.host.zone.region.UpdateVM(instance.InstanceId, input.NAME, input.Description)
  461. }
  462. func (instance *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
  463. return instance.host.zone.region.DeployVM(instance.InstanceId, opts)
  464. }
  465. func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error {
  466. return instance.host.zone.region.AttachDisk(instance.InstanceId, diskId)
  467. }
  468. func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error {
  469. return cloudprovider.RetryOnError(
  470. func() error {
  471. return instance.host.zone.region.DetachDisk(instance.InstanceId, diskId)
  472. },
  473. []string{
  474. `"Code":"InvalidOperation.Conflict"`,
  475. },
  476. 4)
  477. }
  478. func (instance *SInstance) GetProjectId() string {
  479. return instance.ProjectName
  480. }
  481. func (instance *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
  482. return "", cloudprovider.ErrNotSupported
  483. }
  484. func (instance *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
  485. image, err := instance.host.zone.region.SaveImage(instance.InstanceId, opts)
  486. if err != nil {
  487. return nil, errors.Wrapf(err, "SaveImage %s", opts.Name)
  488. }
  489. return image, nil
  490. }
  491. // region
  492. func (region *SRegion) CreateInstance(zoneId string, opts *cloudprovider.SManagedVMCreateConfig) (string, error) {
  493. params := make(map[string]string)
  494. params["RegionId"] = region.RegionId
  495. params["ImageId"] = opts.ExternalImageId
  496. params["InstanceType"] = opts.InstanceType
  497. params["ZoneId"] = zoneId
  498. params["InstanceName"] = opts.Name
  499. if len(opts.ProjectId) > 0 {
  500. params["ProjectName"] = opts.ProjectId
  501. }
  502. if len(opts.Hostname) > 0 {
  503. params["HostName"] = opts.Hostname
  504. }
  505. params["Description"] = opts.Description
  506. if len(opts.Password) > 0 {
  507. params["Password"] = opts.Password
  508. }
  509. if len(opts.KeypairName) > 0 {
  510. params["KeyPairName"] = opts.KeypairName
  511. }
  512. if len(opts.Password) == 0 && len(opts.KeypairName) == 0 {
  513. params["KeepImageCredential"] = "True"
  514. }
  515. if len(opts.UserData) > 0 {
  516. params["UserData"] = opts.UserData
  517. }
  518. tagIdx := 1
  519. for k, v := range opts.Tags {
  520. params[fmt.Sprintf("Tags.%d.Key", tagIdx)] = k
  521. params[fmt.Sprintf("Tags.%d.Value", tagIdx)] = v
  522. tagIdx += 1
  523. }
  524. params["Volumes.1.Size"] = fmt.Sprintf("%d", opts.SysDisk.SizeGB)
  525. params["Volumes.1.VolumeType"] = opts.SysDisk.StorageType
  526. for idx, disk := range opts.DataDisks {
  527. params[fmt.Sprintf("Volumes.%d.Size", idx+2)] = fmt.Sprintf("%d", disk.SizeGB)
  528. params[fmt.Sprintf("Volumes.%d.VolumeType", idx+2)] = disk.StorageType
  529. }
  530. params["NetworkInterfaces.1.SubnetId"] = opts.ExternalNetworkId
  531. if len(opts.IpAddr) > 0 {
  532. //params["NetworkInterfaces.1.IpAddr"] = opts.IpAddr
  533. }
  534. for idx, id := range opts.ExternalSecgroupIds {
  535. params[fmt.Sprintf("NetworkInterfaces.1.SecurityGroupIds.%d", idx+1)] = id
  536. }
  537. params["InstanceChargeType"] = "PostPaid"
  538. params["SpotStrategy"] = "NoSpot"
  539. if opts.BillingCycle != nil {
  540. params["InstanceChargeType"] = "PrePaid"
  541. err := billingCycle2Params(opts.BillingCycle, params)
  542. if err != nil {
  543. return "", err
  544. }
  545. params["AutoRenew"] = "False"
  546. if opts.BillingCycle.AutoRenew {
  547. params["AutoRenew"] = "true"
  548. params["AutoRenewPeriod"] = "1"
  549. }
  550. }
  551. params["ClientToken"] = utils.GenRequestId(20)
  552. resp, err := region.ecsRequest("RunInstances", params)
  553. if err != nil {
  554. return "", errors.Wrapf(err, "RunInstances")
  555. }
  556. ids := []string{}
  557. err = resp.Unmarshal(&ids, "InstanceIds")
  558. if err != nil {
  559. return "", err
  560. }
  561. for _, id := range ids {
  562. return id, nil
  563. }
  564. return "", errors.Wrapf(cloudprovider.ErrNotFound, "after created")
  565. }
  566. func (region *SRegion) RenewInstance(instanceId string, bc billing.SBillingCycle) error {
  567. params := make(map[string]string)
  568. params["InstanceId"] = instanceId
  569. err := billingCycle2Params(&bc, params)
  570. if err != nil {
  571. return err
  572. }
  573. params["ClientToken"] = utils.GenRequestId(20)
  574. _, err = region.ecsRequest("RenewInstance", params)
  575. if err != nil {
  576. return errors.Wrapf(err, "RenewInstance fail")
  577. }
  578. return nil
  579. }
  580. func (region *SRegion) ChangeConfig(instanceId string, instanceTypeId string) error {
  581. params := make(map[string]string)
  582. params["InstanceTypeId"] = instanceTypeId
  583. return region.instanceOperation(instanceId, "ModifyInstanceSpec", params)
  584. }
  585. func (region *SRegion) StartVM(instanceId string) error {
  586. status, err := region.GetInstanceStatus(instanceId)
  587. if err != nil {
  588. return errors.Wrapf(err, "Fail to get instance status on StartVM")
  589. }
  590. if status != InstanceStatusStopped {
  591. return errors.Wrapf(cloudprovider.ErrInvalidStatus, "StartVM: vm status is %s expect %s", status, InstanceStatusStopped)
  592. }
  593. return region.doStartVM(instanceId)
  594. }
  595. func (region *SRegion) StopVM(instanceId string, isForce, stopCharging bool) error {
  596. status, err := region.GetInstanceStatus(instanceId)
  597. if err != nil {
  598. return errors.Wrapf(err, "Fail to get instance status on StopVM")
  599. }
  600. if status == InstanceStatusStopped {
  601. return nil
  602. }
  603. if status != InstanceStatusRunning {
  604. return errors.Wrapf(cloudprovider.ErrInvalidStatus, "StartVM: vm status is %s expect %s", status, InstanceStatusRunning)
  605. }
  606. return region.doStopVM(instanceId, isForce, stopCharging)
  607. }
  608. func (region *SRegion) DeleteVM(instanceId string) error {
  609. return region.instanceOperation(instanceId, "DeleteInstance", nil)
  610. }
  611. func (region *SRegion) doStartVM(instanceId string) error {
  612. return region.instanceOperation(instanceId, "StartInstance", nil)
  613. }
  614. func (region *SRegion) doStopVM(instanceId string, isForce, stopCharging bool) error {
  615. params := make(map[string]string)
  616. if isForce {
  617. params["ForceStop"] = "true"
  618. } else {
  619. params["ForceStop"] = "false"
  620. }
  621. params["StoppedMode"] = "KeepCharging"
  622. if stopCharging {
  623. params["StoppedMode"] = "StopCharging"
  624. }
  625. return region.instanceOperation(instanceId, "StopInstance", params)
  626. }
  627. func (region *SRegion) modifyInstanceAttribute(instanceId string, params map[string]string) error {
  628. return region.instanceOperation(instanceId, "ModifyInstanceAttribute", params)
  629. }
  630. func (region *SRegion) UpdateVM(instanceId string, name, description string) error {
  631. params := make(map[string]string)
  632. params["InstanceName"] = name
  633. params["Description"] = description
  634. return region.modifyInstanceAttribute(instanceId, params)
  635. }
  636. func (region *SRegion) DeployVM(instanceId string, opts *cloudprovider.SInstanceDeployOptions) error {
  637. instance, err := region.GetInstance(instanceId)
  638. if err != nil {
  639. return err
  640. }
  641. if opts.DeleteKeypair {
  642. err = region.DetachKeyPair(instanceId, instance.KeyPairName)
  643. if err != nil {
  644. return err
  645. }
  646. }
  647. if len(opts.PublicKey) > 0 {
  648. keypairName, err := instance.host.zone.region.syncKeypair(opts.PublicKey)
  649. if err != nil {
  650. return err
  651. }
  652. err = region.AttachKeypair(instanceId, keypairName)
  653. if err != nil {
  654. return err
  655. }
  656. }
  657. if len(opts.Password) > 0 {
  658. params := make(map[string]string)
  659. params["Password"] = opts.Password
  660. return region.modifyInstanceAttribute(instanceId, params)
  661. }
  662. return nil
  663. }
  664. func (region *SRegion) DetachDisk(instanceId string, diskId string) error {
  665. params := make(map[string]string)
  666. params["InstanceId"] = instanceId
  667. params["VolumeId"] = diskId
  668. _, err := region.storageRequest("DetachVolume", params)
  669. if err != nil {
  670. return errors.Wrap(err, "DetachDisk")
  671. }
  672. return nil
  673. }
  674. func (region *SRegion) AttachDisk(instanceId string, diskId string) error {
  675. params := make(map[string]string)
  676. params["InstanceId"] = instanceId
  677. params["VolumeId"] = diskId
  678. _, err := region.storageRequest("AttachVolume", params)
  679. if err != nil {
  680. return errors.Wrapf(err, "AttachDisk %s to %s fail", diskId, instanceId)
  681. }
  682. return nil
  683. }
  684. func (region *SRegion) SaveImage(instanceId string, opts *cloudprovider.SaveImageOptions) (*SImage, error) {
  685. params := map[string]string{
  686. "InstanceId": instanceId,
  687. "ImageName": opts.Name,
  688. "Description": opts.Notes,
  689. "ClientToken": utils.GenRequestId(20),
  690. }
  691. body, err := region.ecsRequest("CreateImage", params)
  692. if err != nil {
  693. return nil, errors.Wrapf(err, "CreateImage")
  694. }
  695. imageId, err := body.GetString("ImageId")
  696. if err != nil {
  697. return nil, errors.Wrapf(err, "get imageId")
  698. }
  699. cloudprovider.Wait(time.Second*3, time.Minute, func() (bool, error) {
  700. _, err := region.GetImage(imageId)
  701. if err != nil {
  702. if errors.Cause(err) == cloudprovider.ErrNotFound {
  703. return false, nil
  704. }
  705. return false, err
  706. }
  707. return true, nil
  708. })
  709. return region.GetImage(imageId)
  710. }