instance.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  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 zstack
  15. import (
  16. "context"
  17. "fmt"
  18. "net/url"
  19. "strings"
  20. "time"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/billing"
  25. "yunion.io/x/pkg/util/imagetools"
  26. "yunion.io/x/pkg/util/version"
  27. "yunion.io/x/pkg/utils"
  28. api "yunion.io/x/cloudmux/pkg/apis/compute"
  29. "yunion.io/x/cloudmux/pkg/cloudprovider"
  30. "yunion.io/x/cloudmux/pkg/multicloud"
  31. )
  32. type SInstanceCdrome struct {
  33. }
  34. type SInstance struct {
  35. multicloud.SInstanceBase
  36. ZStackTags
  37. host *SHost
  38. osInfo *imagetools.ImageInfo
  39. ZStackBasic
  40. ZoneUUID string `json:"zoneUuid"`
  41. ClusterUUID string `json:"clusterUuid"`
  42. HostUUID string `json:"hostUuid"`
  43. LastHostUUID string `json:"lastHostUuid"`
  44. RootVolumeUUID string `json:"rootVolumeUuid"`
  45. Platform string `json:"platform"`
  46. InstanceOfferingUUID string `json:"instanceOfferingUuid"`
  47. DefaultL3NetworkUUID string `json:"defaultL3NetworkUuid"`
  48. Type string `json:"type"`
  49. HypervisorType string `json:"hypervisorType"`
  50. MemorySize int `json:"memorySize"`
  51. CPUNum int `json:"cpuNum"`
  52. CPUSpeed int `json:"cpuSpeed"`
  53. State string `json:"state"`
  54. InternalID string `json:"internalId"`
  55. VMNics []SInstanceNic `json:"vmNics"`
  56. AllVolumes []SDisk `json:"allVolumes"`
  57. VMCdRoms []SInstanceCdrome `json:"vmCdRoms"`
  58. ZStackTime
  59. }
  60. func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) {
  61. instance := &SInstance{}
  62. err := region.client.getResource("vm-instances", instanceId, instance)
  63. if err != nil {
  64. return nil, err
  65. }
  66. if instance.State == "Destroyed" {
  67. return nil, cloudprovider.ErrNotFound
  68. }
  69. return instance, nil
  70. }
  71. func (region *SRegion) GetInstances(hostId string, instanceId string, nicId string) ([]SInstance, error) {
  72. instance := []SInstance{}
  73. params := url.Values{}
  74. params.Add("q", "type=UserVm")
  75. params.Add("q", "state!=Destroyed")
  76. if len(hostId) > 0 {
  77. params.Add("q", "lastHostUuid="+hostId)
  78. }
  79. if len(instanceId) > 0 {
  80. params.Add("q", "uuid="+instanceId)
  81. }
  82. if len(nicId) > 0 {
  83. params.Add("q", "vmNics.uuid="+nicId)
  84. }
  85. if SkipEsxi {
  86. params.Add("q", "hypervisorType!=ESX")
  87. }
  88. return instance, region.client.listAll("vm-instances", params, &instance)
  89. }
  90. func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
  91. ids := []string{}
  92. secgroups, err := instance.host.zone.region.GetSecurityGroups("", instance.UUID, "")
  93. if err != nil {
  94. return nil, err
  95. }
  96. for _, secgroup := range secgroups {
  97. ids = append(ids, secgroup.UUID)
  98. }
  99. return ids, nil
  100. }
  101. func (instance *SInstance) GetIHost() cloudprovider.ICloudHost {
  102. return instance.host
  103. }
  104. func (instance *SInstance) GetIHostId() string {
  105. if len(instance.LastHostUUID) > 0 {
  106. return instance.LastHostUUID
  107. }
  108. return instance.HostUUID
  109. }
  110. func (instance *SInstance) GetId() string {
  111. return instance.UUID
  112. }
  113. func (instance *SInstance) GetName() string {
  114. return instance.Name
  115. }
  116. func (instance *SInstance) GetHostname() string {
  117. return ""
  118. }
  119. func (instance *SInstance) GetGlobalId() string {
  120. return instance.GetId()
  121. }
  122. func (instance *SInstance) IsEmulated() bool {
  123. return false
  124. }
  125. func (instance *SInstance) GetInstanceType() string {
  126. if len(instance.InstanceOfferingUUID) > 0 {
  127. offer, err := instance.host.zone.region.GetInstanceOffering(instance.InstanceOfferingUUID)
  128. if err == nil {
  129. return offer.Name
  130. }
  131. }
  132. return instance.Type
  133. }
  134. func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
  135. idisks := []cloudprovider.ICloudDisk{}
  136. rootDisk, err := instance.host.zone.region.GetDiskWithStorage(instance.RootVolumeUUID)
  137. if err != nil {
  138. return nil, err
  139. }
  140. idisks = append(idisks, rootDisk)
  141. for i := 0; i < len(instance.AllVolumes); i++ {
  142. if instance.AllVolumes[i].UUID != instance.RootVolumeUUID {
  143. dataDisk, err := instance.host.zone.region.GetDiskWithStorage(instance.AllVolumes[i].UUID)
  144. if err != nil {
  145. return nil, err
  146. }
  147. idisks = append(idisks, dataDisk)
  148. }
  149. }
  150. return idisks, nil
  151. }
  152. func (instance *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
  153. iNics := []cloudprovider.ICloudNic{}
  154. for i := 0; i < len(instance.VMNics); i++ {
  155. instance.VMNics[i].instance = instance
  156. iNics = append(iNics, &instance.VMNics[i])
  157. }
  158. return iNics, nil
  159. }
  160. func (instance *SInstance) GetVcpuCount() int {
  161. return instance.CPUNum
  162. }
  163. func (instance *SInstance) GetVmemSizeMB() int {
  164. return instance.MemorySize / 1024 / 1024
  165. }
  166. func (instance *SInstance) GetBootOrder() string {
  167. return instance.host.zone.region.GetBootOrder(instance.UUID)
  168. }
  169. func (region *SRegion) GetBootOrder(instanceId string) string {
  170. resp, err := region.client.get("vm-instances", instanceId, "boot-orders")
  171. if err != nil {
  172. return "dcn"
  173. }
  174. orders := []string{}
  175. err = resp.Unmarshal(&orders, "orders")
  176. if err != nil {
  177. return "dcn"
  178. }
  179. order := ""
  180. for _, _order := range orders {
  181. switch _order {
  182. case "CdRom":
  183. order += "c"
  184. case "HardDisk":
  185. order += "d"
  186. default:
  187. log.Errorf("Unknown BootOrder %s for instance %s", _order, instanceId)
  188. }
  189. }
  190. return order
  191. }
  192. func (instance *SInstance) GetVga() string {
  193. return "std"
  194. }
  195. func (instance *SInstance) GetVdi() string {
  196. return "vnc"
  197. }
  198. func (instance *SInstance) getNormalizedOsInfo() *imagetools.ImageInfo {
  199. if instance.osInfo == nil {
  200. osInfo := imagetools.NormalizeImageInfo(instance.Platform, "", "", "", "")
  201. instance.osInfo = &osInfo
  202. }
  203. return instance.osInfo
  204. }
  205. func (instance *SInstance) GetOsType() cloudprovider.TOsType {
  206. return cloudprovider.TOsType(instance.getNormalizedOsInfo().OsType)
  207. }
  208. func (instance *SInstance) GetFullOsName() string {
  209. return instance.Platform
  210. }
  211. func (instance *SInstance) GetBios() cloudprovider.TBiosType {
  212. return cloudprovider.ToBiosType(instance.getNormalizedOsInfo().OsBios)
  213. }
  214. func (instance *SInstance) GetOsDist() string {
  215. return instance.getNormalizedOsInfo().OsDistro
  216. }
  217. func (instance *SInstance) GetOsVersion() string {
  218. return instance.getNormalizedOsInfo().OsVersion
  219. }
  220. func (instance *SInstance) GetOsLang() string {
  221. return instance.getNormalizedOsInfo().OsLang
  222. }
  223. func (instance *SInstance) GetOsArch() string {
  224. return instance.getNormalizedOsInfo().OsArch
  225. }
  226. func (instance *SInstance) GetMachine() string {
  227. return "pc"
  228. }
  229. func (instance *SInstance) GetStatus() string {
  230. switch instance.State {
  231. case "Stopped":
  232. return api.VM_READY
  233. case "Running":
  234. return api.VM_RUNNING
  235. case "Destroyed":
  236. return api.VM_DEALLOCATED
  237. default:
  238. log.Errorf("Unknown instance %s status %s", instance.Name, instance.State)
  239. return api.VM_UNKNOWN
  240. }
  241. }
  242. func (instance *SInstance) Refresh() error {
  243. new, err := instance.host.zone.region.GetInstance(instance.UUID)
  244. if err != nil {
  245. return err
  246. }
  247. return jsonutils.Update(instance, new)
  248. }
  249. func (instance *SInstance) GetHypervisor() string {
  250. return api.HYPERVISOR_ZSTACK
  251. }
  252. func (instance *SInstance) StartVM(ctx context.Context) error {
  253. err := instance.host.zone.region.StartVM(instance.UUID)
  254. if err != nil {
  255. return err
  256. }
  257. return cloudprovider.WaitStatus(instance, api.VM_RUNNING, 5*time.Second, 5*time.Minute)
  258. }
  259. func (region *SRegion) StartVM(instanceId string) error {
  260. params := map[string]interface{}{
  261. "startVmInstance": jsonutils.NewDict(),
  262. }
  263. _, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
  264. return err
  265. }
  266. func (instance *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
  267. err := instance.host.zone.region.StopVM(instance.UUID, opts.IsForce)
  268. if err != nil {
  269. return err
  270. }
  271. return cloudprovider.WaitStatus(instance, api.VM_READY, 5*time.Second, 5*time.Minute)
  272. }
  273. func (region *SRegion) StopVM(instanceId string, isForce bool) error {
  274. option := "grace"
  275. if isForce {
  276. option = "cold"
  277. }
  278. params := map[string]interface{}{
  279. "stopVmInstance": map[string]string{
  280. "type": option,
  281. "stopHA": "true",
  282. },
  283. }
  284. _, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
  285. return err
  286. }
  287. func (instance *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  288. info, err := instance.host.zone.region.GetInstanceConsoleInfo(instance.UUID)
  289. if err != nil {
  290. return nil, err
  291. }
  292. authURL, _ := url.Parse(instance.host.zone.region.client.authURL)
  293. url := fmt.Sprintf("%s://%s:5000/thirdparty/vnc_auto.html?host=%s&port=%d&token=%s&title=%s", info.Scheme, authURL.Hostname(), info.Hostname, info.Port, info.Token, instance.Name)
  294. if ver, _ := instance.host.zone.region.client.GetVersion(); ver != nil {
  295. if version.GE(ver.Version, "4.0.0") {
  296. url = fmt.Sprintf("%s://%s:5000/novnc?host=%s&port=%d&token=%s&title=%s&language=zh-CN&lowVersion=false", info.Scheme, authURL.Hostname(), info.Hostname, info.Port, info.Token, instance.Name)
  297. }
  298. }
  299. password, _ := instance.host.zone.region.GetInstanceConsolePassword(instance.UUID)
  300. if len(password) > 0 {
  301. url = url + fmt.Sprintf("&password=%s", password)
  302. }
  303. ret := &cloudprovider.ServerVncOutput{
  304. Url: url,
  305. Protocol: "zstack",
  306. InstanceId: instance.UUID,
  307. Hypervisor: api.HYPERVISOR_ZSTACK,
  308. }
  309. return ret, nil
  310. }
  311. func (instance *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
  312. params := map[string]interface{}{
  313. "updateVmInstance": map[string]string{
  314. "name": input.NAME,
  315. },
  316. }
  317. return instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
  318. }
  319. func (region *SRegion) UpdateVM(instanceId string, params jsonutils.JSONObject) error {
  320. _, err := region.client.put("vm-instances", instanceId, params)
  321. return err
  322. }
  323. func (instance *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
  324. if len(opts.Password) > 0 {
  325. params := map[string]interface{}{
  326. "changeVmPassword": map[string]string{
  327. "account": opts.Username,
  328. "password": opts.Password,
  329. },
  330. }
  331. err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
  332. if err != nil {
  333. return err
  334. }
  335. }
  336. if len(opts.PublicKey) > 0 {
  337. params := map[string]interface{}{
  338. "setVmSshKey": map[string]string{
  339. "SshKey": opts.PublicKey,
  340. },
  341. }
  342. err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params))
  343. if err != nil {
  344. return err
  345. }
  346. }
  347. if opts.DeleteKeypair {
  348. err := instance.host.zone.region.client.delete("vm-instances", fmt.Sprintf("%s/ssh-keys", instance.UUID), "")
  349. if err != nil {
  350. return err
  351. }
  352. }
  353. return nil
  354. }
  355. func (instance *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
  356. return instance.host.zone.region.RebuildRoot(instance.UUID, desc.ImageId, desc.SysSizeGB)
  357. }
  358. func (region *SRegion) RebuildRoot(instanceId, imageId string, sysSizeGB int) (string, error) {
  359. params := map[string]interface{}{
  360. "changeVmImage": map[string]string{
  361. "imageUuid": imageId,
  362. },
  363. }
  364. instance := &SInstance{}
  365. resp, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
  366. if err != nil {
  367. return "", err
  368. }
  369. err = resp.Unmarshal(instance, "inventory")
  370. if err != nil {
  371. return "", err
  372. }
  373. disk, err := region.GetDisk(instance.RootVolumeUUID)
  374. if err != nil {
  375. return "", err
  376. }
  377. if sysSizeGB > disk.GetDiskSizeMB()*1024 {
  378. return instance.RootVolumeUUID, region.ResizeDisk(disk.UUID, int64(sysSizeGB)*1024)
  379. }
  380. return instance.RootVolumeUUID, nil
  381. }
  382. func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
  383. offerings, err := instance.host.zone.region.GetInstanceOfferings("", "", config.Cpu, config.MemoryMB)
  384. if err != nil {
  385. return err
  386. }
  387. if len(config.InstanceType) > 0 {
  388. for _, offering := range offerings {
  389. if offering.Name == config.InstanceType {
  390. return instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID)
  391. }
  392. }
  393. offering, err := instance.host.zone.region.CreateInstanceOffering(config.InstanceType, config.Cpu, config.MemoryMB, "UserVm")
  394. if err != nil {
  395. return err
  396. }
  397. return instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID)
  398. }
  399. for _, offering := range offerings {
  400. log.Debugf("try instance offering %s(%s) ...", offering.Name, offering.UUID)
  401. err := instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID)
  402. if err != nil {
  403. log.Errorf("failed to change config for instance %s(%s) error: %v", instance.Name, instance.UUID, err)
  404. } else {
  405. return nil
  406. }
  407. }
  408. return fmt.Errorf("Failed to change vm config, specification not supported")
  409. }
  410. func (region *SRegion) ChangeConfig(instanceId, offeringId string) error {
  411. params := map[string]interface{}{
  412. "changeInstanceOffering": map[string]string{
  413. "instanceOfferingUuid": offeringId,
  414. },
  415. }
  416. return region.UpdateVM(instanceId, jsonutils.Marshal(params))
  417. }
  418. func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error {
  419. return instance.host.zone.region.AttachDisk(instance.UUID, diskId)
  420. }
  421. func (region *SRegion) AttachDisk(instanceId string, diskId string) error {
  422. _, err := region.client.post(fmt.Sprintf("volumes/%s/vm-instances/%s", diskId, instanceId), jsonutils.NewDict())
  423. return err
  424. }
  425. func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error {
  426. return instance.host.zone.region.DetachDisk(instance.UUID, diskId)
  427. }
  428. func (region *SRegion) DetachDisk(instanceId, diskId string) error {
  429. url := fmt.Sprintf("volumes/%s/vm-instances?vmUuid=%s", diskId, instanceId)
  430. err := region.client.delete(url, "", "")
  431. if err != nil && strings.Contains(err.Error(), "is not attached to any vm") {
  432. return nil
  433. }
  434. return err
  435. }
  436. func (instance *SInstance) DeleteVM(ctx context.Context) error {
  437. disks, err := instance.GetIDisks()
  438. if err != nil {
  439. return errors.Wrapf(err, "GetIDisks")
  440. }
  441. err = instance.host.zone.region.DeleteVM(instance.UUID)
  442. if err != nil {
  443. return errors.Wrapf(err, "DeleteVM")
  444. }
  445. for i := range disks {
  446. if disks[i].GetDiskType() != api.DISK_TYPE_SYS && disks[i].GetIsAutoDelete() {
  447. err = disks[i].Delete(ctx)
  448. if err != nil {
  449. log.Warningf("delete disk %s failed %s", disks[i].GetId(), err)
  450. }
  451. }
  452. }
  453. return nil
  454. }
  455. func (region *SRegion) DeleteVM(instanceId string) error {
  456. err := region.client.delete("vm-instances", instanceId, "Enforcing")
  457. if err != nil {
  458. return err
  459. }
  460. params := map[string]interface{}{
  461. "expungeVmInstance": jsonutils.NewDict(),
  462. }
  463. _, err = region.client.put("vm-instances", instanceId, jsonutils.Marshal(params))
  464. if err != nil {
  465. return errors.Wrapf(err, "expungeVmInstance")
  466. }
  467. return nil
  468. }
  469. func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
  470. eips, err := instance.host.zone.region.GetEips("", instance.UUID)
  471. if err != nil {
  472. return nil, err
  473. }
  474. if len(eips) == 0 {
  475. return nil, cloudprovider.ErrNotFound
  476. }
  477. if len(eips) == 1 {
  478. return &eips[0], nil
  479. }
  480. return nil, cloudprovider.ErrDuplicateId
  481. }
  482. func (instance *SInstance) SetSecurityGroups(secgroupIds []string) error {
  483. currentIds, err := instance.GetSecurityGroupIds()
  484. if err != nil {
  485. return err
  486. }
  487. for _, id := range currentIds {
  488. if !utils.IsInStringArray(id, secgroupIds) {
  489. err := instance.host.zone.region.RevokeSecurityGroup(instance.UUID, id)
  490. if err != nil {
  491. return err
  492. }
  493. }
  494. }
  495. for _, id := range secgroupIds {
  496. if !utils.IsInStringArray(id, currentIds) {
  497. err := instance.host.zone.region.AssignSecurityGroup(instance.UUID, id)
  498. if err != nil {
  499. return err
  500. }
  501. }
  502. }
  503. return nil
  504. }
  505. func (region *SRegion) AssignSecurityGroup(instanceId, secgroupId string) error {
  506. instance, err := region.GetInstance(instanceId)
  507. if err != nil {
  508. return err
  509. }
  510. secgroup, err := region.GetSecurityGroup(secgroupId)
  511. if err != nil {
  512. return err
  513. }
  514. if len(instance.VMNics) > 0 {
  515. if !utils.IsInStringArray(instance.VMNics[0].L3NetworkUUID, secgroup.AttachedL3NetworkUUIDs) {
  516. resource := fmt.Sprintf("security-groups/%s/l3-networks/%s", secgroupId, instance.VMNics[0].L3NetworkUUID)
  517. _, err := region.client.post(resource, jsonutils.NewDict())
  518. if err != nil {
  519. return err
  520. }
  521. }
  522. params := map[string]interface{}{
  523. "params": map[string]interface{}{
  524. "vmNicUuids": []string{instance.VMNics[0].UUID},
  525. },
  526. }
  527. resource := fmt.Sprintf("security-groups/%s/vm-instances/nics", secgroupId)
  528. _, err = region.client.post(resource, jsonutils.Marshal(params))
  529. return err
  530. }
  531. return nil
  532. }
  533. func (region *SRegion) RevokeSecurityGroup(instanceId, secgroupId string) error {
  534. instance, err := region.GetInstance(instanceId)
  535. if err != nil {
  536. return err
  537. }
  538. for _, nic := range instance.VMNics {
  539. resource := fmt.Sprintf("security-groups/%s/vm-instances/nics?vmNicUuids=%s", secgroupId, nic.UUID)
  540. err := region.client.delete(resource, "", "")
  541. if err != nil {
  542. return err
  543. }
  544. }
  545. return nil
  546. }
  547. func (instance *SInstance) GetBillingType() string {
  548. return ""
  549. }
  550. func (instance *SInstance) GetCreatedAt() time.Time {
  551. return instance.CreateDate
  552. }
  553. func (instance *SInstance) GetExpiredAt() time.Time {
  554. return time.Time{}
  555. }
  556. func (instance *SInstance) UpdateUserData(userData string) error {
  557. return cloudprovider.ErrNotSupported
  558. }
  559. func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
  560. return cloudprovider.ErrNotSupported
  561. }
  562. func (instance *SInstance) GetProjectId() string {
  563. return ""
  564. }
  565. func (instance *SInstance) GetError() error {
  566. return nil
  567. }
  568. type SConsoleInfo struct {
  569. Scheme string `json:"scheme"`
  570. Hostname string `json:"hostname"`
  571. Port int `json:"port"`
  572. Token string `json:"token"`
  573. }
  574. func (region *SRegion) GetInstanceConsoleInfo(instnaceId string) (*SConsoleInfo, error) {
  575. params := map[string]interface{}{
  576. "params": map[string]string{
  577. "vmInstanceUuid": instnaceId,
  578. },
  579. }
  580. resp, err := region.client.post("consoles", jsonutils.Marshal(params))
  581. if err != nil {
  582. return nil, err
  583. }
  584. info := &SConsoleInfo{}
  585. err = resp.Unmarshal(info, "inventory")
  586. if err != nil {
  587. return nil, err
  588. }
  589. return info, nil
  590. }
  591. func (region *SRegion) GetInstanceConsolePassword(instnaceId string) (string, error) {
  592. resp, err := region.client.get("vm-instances", instnaceId, "console-passwords")
  593. if err != nil {
  594. return "", err
  595. }
  596. if resp.Contains("consolePassword") {
  597. return resp.GetString("consolePassword")
  598. }
  599. return "", nil
  600. }