instance.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  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 aws
  15. import (
  16. "context"
  17. "encoding/base64"
  18. "fmt"
  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/cloudinit"
  26. "yunion.io/x/pkg/util/osprofile"
  27. "yunion.io/x/pkg/utils"
  28. "yunion.io/x/cloudmux/pkg/apis"
  29. billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
  30. api "yunion.io/x/cloudmux/pkg/apis/compute"
  31. "yunion.io/x/cloudmux/pkg/cloudprovider"
  32. "yunion.io/x/cloudmux/pkg/multicloud"
  33. )
  34. const (
  35. InstanceStatusPending = "pending"
  36. InstanceStatusRunning = "running"
  37. InstanceStatusShutting = "shutting-down"
  38. InstanceStatusTerminated = "terminated"
  39. InstanceStatusStopping = "stopping"
  40. InstanceStatusStopped = "stopped"
  41. )
  42. type InstanceChargeType string
  43. type SIpAddress struct {
  44. IpAddress []string
  45. }
  46. type SSecurityGroupIds struct {
  47. SecurityGroupId []string
  48. }
  49. type SVpcAttributes struct {
  50. PrivateIpAddress SIpAddress
  51. NetworkId string // subnet id
  52. VpcId string
  53. }
  54. type EbsInstanceBlockDevice struct {
  55. AttachTime time.Time `xml:"attachTime"`
  56. DeleteOnTermination bool `xml:"deleteOnTermination"`
  57. Status string `xml:"status"`
  58. VolumeId string `xml:"volumeId"`
  59. }
  60. type InstanceBlockDeviceMapping struct {
  61. DeviceName *string `xml:"deviceName"`
  62. Ebs EbsInstanceBlockDevice `xml:"ebs"`
  63. }
  64. type SInstance struct {
  65. multicloud.SInstanceBase
  66. AwsTags
  67. host *SHost
  68. img *SImage
  69. AmiLaunchIndex int64 `xml:"amiLaunchIndex"`
  70. Architecture string `xml:"architecture"`
  71. BlockDeviceMappings []InstanceBlockDeviceMapping `xml:"blockDeviceMapping>item"`
  72. BootMode string `xml:"bootMode"`
  73. CapacityReservationId string `xml:"capacityReservationId"`
  74. ClientToken string `xml:"clientToken"`
  75. CpuOptions struct {
  76. CoreCount int `xml:"coreCount"`
  77. ThreadsPerCore int `xml:"threadsPerCore"`
  78. } `xml:"cpuOptions"`
  79. EbsOptimized bool `xml:"ebsOptimized"`
  80. ElasticGpuAssociations []struct {
  81. ElasticGpuAssociationId string `xml:"elasticGpuAssociationId"`
  82. ElasticGpuAssociationState string `xml:"elasticGpuAssociationState"`
  83. ElasticGpuAssociationTime string `xml:"elasticGpuAssociationTime"`
  84. ElasticGpuId string `xml:"elasticGpuId"`
  85. } `xml:"elasticGpuAssociationSet>item"`
  86. EnaSupport bool `xml:"enaSupport"`
  87. EnclaveOptions struct {
  88. Enabled bool `xml:"enabled"`
  89. } `xml:"enclaveOptions"`
  90. HibernationOptions struct {
  91. Configured bool `xml:"configured"`
  92. } `xml:"hibernationOptions"`
  93. Hypervisor string `xml:"hypervisor"`
  94. IamInstanceProfile struct {
  95. Id string `xml:"id"`
  96. Arn string `xml:"arn"`
  97. } `xml:"iamInstanceProfile"`
  98. ImageId string `xml:"imageId"`
  99. InstanceId string `xml:"instanceId"`
  100. InstanceLifecycle string `xml:"instanceLifecycle"`
  101. InstanceType string `xml:"instanceType"`
  102. KernelId string `xml:"kernelId"`
  103. KeyName string `xml:"keyName"`
  104. LaunchTime time.Time `xml:"launchTime"`
  105. Licenses []struct {
  106. LicenseConfigurationArn string `xml:"licenseConfigurationArn"`
  107. } `xml:"licenseSet>item"`
  108. MetadataOptions struct {
  109. HttpEndpoint string `xml:"httpEndpoint"`
  110. HttpPutResponseHopLimit int64 `xml:"httpPutResponseHopLimit"`
  111. HttpTokens string `xml:"httpTokens"`
  112. State string `xml:"state"`
  113. } `xml:"metadataOptions"`
  114. Monitoring struct {
  115. State string `xml:"state"`
  116. } `xml:"monitoring"`
  117. NetworkInterfaces []SNetworkInterface `xml:"networkInterfaceSet>item"`
  118. OutpostArn string `xml:"outpostArn"`
  119. Placement struct {
  120. Affinity string `xml:"affinity"`
  121. AvailabilityZone string `xml:"availabilityZone"`
  122. GroupName string `xml:"groupName"`
  123. HostId string `xml:"hostId"`
  124. HostResourceGroupArn string `xml:"hostResourceGroupArn"`
  125. PartitionNumber int64 `xml:"partitionNumber"`
  126. SpreadDomain string `xml:"spreadDomain"`
  127. Tenancy string `xml:"tenancy"`
  128. } `xml:"placement"`
  129. Platform string `xml:"platform"`
  130. PrivateDnsName string `xml:"privateDnsName"`
  131. PrivateIpAddress string `xml:"privateIpAddress"`
  132. ProductCodes []struct {
  133. ProductCodeId string `xml:"productCode"`
  134. ProductCodeType string `xml:"type"`
  135. } `xml:"productCodes>item"`
  136. PublicDnsName string `xml:"dnsName"`
  137. PublicIpAddress string `xml:"ipAddress"`
  138. RamdiskId string `xml:"ramdiskId"`
  139. RootDeviceName string `xml:"rootDeviceName"`
  140. RootDeviceType string `xml:"rootDeviceType"`
  141. SecurityGroups []struct {
  142. GroupId string `xml:"groupId"`
  143. GroupName string `xml:"groupName"`
  144. } `xml:"groupSet>item"`
  145. SourceDestCheck bool `xml:"sourceDestCheck"`
  146. SpotInstanceRequestId string `xml:"spotInstanceRequestId"`
  147. SriovNetSupport string `xml:"sriovNetSupport"`
  148. State struct {
  149. Code int64 `xml:"code"`
  150. Name string `xml:"name"`
  151. } `xml:"instanceState"`
  152. StateReason struct {
  153. Code string `xml:"code"`
  154. Message string `xml:"message"`
  155. } `xml:"stateReason"`
  156. StateTransitionReason string `xml:"reason"`
  157. SubnetId string `xml:"subnetId"`
  158. VirtualizationType string `xml:"virtualizationType"`
  159. VpcId string `xml:"vpcId"`
  160. }
  161. func (self *SInstance) UpdateUserData(userData string) error {
  162. return self.host.zone.region.ModifyInstanceAttribute(self.InstanceId, &SInstanceAttr{UserData: userData})
  163. }
  164. func (self *SInstance) GetUserData() (string, error) {
  165. ret, err := self.host.zone.region.DescribeInstanceAttribute(self.InstanceId, &InstanceAttributeInput{UserData: true})
  166. if err != nil {
  167. return "", errors.Wrapf(err, "DescribeInstanceAttribute")
  168. }
  169. udata, err := base64.StdEncoding.DecodeString(ret.UserData.Value)
  170. return string(udata), err
  171. }
  172. type InstanceAttribute struct {
  173. UserData struct {
  174. Value string `xml:"value"`
  175. } `xml:"userData"`
  176. }
  177. type InstanceAttributeInput struct {
  178. UserData bool
  179. }
  180. func (self *SRegion) DescribeInstanceAttribute(id string, opts *InstanceAttributeInput) (*InstanceAttribute, error) {
  181. params := map[string]string{
  182. "InstanceId": id,
  183. }
  184. if opts.UserData {
  185. params["Attribute"] = "userData"
  186. }
  187. ret := &InstanceAttribute{}
  188. err := self.ec2Request("DescribeInstanceAttribute", params, ret)
  189. return ret, err
  190. }
  191. func (self *SInstance) GetId() string {
  192. return self.InstanceId
  193. }
  194. func (self *SInstance) GetName() string {
  195. name := self.AwsTags.GetName()
  196. if len(name) > 0 {
  197. return name
  198. }
  199. return self.InstanceId
  200. }
  201. func (self *SInstance) GetHostname() string {
  202. return ""
  203. }
  204. func (self *SInstance) GetGlobalId() string {
  205. return self.InstanceId
  206. }
  207. func (self *SInstance) GetStatus() string {
  208. switch self.State.Name {
  209. case InstanceStatusRunning:
  210. return api.VM_RUNNING
  211. case InstanceStatusPending: // todo: pending ?
  212. return api.VM_STARTING
  213. case InstanceStatusStopping:
  214. return api.VM_STOPPING
  215. case InstanceStatusStopped:
  216. return api.VM_READY
  217. default:
  218. return api.VM_UNKNOWN
  219. }
  220. }
  221. func (self *SInstance) Refresh() error {
  222. vm, err := self.host.zone.region.GetInstance(self.InstanceId)
  223. if err != nil {
  224. return err
  225. }
  226. self.BlockDeviceMappings = nil
  227. self.NetworkInterfaces = nil
  228. self.SecurityGroups = nil
  229. return jsonutils.Update(self, vm)
  230. }
  231. func (self *SInstance) GetInstanceType() string {
  232. return self.InstanceType
  233. }
  234. func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
  235. ret := []string{}
  236. for _, group := range self.SecurityGroups {
  237. ret = append(ret, group.GroupId)
  238. }
  239. return ret, nil
  240. }
  241. func (self *SInstance) GetBillingType() string {
  242. return billing_api.BILLING_TYPE_POSTPAID
  243. }
  244. func (self *SInstance) GetCreatedAt() time.Time {
  245. return self.LaunchTime
  246. }
  247. func (self *SInstance) GetExpiredAt() time.Time {
  248. return time.Time{}
  249. }
  250. func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
  251. return self.host
  252. }
  253. func (self *SInstance) GetThroughput() int {
  254. return 0
  255. }
  256. func (self *SInstance) GetInternetMaxBandwidthOut() int {
  257. return 0
  258. }
  259. func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
  260. disks, err := self.host.zone.region.GetDisks(self.InstanceId, "", "", nil)
  261. if err != nil {
  262. return nil, errors.Wrap(err, "GetDisks")
  263. }
  264. ret := []cloudprovider.ICloudDisk{}
  265. for i := 0; i < len(disks); i += 1 {
  266. store, err := self.host.zone.getStorageByCategory(disks[i].VolumeType)
  267. if err != nil {
  268. return nil, errors.Wrap(err, "getStorageByCategory")
  269. }
  270. disks[i].storage = store
  271. if disks[i].getDevice() == self.RootDeviceName {
  272. ret = append([]cloudprovider.ICloudDisk{&disks[i]}, ret...)
  273. } else {
  274. ret = append(ret, &disks[i])
  275. }
  276. }
  277. return ret, nil
  278. }
  279. func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
  280. var (
  281. networkInterfaces = self.NetworkInterfaces
  282. nics = make([]cloudprovider.ICloudNic, 0)
  283. )
  284. for _, networkInterface := range networkInterfaces {
  285. nic := SInstanceNic{
  286. instance: self,
  287. id: networkInterface.NetworkInterfaceId,
  288. ipAddr: networkInterface.PrivateIpAddress,
  289. macAddr: networkInterface.MacAddress,
  290. }
  291. for _, ip6 := range networkInterface.IPv6AddressesSet {
  292. if len(ip6.IPv6Address) > 0 {
  293. nic.ip6Addr = ip6.IPv6Address
  294. break
  295. }
  296. }
  297. nics = append(nics, &nic)
  298. }
  299. return nics, nil
  300. }
  301. func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
  302. if len(self.PublicIpAddress) > 0 {
  303. eip, err := self.host.zone.region.GetEipByIpAddress(self.PublicIpAddress)
  304. if err != nil {
  305. if errors.Cause(err) == cloudprovider.ErrNotFound {
  306. eip := SEipAddress{region: self.host.zone.region}
  307. eip.region = self.host.zone.region
  308. eip.PublicIp = self.PublicIpAddress
  309. eip.InstanceId = self.InstanceId
  310. eip.AllocationId = self.InstanceId // fixed. AllocationId等于InstanceId即表示为 仿真EIP。
  311. return &eip, nil
  312. }
  313. return nil, err
  314. }
  315. return eip, nil
  316. }
  317. for _, nic := range self.NetworkInterfaces {
  318. if len(nic.Association.PublicIp) > 0 {
  319. eip := SEipAddress{region: self.host.zone.region}
  320. eip.region = self.host.zone.region
  321. eip.PublicIp = nic.Association.PublicIp
  322. eip.InstanceId = self.InstanceId
  323. eip.AllocationId = self.InstanceId // fixed. AllocationId等于InstanceId即表示为 仿真EIP。
  324. return &eip, nil
  325. }
  326. }
  327. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty eip")
  328. }
  329. func (self *SInstance) GetVcpuCount() int {
  330. return self.CpuOptions.CoreCount * self.CpuOptions.ThreadsPerCore
  331. }
  332. func (self *SInstance) GetVmemSizeMB() int {
  333. instanceType, _ := self.host.zone.region.GetInstanceType(self.InstanceType)
  334. if instanceType != nil {
  335. return instanceType.MemoryInfo.SizeInMiB
  336. }
  337. return 0
  338. }
  339. func (self *SInstance) GetBootOrder() string {
  340. return "dcn"
  341. }
  342. func (self *SInstance) GetVga() string {
  343. return "std"
  344. }
  345. func (self *SInstance) GetVdi() string {
  346. return "vnc"
  347. }
  348. func (self *SInstance) GetOsType() cloudprovider.TOsType {
  349. if len(self.Platform) > 0 {
  350. return cloudprovider.TOsType(osprofile.NormalizeOSType(self.Platform))
  351. }
  352. return cloudprovider.OsTypeLinux
  353. }
  354. func (self *SInstance) GetFullOsName() string {
  355. img, err := self.GetImage()
  356. if err != nil {
  357. return ""
  358. }
  359. return img.ImageName
  360. }
  361. func (self *SInstance) GetBios() cloudprovider.TBiosType {
  362. img, err := self.GetImage()
  363. if err != nil {
  364. log.Errorf("GetImage fail %s", err)
  365. return cloudprovider.BIOS
  366. }
  367. return img.GetBios()
  368. }
  369. func (self *SInstance) GetOsArch() string {
  370. if len(self.Architecture) > 0 {
  371. switch self.Architecture {
  372. case "arm64":
  373. return apis.OS_ARCH_AARCH64
  374. case "i386":
  375. return apis.OS_ARCH_X86
  376. case "x86_64":
  377. return apis.OS_ARCH_X86_64
  378. default:
  379. return apis.OS_ARCH_X86_64
  380. }
  381. }
  382. img, err := self.GetImage()
  383. if err != nil {
  384. return apis.OS_ARCH_X86_64
  385. }
  386. return img.GetOsArch()
  387. }
  388. func (self *SInstance) GetOsDist() string {
  389. img, err := self.GetImage()
  390. if err != nil {
  391. log.Errorf("GetImage fail %s", err)
  392. return ""
  393. }
  394. return img.GetOsDist()
  395. }
  396. func (self *SInstance) GetOsVersion() string {
  397. img, err := self.GetImage()
  398. if err != nil {
  399. log.Errorf("GetImage fail %s", err)
  400. return ""
  401. }
  402. return img.GetOsVersion()
  403. }
  404. func (self *SInstance) GetOsLang() string {
  405. img, err := self.GetImage()
  406. if err != nil {
  407. log.Errorf("GetImage fail %s", err)
  408. return ""
  409. }
  410. return img.GetOsLang()
  411. }
  412. func (self *SInstance) GetMachine() string {
  413. return "pc"
  414. }
  415. func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
  416. return self.host.zone.region.assignSecurityGroups(secgroupIds, self.InstanceId)
  417. }
  418. func (self *SInstance) GetHypervisor() string {
  419. return api.HYPERVISOR_AWS
  420. }
  421. func (self *SInstance) StartVM(ctx context.Context) error {
  422. timeout := 300 * time.Second
  423. interval := 15 * time.Second
  424. startTime := time.Now()
  425. for time.Now().Sub(startTime) < timeout {
  426. err := self.Refresh()
  427. if err != nil {
  428. return err
  429. }
  430. if self.GetStatus() == api.VM_RUNNING {
  431. return nil
  432. } else if self.GetStatus() == api.VM_READY {
  433. err := self.host.zone.region.StartVM(self.InstanceId)
  434. if err != nil {
  435. return err
  436. }
  437. }
  438. time.Sleep(interval)
  439. }
  440. return cloudprovider.ErrTimeout
  441. }
  442. func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
  443. err := cloudprovider.Wait(time.Second*4, time.Minute*10, func() (bool, error) {
  444. if utils.IsInStringArray(self.State.Name, []string{"running", "pending", "stopping", "stopped"}) {
  445. return true, nil
  446. }
  447. err := self.Refresh()
  448. if err != nil {
  449. return false, err
  450. }
  451. return false, nil
  452. })
  453. if err != nil {
  454. log.Errorf("wait instance status stoped, current status: %s error: %v", self.State.Name, err)
  455. }
  456. return self.host.zone.region.StopVM(self.InstanceId, opts.IsForce)
  457. }
  458. func (self *SInstance) DeleteVM(ctx context.Context) error {
  459. err := cloudprovider.Wait(time.Second*4, time.Minute*10, func() (bool, error) {
  460. if utils.IsInStringArray(self.State.Name, []string{"running", "pending", "stopping", "stopped"}) {
  461. return true, nil
  462. }
  463. err := self.Refresh()
  464. if err != nil {
  465. return false, err
  466. }
  467. return false, nil
  468. })
  469. if err != nil {
  470. log.Errorf("wait instance status stoped to delete, current status: %s error: %v", self.State.Name, err)
  471. }
  472. return self.host.zone.region.DeleteVM(self.InstanceId)
  473. }
  474. func (self *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
  475. return self.host.zone.region.UpdateVM(self.InstanceId, input)
  476. }
  477. func (self *SRegion) UpdateVM(instanceId string, input cloudprovider.SInstanceUpdateOptions) error {
  478. return self.setTags("instance", instanceId, map[string]string{"Name": input.NAME, "Description": input.Description}, false)
  479. }
  480. func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
  481. udata, err := self.GetUserData()
  482. if err != nil {
  483. return "", err
  484. }
  485. // compare sysSizeGB
  486. image, err := self.host.zone.region.GetImage(desc.ImageId)
  487. if err != nil {
  488. return "", err
  489. } else {
  490. minSizeGB := image.GetMinOsDiskSizeGb()
  491. if minSizeGB > desc.SysSizeGB {
  492. desc.SysSizeGB = minSizeGB
  493. }
  494. }
  495. // upload keypair
  496. keypairName := self.KeyName
  497. if len(desc.PublicKey) > 0 {
  498. keypairName, err = self.host.zone.region.SyncKeypair(desc.PublicKey)
  499. if err != nil {
  500. return "", fmt.Errorf("RebuildRoot.syncKeypair %s", err)
  501. }
  502. }
  503. userdata := ""
  504. srcOsType := strings.ToLower(string(self.GetOsType()))
  505. destOsType := strings.ToLower(string(image.GetOsType()))
  506. winOS := strings.ToLower(osprofile.OS_TYPE_WINDOWS)
  507. cloudconfig := &cloudinit.SCloudConfig{}
  508. if srcOsType != winOS && len(udata) > 0 {
  509. _cloudconfig, err := cloudinit.ParseUserData(udata)
  510. if err != nil {
  511. // 忽略无效的用户数据
  512. log.Debugf("RebuildRoot invalid instance user data %s", udata)
  513. } else {
  514. cloudconfig = _cloudconfig
  515. }
  516. }
  517. if (srcOsType != winOS && destOsType != winOS) || (srcOsType == winOS && destOsType != winOS) {
  518. // linux/windows to linux
  519. loginUser := cloudinit.NewUser(api.VM_AWS_DEFAULT_LOGIN_USER)
  520. loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD)
  521. if len(desc.PublicKey) > 0 {
  522. loginUser.SshKey(desc.PublicKey)
  523. cloudconfig.MergeUser(loginUser)
  524. } else if len(desc.Password) > 0 {
  525. cloudconfig.SshPwauth = cloudinit.SSH_PASSWORD_AUTH_ON
  526. loginUser.Password(desc.Password)
  527. cloudconfig.MergeUser(loginUser)
  528. }
  529. userdata = cloudconfig.UserDataBase64()
  530. } else {
  531. // linux/windows to windows
  532. data := ""
  533. if len(desc.Password) > 0 {
  534. cloudconfig.SshPwauth = cloudinit.SSH_PASSWORD_AUTH_ON
  535. loginUser := cloudinit.NewUser(api.VM_AWS_DEFAULT_WINDOWS_LOGIN_USER)
  536. loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD)
  537. loginUser.Password(desc.Password)
  538. cloudconfig.MergeUser(loginUser)
  539. data = fmt.Sprintf("<powershell>%s</powershell>", cloudconfig.UserDataPowerShell())
  540. } else {
  541. if len(udata) > 0 {
  542. data = fmt.Sprintf("<powershell>%s</powershell>", udata)
  543. }
  544. }
  545. userdata = base64.StdEncoding.EncodeToString([]byte(data))
  546. }
  547. diskId, err := self.host.zone.region.ReplaceSystemDisk(ctx, self.InstanceId, image, desc.SysSizeGB, keypairName, userdata)
  548. if err != nil {
  549. return "", err
  550. }
  551. return diskId, nil
  552. }
  553. func (self *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
  554. return cloudprovider.ErrNotSupported
  555. }
  556. func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
  557. if len(config.InstanceType) > 0 {
  558. return self.ChangeConfig2(ctx, config.InstanceType)
  559. }
  560. return errors.Wrap(errors.ErrClient, "Instance.ChangeConfig.InstanceTypeIsEmpty")
  561. }
  562. func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
  563. return self.host.zone.region.ChangeVMConfig2(self.InstanceId, instanceType)
  564. }
  565. func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  566. return nil, cloudprovider.ErrNotSupported
  567. }
  568. func (self *SInstance) GetImage() (*SImage, error) {
  569. if self.img != nil {
  570. return self.img, nil
  571. }
  572. img, err := self.host.zone.region.GetImage(self.ImageId)
  573. if err != nil {
  574. return nil, errors.Wrap(err, "GetImage")
  575. }
  576. self.img = img
  577. return self.img, nil
  578. }
  579. func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
  580. img, err := self.GetImage()
  581. if err != nil {
  582. return errors.Wrap(err, "GetImage")
  583. }
  584. err = self.Refresh()
  585. if err != nil {
  586. return err
  587. }
  588. deviceNames := img.GetBlockDeviceNames()
  589. for _, dev := range self.BlockDeviceMappings {
  590. if dev.DeviceName != nil && len(*dev.DeviceName) > 0 {
  591. deviceNames = append(deviceNames, *dev.DeviceName)
  592. }
  593. }
  594. name, err := NextDeviceName(deviceNames)
  595. if err != nil {
  596. return err
  597. }
  598. err = self.host.zone.region.AttachDisk(self.InstanceId, diskId, name)
  599. if err != nil {
  600. return err
  601. }
  602. return nil
  603. }
  604. func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
  605. return self.host.zone.region.DetachDisk(self.InstanceId, diskId)
  606. }
  607. func (self *SInstance) getVpc() (*SVpc, error) {
  608. return self.host.zone.region.getVpc(self.VpcId)
  609. }
  610. func (self *SRegion) GetInstances(zoneId, imageId string, ids []string) ([]SInstance, error) {
  611. params := map[string]string{}
  612. idx := 1
  613. if len(zoneId) > 0 {
  614. params[fmt.Sprintf("Filter.%d.Name", idx)] = "availability-zone"
  615. params[fmt.Sprintf("Filter.%d.Value.1", idx)] = zoneId
  616. idx++
  617. }
  618. if len(imageId) > 0 {
  619. params[fmt.Sprintf("Filter.%d.Name", idx)] = "image-id"
  620. params[fmt.Sprintf("Filter.%d.Value.1", idx)] = imageId
  621. idx++
  622. }
  623. // skip terminated instance
  624. params[fmt.Sprintf("Filter.%d.Name", idx)] = "instance-state-name"
  625. for i, state := range []string{"pending", "running", "shutting-down", "stopping", "stopped"} {
  626. params[fmt.Sprintf("Filter.%d.Value.%d", idx, i+1)] = state
  627. }
  628. idx++
  629. for i, id := range ids {
  630. params[fmt.Sprintf("InstanceId.%d", i+1)] = id
  631. }
  632. ret := []SInstance{}
  633. for {
  634. part := struct {
  635. NextToken string `xml:"nextToken"`
  636. ReservationSet []struct {
  637. InstancesSet []SInstance `xml:"instancesSet>item"`
  638. } `xml:"reservationSet>item"`
  639. }{}
  640. err := self.ec2Request("DescribeInstances", params, &part)
  641. if err != nil {
  642. return nil, err
  643. }
  644. for _, res := range part.ReservationSet {
  645. ret = append(ret, res.InstancesSet...)
  646. }
  647. if len(part.ReservationSet) == 0 || len(part.NextToken) == 0 {
  648. break
  649. }
  650. params["NextToken"] = part.NextToken
  651. }
  652. return ret, nil
  653. }
  654. func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) {
  655. instances, err := self.GetInstances("", "", []string{instanceId})
  656. if err != nil {
  657. return nil, errors.Wrap(err, "GetInstances")
  658. }
  659. for i := range instances {
  660. if instances[i].InstanceId == instanceId {
  661. return &instances[i], nil
  662. }
  663. }
  664. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", instanceId)
  665. }
  666. func (self *SRegion) GetInstanceIdByImageId(imageId string) (string, error) {
  667. instances, err := self.GetInstances("", imageId, nil)
  668. if err != nil {
  669. return "", err
  670. }
  671. for i := range instances {
  672. return instances[i].InstanceId, nil
  673. }
  674. return "", fmt.Errorf("instance launch with image %s not found", imageId)
  675. }
  676. func (self *SRegion) CreateInstance(name string, image *SImage, instanceType string, subnetId string, secgroupIds []string,
  677. zoneId string, desc string, disks []cloudprovider.SDiskInfo, ipAddr string,
  678. keypair string, userData string, tags map[string]string, enableMonitorAgent bool,
  679. ) (*SInstance, error) {
  680. params, devNames := map[string]string{}, image.GetBlockDeviceNames()
  681. for i, disk := range disks {
  682. deviceName := image.RootDeviceName
  683. if i == 0 && len(deviceName) == 0 {
  684. deviceName = "/dev/sda1"
  685. devNames = append(devNames, deviceName)
  686. }
  687. if i > 0 {
  688. var err error
  689. deviceName, err = NextDeviceName(devNames)
  690. if err != nil {
  691. return nil, errors.Wrapf(err, "NextDeviceName")
  692. }
  693. devNames = append(devNames, deviceName)
  694. }
  695. params[fmt.Sprintf("BlockDeviceMapping.%d.DeviceName", i+1)] = deviceName
  696. params[fmt.Sprintf("BlockDeviceMapping.%d.Ebs.DeleteOnTermination", i+1)] = "true"
  697. params[fmt.Sprintf("BlockDeviceMapping.%d.Ebs.VolumeSize", i+1)] = fmt.Sprintf("%d", disk.SizeGB)
  698. params[fmt.Sprintf("BlockDeviceMapping.%d.Ebs.VolumeType", i+1)] = disk.StorageType
  699. iops := disk.Iops
  700. if iops == 0 {
  701. iops = int(GenDiskIops(disk.StorageType, disk.SizeGB))
  702. }
  703. if utils.IsInStringArray(disk.StorageType, []string{
  704. api.STORAGE_IO1_SSD,
  705. api.STORAGE_IO2_SSD,
  706. api.STORAGE_GP3_SSD,
  707. }) {
  708. params[fmt.Sprintf("BlockDeviceMapping.%d.Ebs.Iops", i+1)] = fmt.Sprintf("%d", iops)
  709. }
  710. if disk.Throughput >= 125 && disk.Throughput <= 1000 && disk.StorageType == api.STORAGE_GP3_SSD {
  711. params[fmt.Sprintf("BlockDeviceMapping.%d.Ebs.Throughput", i+1)] = fmt.Sprintf("%d", disk.Throughput)
  712. }
  713. }
  714. tagIdx := 1
  715. params[fmt.Sprintf("TagSpecification.1.ResourceType")] = "instance"
  716. params[fmt.Sprintf("TagSpecification.1.Tag.%d.Key", tagIdx)] = "Name"
  717. params[fmt.Sprintf("TagSpecification.1.Tag.%d.Value", tagIdx)] = name
  718. tagIdx++
  719. if len(desc) > 0 {
  720. params[fmt.Sprintf("TagSpecification.1.Tag.%d.Key", tagIdx)] = "Description"
  721. params[fmt.Sprintf("TagSpecification.1.Tag.%d.Value", tagIdx)] = desc
  722. tagIdx++
  723. }
  724. for k, v := range tags {
  725. params[fmt.Sprintf("TagSpecification.1.Tag.%d.Key", tagIdx)] = k
  726. params[fmt.Sprintf("TagSpecification.1.Tag.%d.Value", tagIdx)] = v
  727. tagIdx++
  728. }
  729. tagIdx = 1
  730. for k, v := range tags {
  731. params[fmt.Sprintf("TagSpecification.2.ResourceType")] = "volume"
  732. params[fmt.Sprintf("TagSpecification.2.Tag.%d.Key", tagIdx)] = k
  733. params[fmt.Sprintf("TagSpecification.2.Tag.%d.Value", tagIdx)] = v
  734. tagIdx++
  735. }
  736. params["ImageId"] = image.ImageId
  737. params["InstanceType"] = instanceType
  738. params["MaxCount"] = "1"
  739. params["MinCount"] = "1"
  740. params["Placement.AvailabilityZone"] = zoneId
  741. params["Monitoring.Enabled"] = fmt.Sprintf("%v", enableMonitorAgent)
  742. // keypair
  743. if len(keypair) > 0 {
  744. params["KeyName"] = keypair
  745. }
  746. // user data
  747. if len(userData) > 0 {
  748. params["UserData"] = userData
  749. }
  750. // ip address
  751. if len(ipAddr) > 0 {
  752. params["PrivateIpAddress"] = ipAddr
  753. }
  754. // subnet id
  755. if len(subnetId) > 0 {
  756. params["SubnetId"] = subnetId
  757. }
  758. // security group
  759. for i, id := range secgroupIds {
  760. params[fmt.Sprintf("SecurityGroupId.%d", i+1)] = id
  761. }
  762. ret := struct {
  763. InstancesSet []SInstance `xml:"instancesSet>item"`
  764. }{}
  765. err := self.ec2Request("RunInstances", params, &ret)
  766. if err != nil {
  767. return nil, errors.Wrapf(err, "RunInstances")
  768. }
  769. for i := range ret.InstancesSet {
  770. return &ret.InstancesSet[i], nil
  771. }
  772. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created")
  773. }
  774. func (self *SRegion) StartVM(instanceId string) error {
  775. params := map[string]string{
  776. "InstanceId.1": instanceId,
  777. }
  778. ret := struct{}{}
  779. return self.ec2Request("StartInstances", params, &ret)
  780. }
  781. func (self *SRegion) StopVM(instanceId string, isForce bool) error {
  782. params := map[string]string{
  783. "InstanceId.1": instanceId,
  784. }
  785. if isForce {
  786. params["Force"] = "true"
  787. }
  788. ret := struct{}{}
  789. return self.ec2Request("StopInstances", params, &ret)
  790. }
  791. func (self *SRegion) DeleteVM(instanceId string) error {
  792. disableApiTermination := false
  793. err := self.ModifyInstanceAttribute(instanceId, &SInstanceAttr{
  794. DisableApiTermination: &disableApiTermination,
  795. })
  796. if err != nil {
  797. return err
  798. }
  799. params := map[string]string{
  800. "InstanceId.1": instanceId,
  801. }
  802. ret := struct{}{}
  803. return self.ec2Request("TerminateInstances", params, &ret)
  804. }
  805. func (self *SRegion) ReplaceSystemDisk(ctx context.Context, instanceId string, image *SImage, sysDiskSizeGB int, keypair string, userdata string) (string, error) {
  806. instance, err := self.GetInstance(instanceId)
  807. if err != nil {
  808. return "", err
  809. }
  810. disks, err := self.GetDisks(instanceId, instance.Placement.AvailabilityZone, "", nil)
  811. if err != nil {
  812. return "", err
  813. }
  814. var rootDisk *SDisk
  815. for _, disk := range disks {
  816. if disk.getDevice() == instance.RootDeviceName {
  817. rootDisk = &disk
  818. break
  819. }
  820. }
  821. if rootDisk == nil {
  822. return "", fmt.Errorf("can not find root disk of instance %s", instanceId)
  823. }
  824. log.Debugf("ReplaceSystemDisk replace root disk %s", rootDisk.VolumeId)
  825. subnetId := instance.SubnetId
  826. // create tmp server
  827. tempName := fmt.Sprintf("__tmp_%s", instance.GetName())
  828. vm, err := self.CreateInstance(tempName,
  829. image,
  830. instance.InstanceType,
  831. subnetId,
  832. []string{},
  833. instance.Placement.AvailabilityZone,
  834. instance.GetDescription(),
  835. []cloudprovider.SDiskInfo{{SizeGB: sysDiskSizeGB, StorageType: rootDisk.VolumeType}},
  836. "",
  837. keypair,
  838. userdata,
  839. nil,
  840. false,
  841. )
  842. if err == nil {
  843. defer self.DeleteVM(vm.InstanceId)
  844. } else {
  845. return "", fmt.Errorf("ReplaceSystemDisk create temp server failed.")
  846. }
  847. err = cloudprovider.Wait(time.Second*2, time.Minute*10, func() (bool, error) {
  848. instance, err := self.GetInstance(vm.InstanceId)
  849. if err != nil {
  850. return false, errors.Wrapf(err, "GetInstance")
  851. }
  852. log.Debugf("wait temp vm %s running, current status: %s", vm.InstanceId, instance.GetStatus())
  853. if instance.GetStatus() == api.VM_RUNNING {
  854. return true, nil
  855. }
  856. return false, nil
  857. })
  858. if err != nil {
  859. log.Errorf("wait temp vm %s running error: %v", vm.InstanceId, err)
  860. }
  861. err = self.StopVM(vm.InstanceId, true)
  862. if err != nil {
  863. return "", errors.Wrapf(err, "StopVM")
  864. }
  865. err = cloudprovider.Wait(time.Second*2, time.Minute*10, func() (bool, error) {
  866. instance, err := self.GetInstance(vm.InstanceId)
  867. if err != nil {
  868. return false, errors.Wrapf(err, "GetInstance")
  869. }
  870. log.Debugf("wait temp vm %s stop, current status: %s", vm.InstanceId, instance.GetStatus())
  871. if instance.GetStatus() == api.VM_READY {
  872. return true, nil
  873. }
  874. return false, nil
  875. })
  876. if err != nil {
  877. log.Errorf("wait temp vm %s stop error: %v", vm.InstanceId, err)
  878. }
  879. // detach disks
  880. tempInstance, err := self.GetInstance(vm.InstanceId)
  881. if err != nil {
  882. return "", errors.Wrapf(err, "GetInstance")
  883. }
  884. tempRootDiskId := tempInstance.BlockDeviceMappings[0].Ebs.VolumeId
  885. err = self.DetachDisk(tempInstance.GetId(), tempRootDiskId)
  886. if err != nil {
  887. return "", errors.Wrapf(err, "DetachDisk temp vm")
  888. }
  889. err = self.DetachDisk(instance.GetId(), rootDisk.VolumeId)
  890. if err != nil {
  891. self.DeleteDisk(tempRootDiskId)
  892. return "", errors.Wrapf(err, "DetachDisk")
  893. }
  894. err = self.AttachDisk(instance.GetId(), tempRootDiskId, rootDisk.getDevice())
  895. if err != nil {
  896. self.DeleteDisk(tempRootDiskId)
  897. self.AttachDisk(instance.GetId(), rootDisk.VolumeId, rootDisk.getDevice())
  898. return "", errors.Wrapf(err, "ttachDisk")
  899. }
  900. err = self.ModifyInstanceAttribute(instance.InstanceId, &SInstanceAttr{UserData: userdata})
  901. if err != nil {
  902. self.DeleteDisk(tempRootDiskId)
  903. self.AttachDisk(instance.GetId(), rootDisk.VolumeId, rootDisk.getDevice())
  904. return "", errors.Wrapf(err, "ModifyInstanceAttribute")
  905. }
  906. err = self.DeleteDisk(rootDisk.VolumeId)
  907. if err != nil {
  908. log.Errorf("DeleteDisk %s", rootDisk.VolumeId)
  909. }
  910. return tempRootDiskId, nil
  911. }
  912. func (self *SRegion) ChangeVMConfig2(instanceId string, instanceType string) error {
  913. return self.ModifyInstanceAttribute(instanceId, &SInstanceAttr{InstanceType: instanceType})
  914. }
  915. func (self *SRegion) DetachDisk(instanceId string, diskId string) error {
  916. params := map[string]string{
  917. "InstanceId": instanceId,
  918. "VolumeId": diskId,
  919. }
  920. ret := struct{}{}
  921. err := self.ec2Request("DetachVolume", params, &ret)
  922. if err != nil {
  923. if strings.Contains(err.Error(), "in the 'available' state") {
  924. return nil
  925. }
  926. if errors.Cause(err) == cloudprovider.ErrNotFound {
  927. return nil
  928. }
  929. return errors.Wrapf(err, "DetachVolume")
  930. }
  931. return nil
  932. }
  933. func (self *SRegion) AttachDisk(instanceId string, diskId string, deviceName string) error {
  934. params := map[string]string{
  935. "InstanceId": instanceId,
  936. "VolumeId": diskId,
  937. "Device": deviceName,
  938. }
  939. ret := struct{}{}
  940. return self.ec2Request("AttachVolume", params, &ret)
  941. }
  942. type SInstanceAttr struct {
  943. DisableApiTermination *bool
  944. InstanceType string
  945. UserData string
  946. }
  947. func (self *SRegion) ModifyInstanceAttribute(instanceId string, opts *SInstanceAttr) error {
  948. params := map[string]string{
  949. "InstanceId": instanceId,
  950. }
  951. if len(opts.InstanceType) > 0 {
  952. params["InstanceType.Value"] = opts.InstanceType
  953. }
  954. if len(opts.UserData) > 0 {
  955. params["UserData.Value"] = opts.UserData
  956. }
  957. if opts.DisableApiTermination != nil {
  958. params["DisableApiTermination.Value"] = fmt.Sprintf("%v", opts.DisableApiTermination)
  959. }
  960. ret := struct{}{}
  961. return self.ec2Request("ModifyInstanceAttribute", params, &ret)
  962. }
  963. func (self *SRegion) GetPasswordData(instanceId string) (string, error) {
  964. params := map[string]string{
  965. "InstanceId": instanceId,
  966. }
  967. ret := struct {
  968. PasswordData string `xml:"passwordData"`
  969. }{}
  970. err := self.ec2Request("GetPasswordData", params, &ret)
  971. if err != nil {
  972. return "", errors.Wrapf(err, "GetPasswordData")
  973. }
  974. return ret.PasswordData, nil
  975. }
  976. func (self *SInstance) Renew(bc billing.SBillingCycle) error {
  977. return cloudprovider.ErrNotSupported
  978. }
  979. func (self *SInstance) GetProjectId() string {
  980. return ""
  981. }
  982. func (self *SInstance) GetError() error {
  983. return nil
  984. }
  985. func (self *SInstance) SetTags(tags map[string]string, replace bool) error {
  986. return self.host.zone.region.setTags("instance", self.InstanceId, tags, replace)
  987. }
  988. func (self *SInstance) GetAccountId() string {
  989. identity, err := self.host.zone.region.client.GetCallerIdentity()
  990. if err != nil {
  991. log.Errorf("GetCallerIdentity %v", err)
  992. return ""
  993. }
  994. return identity.Account
  995. }
  996. func (self *SRegion) SaveImage(instanceId string, opts *cloudprovider.SaveImageOptions) (*SImage, error) {
  997. params := map[string]string{
  998. "Description": opts.Notes,
  999. "InstanceId": instanceId,
  1000. "Name": opts.Name,
  1001. }
  1002. ret := struct {
  1003. ImageId string `xml:"imageId"`
  1004. }{}
  1005. err := self.ec2Request("CreateImage", params, &ret)
  1006. if err != nil {
  1007. return nil, errors.Wrapf(err, "CreateImage")
  1008. }
  1009. err = cloudprovider.Wait(time.Second*10, time.Minute*5, func() (bool, error) {
  1010. _, err := self.GetImage(ret.ImageId)
  1011. if err != nil {
  1012. if errors.Cause(err) == cloudprovider.ErrNotFound {
  1013. return false, nil
  1014. }
  1015. return false, errors.Wrapf(err, "GetImage(%s)", ret.ImageId)
  1016. }
  1017. return true, nil
  1018. })
  1019. if err != nil {
  1020. return nil, errors.Wrapf(err, "wait for image created")
  1021. }
  1022. image, err := self.GetImage(ret.ImageId)
  1023. if err != nil {
  1024. return nil, errors.Wrapf(err, "GetImage(%s)", ret.ImageId)
  1025. }
  1026. image.storageCache = self.getStorageCache()
  1027. return image, nil
  1028. }
  1029. func (self *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
  1030. image, err := self.host.zone.region.SaveImage(self.InstanceId, opts)
  1031. if err != nil {
  1032. return nil, errors.Wrapf(err, "SaveImage")
  1033. }
  1034. return image, nil
  1035. }
  1036. func (ins *SInstance) GetDescription() string {
  1037. return ins.AwsTags.GetDescription()
  1038. }