yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/instance.go (about)

     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  
    15  package aws
    16  
    17  import (
    18  	"context"
    19  	"encoding/base64"
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/aws/aws-sdk-go/service/ec2"
    25  
    26  	"yunion.io/x/jsonutils"
    27  	"yunion.io/x/log"
    28  	"yunion.io/x/pkg/errors"
    29  	"yunion.io/x/pkg/util/osprofile"
    30  	"yunion.io/x/pkg/utils"
    31  
    32  	"yunion.io/x/cloudmux/pkg/apis"
    33  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    34  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    35  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    36  	"yunion.io/x/onecloud/pkg/compute/options"
    37  	"yunion.io/x/cloudmux/pkg/multicloud"
    38  	"yunion.io/x/onecloud/pkg/util/billing"
    39  	"yunion.io/x/onecloud/pkg/util/cloudinit"
    40  )
    41  
    42  const (
    43  	InstanceStatusPending    = "pending"
    44  	InstanceStatusRunning    = "running"
    45  	InstanceStatusShutting   = "shutting-down"
    46  	InstanceStatusTerminated = "terminated"
    47  	InstanceStatusStopping   = "stopping"
    48  	InstanceStatusStopped    = "stopped"
    49  )
    50  
    51  type InstanceChargeType string
    52  
    53  type SIpAddress struct {
    54  	IpAddress []string
    55  }
    56  
    57  type SSecurityGroupIds struct {
    58  	SecurityGroupId []string
    59  }
    60  
    61  type SVpcAttributes struct {
    62  	PrivateIpAddress SIpAddress
    63  	NetworkId        string // subnet id
    64  	VpcId            string
    65  }
    66  
    67  type SInstance struct {
    68  	multicloud.SInstanceBase
    69  
    70  	host       *SHost
    71  	img        *SImage
    72  	RegionId   string
    73  	ZoneId     string
    74  	InstanceId string
    75  	ImageId    string
    76  
    77  	HostName                string
    78  	InstanceName            string
    79  	InstanceType            string
    80  	Cpu                     int
    81  	Memory                  int // MB
    82  	IoOptimized             bool
    83  	KeyPairName             string
    84  	CreationTime            time.Time // LaunchTime
    85  	ExpiredTime             time.Time
    86  	ProductCodes            []string
    87  	PublicDNSName           string
    88  	InnerIpAddress          SIpAddress
    89  	PublicIpAddress         SIpAddress
    90  	RootDeviceName          string
    91  	Status                  string // state
    92  	VlanId                  string // subnet ID ?
    93  	VpcAttributes           SVpcAttributes
    94  	SecurityGroupIds        SSecurityGroupIds
    95  	NetworkInterfaces       []SNetworkInterface
    96  	EipAddress              SEipAddress
    97  	Disks                   []string
    98  	DeviceNames             []string
    99  	OSName                  string
   100  	OSType                  string
   101  	Description             string
   102  	InternetMaxBandwidthOut int
   103  	Throughput              int
   104  
   105  	TagSpec TagSpec
   106  
   107  	// 这些貌似都没啥用
   108  	// AutoReleaseTime         string
   109  	// DeviceAvailable         bool
   110  	// GPUAmount               int
   111  	// GPUSpec                 string
   112  	// InstanceChargeType      InstanceChargeType
   113  	// InstanceNetworkType     string
   114  	// InstanceTypeFamily      string
   115  	// InternetChargeType      string
   116  	// InternetMaxBandwidthIn  int
   117  	// InternetMaxBandwidthOut int
   118  	// OperationLocks          SOperationLocks
   119  	// Recyclable              bool
   120  	// SerialNumber            string
   121  	// SpotPriceLimit          string
   122  	// SpotStrategy            string
   123  	// StartTime               time.Time
   124  	// StoppedMode             string
   125  }
   126  
   127  func (self *SInstance) UpdateUserData(userData string) error {
   128  	udata := &ec2.BlobAttributeValue{}
   129  	udata.SetValue([]byte(userData))
   130  
   131  	input := &ec2.ModifyInstanceAttributeInput{}
   132  	input.SetUserData(udata)
   133  	input.SetInstanceId(self.GetId())
   134  	ec2Client, err := self.host.zone.region.getEc2Client()
   135  	if err != nil {
   136  		return errors.Wrap(err, "getEc2Client")
   137  	}
   138  	_, err = ec2Client.ModifyInstanceAttribute(input)
   139  	if err != nil {
   140  		return errors.Wrap(err, "ModifyInstanceAttribute")
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  func (self *SInstance) GetUserData() (string, error) {
   147  	input := &ec2.DescribeInstanceAttributeInput{}
   148  	input.SetInstanceId(self.GetId())
   149  	input.SetAttribute("userData")
   150  	ec2Client, err := self.host.zone.region.getEc2Client()
   151  	if err != nil {
   152  		return "", errors.Wrap(err, "getEc2Client")
   153  	}
   154  	ret, err := ec2Client.DescribeInstanceAttribute(input)
   155  	if err != nil {
   156  		return "", err
   157  	}
   158  
   159  	d := StrVal(ret.UserData.Value)
   160  	udata, err := base64.StdEncoding.DecodeString(d)
   161  	if err != nil {
   162  		return "", fmt.Errorf("GetUserData decode user data %s", err)
   163  	}
   164  
   165  	return string(udata), nil
   166  }
   167  
   168  func (self *SInstance) GetId() string {
   169  	return self.InstanceId
   170  }
   171  
   172  func (self *SInstance) GetName() string {
   173  	if len(self.InstanceName) > 0 {
   174  		return self.InstanceName
   175  	}
   176  
   177  	return self.GetId()
   178  }
   179  
   180  func (self *SInstance) GetHostname() string {
   181  	return self.GetName()
   182  }
   183  
   184  func (self *SInstance) GetGlobalId() string {
   185  	return self.InstanceId
   186  }
   187  
   188  func (self *SInstance) GetStatus() string {
   189  	switch self.Status {
   190  	case InstanceStatusRunning:
   191  		return api.VM_RUNNING
   192  	case InstanceStatusPending: // todo: pending ?
   193  		return api.VM_STARTING
   194  	case InstanceStatusStopping:
   195  		return api.VM_STOPPING
   196  	case InstanceStatusStopped:
   197  		return api.VM_READY
   198  	default:
   199  		return api.VM_UNKNOWN
   200  	}
   201  }
   202  
   203  func (self *SInstance) Refresh() error {
   204  	new, err := self.host.zone.region.GetInstance(self.InstanceId)
   205  	if err != nil {
   206  		return err
   207  	}
   208  	return jsonutils.Update(self, new)
   209  }
   210  
   211  func (self *SInstance) IsEmulated() bool {
   212  	return false
   213  }
   214  
   215  func (self *SInstance) GetInstanceType() string {
   216  	return self.InstanceType
   217  }
   218  
   219  func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
   220  	return self.SecurityGroupIds.SecurityGroupId, nil
   221  }
   222  
   223  func (self *SInstance) GetTags() (map[string]string, error) {
   224  	return self.TagSpec.GetTags()
   225  }
   226  
   227  func (self *SInstance) GetSysTags() map[string]string {
   228  	return map[string]string{}
   229  }
   230  
   231  func (self *SInstance) GetBillingType() string {
   232  	// todo: implement me
   233  	return billing_api.BILLING_TYPE_POSTPAID
   234  }
   235  
   236  func (self *SInstance) GetCreatedAt() time.Time {
   237  	return self.CreationTime
   238  }
   239  
   240  func (self *SInstance) GetExpiredAt() time.Time {
   241  	return self.ExpiredTime
   242  }
   243  
   244  func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
   245  	return self.host
   246  }
   247  
   248  func (self *SInstance) GetThroughput() int {
   249  	return self.Throughput
   250  }
   251  
   252  func (self *SInstance) GetInternetMaxBandwidthOut() int {
   253  	return self.InternetMaxBandwidthOut
   254  }
   255  
   256  func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
   257  	disks, _, err := self.host.zone.region.GetDisks(self.InstanceId, "", "", nil, 0, 0)
   258  	if err != nil {
   259  		log.Errorf("fetchDisks fail %s", err)
   260  		return nil, errors.Wrap(err, "GetDisks")
   261  	}
   262  
   263  	idisks := make([]cloudprovider.ICloudDisk, len(disks))
   264  	for i := 0; i < len(disks); i += 1 {
   265  		store, err := self.host.zone.getStorageByCategory(disks[i].Category)
   266  		if err != nil {
   267  			return nil, errors.Wrap(err, "getStorageByCategory")
   268  		}
   269  		disks[i].storage = store
   270  		idisks[i] = &disks[i]
   271  	}
   272  	return idisks, nil
   273  }
   274  
   275  func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
   276  	var (
   277  		networkInterfaces = self.NetworkInterfaces
   278  		nics              = make([]cloudprovider.ICloudNic, 0)
   279  	)
   280  	for _, networkInterface := range networkInterfaces {
   281  		nic := SInstanceNic{
   282  			instance: self,
   283  			id:       networkInterface.NetworkInterfaceId,
   284  			ipAddr:   networkInterface.PrivateIpAddress,
   285  			macAddr:  networkInterface.MacAddress,
   286  		}
   287  		nics = append(nics, &nic)
   288  	}
   289  	return nics, nil
   290  }
   291  
   292  func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
   293  	if len(self.EipAddress.PublicIp) > 0 {
   294  		return self.host.zone.region.GetEipByIpAddress(self.EipAddress.PublicIp)
   295  	}
   296  	if len(self.PublicIpAddress.IpAddress) > 0 {
   297  		eip := SEipAddress{region: self.host.zone.region}
   298  		eip.region = self.host.zone.region
   299  		eip.PublicIp = self.PublicIpAddress.IpAddress[0]
   300  		eip.InstanceId = self.InstanceId
   301  		eip.AllocationId = self.InstanceId // fixed. AllocationId等于InstanceId即表示为 仿真EIP。
   302  		return &eip, nil
   303  	}
   304  	return nil, nil
   305  }
   306  
   307  func (self *SInstance) GetVcpuCount() int {
   308  	return self.Cpu
   309  }
   310  
   311  func (self *SInstance) GetVmemSizeMB() int {
   312  	return self.Memory
   313  }
   314  
   315  func (self *SInstance) GetBootOrder() string {
   316  	return "dcn"
   317  }
   318  
   319  func (self *SInstance) GetVga() string {
   320  	return "std"
   321  }
   322  
   323  func (self *SInstance) GetVdi() string {
   324  	return "vnc"
   325  }
   326  
   327  func (self *SInstance) GetOsType() cloudprovider.TOsType {
   328  	return cloudprovider.TOsType(osprofile.NormalizeOSType(self.OSType))
   329  }
   330  
   331  func (self *SInstance) GetFullOsName() string {
   332  	return self.OSName
   333  }
   334  
   335  func (self *SInstance) GetBios() cloudprovider.TBiosType {
   336  	img, err := self.GetImage()
   337  	if err != nil {
   338  		log.Errorf("GetImage fail %s", err)
   339  		return cloudprovider.BIOS
   340  	}
   341  	return img.GetBios()
   342  }
   343  
   344  func (self *SInstance) GetOsArch() string {
   345  	img, err := self.GetImage()
   346  	if err != nil {
   347  		log.Errorf("GetImage fail %s", err)
   348  		return apis.OS_ARCH_X86_64
   349  	}
   350  	return img.GetOsArch()
   351  }
   352  
   353  func (self *SInstance) GetOsDist() string {
   354  	img, err := self.GetImage()
   355  	if err != nil {
   356  		log.Errorf("GetImage fail %s", err)
   357  		return ""
   358  	}
   359  	return img.GetOsDist()
   360  }
   361  
   362  func (self *SInstance) GetOsVersion() string {
   363  	img, err := self.GetImage()
   364  	if err != nil {
   365  		log.Errorf("GetImage fail %s", err)
   366  		return ""
   367  	}
   368  	return img.GetOsVersion()
   369  }
   370  
   371  func (self *SInstance) GetOsLang() string {
   372  	img, err := self.GetImage()
   373  	if err != nil {
   374  		log.Errorf("GetImage fail %s", err)
   375  		return ""
   376  	}
   377  	return img.GetOsLang()
   378  }
   379  
   380  func (self *SInstance) GetMachine() string {
   381  	return "pc"
   382  }
   383  
   384  func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
   385  	return self.SetSecurityGroups([]string{secgroupId})
   386  }
   387  
   388  func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
   389  	ids := []*string{}
   390  	for i := 0; i < len(secgroupIds); i++ {
   391  		ids = append(ids, &secgroupIds[i])
   392  	}
   393  	return self.host.zone.region.assignSecurityGroups(ids, self.InstanceId)
   394  }
   395  
   396  func (self *SInstance) GetHypervisor() string {
   397  	return api.HYPERVISOR_AWS
   398  }
   399  
   400  func (self *SInstance) StartVM(ctx context.Context) error {
   401  	timeout := 300 * time.Second
   402  	interval := 15 * time.Second
   403  
   404  	startTime := time.Now()
   405  	for time.Now().Sub(startTime) < timeout {
   406  		err := self.Refresh()
   407  		if err != nil {
   408  			return err
   409  		}
   410  
   411  		if self.GetStatus() == api.VM_RUNNING {
   412  			return nil
   413  		} else if self.GetStatus() == api.VM_READY {
   414  			err := self.host.zone.region.StartVM(self.InstanceId)
   415  			if err != nil {
   416  				return err
   417  			}
   418  		}
   419  		time.Sleep(interval)
   420  	}
   421  	return cloudprovider.ErrTimeout
   422  }
   423  
   424  func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
   425  	err := self.host.zone.region.StopVM(self.InstanceId, opts.IsForce)
   426  	if err != nil {
   427  		return err
   428  	}
   429  	return cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second) // 5mintues
   430  }
   431  
   432  func (self *SInstance) DeleteVM(ctx context.Context) error {
   433  	for {
   434  		err := self.host.zone.region.DeleteVM(self.InstanceId)
   435  		if err != nil && self.Status != InstanceStatusTerminated {
   436  			return err
   437  		} else {
   438  			break
   439  		}
   440  	}
   441  
   442  	params := &ec2.DescribeInstancesInput{InstanceIds: []*string{&self.InstanceId}}
   443  	ec2Client, err := self.host.zone.region.getEc2Client()
   444  	if err != nil {
   445  		return errors.Wrap(err, "getEc2Client")
   446  	}
   447  
   448  	return ec2Client.WaitUntilInstanceTerminated(params)
   449  }
   450  
   451  func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
   452  	addTags := map[string]string{}
   453  	addTags["Name"] = name
   454  	Arn := self.GetArn()
   455  	err := self.host.zone.region.TagResources([]string{Arn}, addTags)
   456  	if err != nil {
   457  		return errors.Wrapf(err, "self.host.zone.region.TagResources([]string{%s}, %s)", Arn, jsonutils.Marshal(addTags).String())
   458  	}
   459  	return nil
   460  }
   461  
   462  func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
   463  	udata, err := self.GetUserData()
   464  	if err != nil {
   465  		return "", err
   466  	}
   467  
   468  	// compare sysSizeGB
   469  	image, err := self.host.zone.region.GetImage(desc.ImageId)
   470  	if err != nil {
   471  		return "", err
   472  	} else {
   473  		minSizeGB := image.GetMinOsDiskSizeGb()
   474  		if minSizeGB > desc.SysSizeGB {
   475  			desc.SysSizeGB = minSizeGB
   476  		}
   477  	}
   478  
   479  	// upload keypair
   480  	keypairName := self.KeyPairName
   481  	if len(desc.PublicKey) > 0 {
   482  		keypairName, err = self.host.zone.region.syncKeypair(desc.PublicKey)
   483  		if err != nil {
   484  			return "", fmt.Errorf("RebuildRoot.syncKeypair %s", err)
   485  		}
   486  	}
   487  
   488  	userdata := ""
   489  	srcOsType := strings.ToLower(string(self.GetOsType()))
   490  	destOsType := strings.ToLower(string(image.GetOsType()))
   491  	winOS := strings.ToLower(osprofile.OS_TYPE_WINDOWS)
   492  
   493  	cloudconfig := &cloudinit.SCloudConfig{}
   494  	if srcOsType != winOS && len(udata) > 0 {
   495  		_cloudconfig, err := cloudinit.ParseUserDataBase64(udata)
   496  		if err != nil {
   497  			// 忽略无效的用户数据
   498  			log.Debugf("RebuildRoot invalid instance user data %s", udata)
   499  		} else {
   500  			cloudconfig = _cloudconfig
   501  		}
   502  	}
   503  
   504  	if (srcOsType != winOS && destOsType != winOS) || (srcOsType == winOS && destOsType != winOS) {
   505  		// linux/windows to linux
   506  		loginUser := cloudinit.NewUser(api.VM_AWS_DEFAULT_LOGIN_USER)
   507  		loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD)
   508  		if len(desc.PublicKey) > 0 {
   509  			loginUser.SshKey(desc.PublicKey)
   510  			cloudconfig.MergeUser(loginUser)
   511  		} else if len(desc.Password) > 0 {
   512  			cloudconfig.SshPwauth = cloudinit.SSH_PASSWORD_AUTH_ON
   513  			loginUser.Password(desc.Password)
   514  			cloudconfig.MergeUser(loginUser)
   515  		}
   516  
   517  		userdata = cloudconfig.UserDataBase64()
   518  	} else {
   519  		// linux/windows to windows
   520  		data := ""
   521  		if len(desc.Password) > 0 {
   522  			cloudconfig.SshPwauth = cloudinit.SSH_PASSWORD_AUTH_ON
   523  			loginUser := cloudinit.NewUser(api.VM_AWS_DEFAULT_WINDOWS_LOGIN_USER)
   524  			loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD)
   525  			loginUser.Password(desc.Password)
   526  			cloudconfig.MergeUser(loginUser)
   527  			data = fmt.Sprintf("<powershell>%s</powershell>", cloudconfig.UserDataPowerShell())
   528  		} else {
   529  			if len(udata) > 0 {
   530  				data = fmt.Sprintf("<powershell>%s</powershell>", udata)
   531  			}
   532  		}
   533  
   534  		userdata = base64.StdEncoding.EncodeToString([]byte(data))
   535  	}
   536  
   537  	diskId, err := self.host.zone.region.ReplaceSystemDisk(ctx, self.InstanceId, image, desc.SysSizeGB, keypairName, userdata)
   538  	if err != nil {
   539  		return "", err
   540  	}
   541  
   542  	return diskId, nil
   543  }
   544  
   545  func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
   546  	return self.host.zone.region.DeployVM(self.InstanceId, name, password, publicKey, deleteKeypair, description)
   547  }
   548  
   549  func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
   550  	if len(config.InstanceType) > 0 {
   551  		return self.ChangeConfig2(ctx, config.InstanceType)
   552  	}
   553  	return errors.Wrap(errors.ErrClient, "Instance.ChangeConfig.InstanceTypeIsEmpty")
   554  }
   555  
   556  func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
   557  	return self.host.zone.region.ChangeVMConfig2(self.ZoneId, self.InstanceId, instanceType, nil)
   558  }
   559  
   560  func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
   561  	return nil, cloudprovider.ErrNotSupported
   562  }
   563  
   564  func (self *SInstance) GetImage() (*SImage, error) {
   565  	if self.img != nil {
   566  		return self.img, nil
   567  	}
   568  
   569  	img, err := self.host.zone.region.GetImage(self.ImageId)
   570  	if err != nil {
   571  		return nil, errors.Wrap(err, "GetImage")
   572  	}
   573  
   574  	self.img = img
   575  	return self.img, nil
   576  }
   577  
   578  func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
   579  	img, err := self.GetImage()
   580  	if err != nil {
   581  		return errors.Wrap(err, "GetImage")
   582  	}
   583  
   584  	// mix in image block device names
   585  	for i := range img.BlockDevicesNames {
   586  		if !utils.IsInStringArray(img.BlockDevicesNames[i], self.DeviceNames) {
   587  			self.DeviceNames = append(self.DeviceNames, img.BlockDevicesNames[i])
   588  		}
   589  	}
   590  
   591  	name, err := NextDeviceName(self.DeviceNames)
   592  	if err != nil {
   593  		return err
   594  	}
   595  
   596  	err = self.host.zone.region.AttachDisk(self.InstanceId, diskId, name)
   597  	if err != nil {
   598  		return err
   599  	}
   600  
   601  	self.DeviceNames = append(self.DeviceNames, name)
   602  	return nil
   603  }
   604  
   605  func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
   606  	return self.host.zone.region.DetachDisk(self.InstanceId, diskId)
   607  }
   608  
   609  func (self *SInstance) getVpc() (*SVpc, error) {
   610  	return self.host.zone.region.getVpc(self.VpcAttributes.VpcId)
   611  }
   612  
   613  func (self *SRegion) GetInstances(zoneId string, ids []string, offset int, limit int) ([]SInstance, int, error) {
   614  	params := &ec2.DescribeInstancesInput{}
   615  	filters := make([]*ec2.Filter, 0)
   616  	if len(zoneId) > 0 {
   617  		filters = AppendSingleValueFilter(filters, "availability-zone", zoneId)
   618  	}
   619  
   620  	if len(ids) > 0 {
   621  		params = params.SetInstanceIds(ConvertedList(ids))
   622  	}
   623  
   624  	if len(filters) > 0 {
   625  		params = params.SetFilters(filters)
   626  	}
   627  
   628  	ec2Client, err := self.getEc2Client()
   629  	if err != nil {
   630  		return nil, 0, errors.Wrap(err, "getEc2Client")
   631  	}
   632  	res, err := ec2Client.DescribeInstances(params)
   633  	if err != nil {
   634  		if strings.Contains(err.Error(), "InvalidInstanceID.NotFound") {
   635  			return nil, 0, errors.Wrap(cloudprovider.ErrNotFound, "DescribeInstances")
   636  		} else {
   637  			return nil, 0, err
   638  		}
   639  	}
   640  
   641  	instances := []SInstance{}
   642  	for _, reservation := range res.Reservations {
   643  		for _, instance := range reservation.Instances {
   644  			if err := FillZero(instance); err != nil {
   645  				return nil, 0, err
   646  			}
   647  
   648  			// 不同步已经terminated的主机
   649  			if *instance.State.Name == ec2.InstanceStateNameTerminated {
   650  				continue
   651  			}
   652  
   653  			tagspec := TagSpec{}
   654  			tagspec.LoadingEc2Tags(instance.Tags)
   655  
   656  			disks := []string{}
   657  			devicenames := []string{}
   658  			for _, d := range instance.BlockDeviceMappings {
   659  				if d.Ebs != nil && d.Ebs.VolumeId != nil {
   660  					disks = append(disks, *d.Ebs.VolumeId)
   661  					devicenames = append(devicenames, *d.DeviceName)
   662  				}
   663  			}
   664  
   665  			var secgroups SSecurityGroupIds
   666  			for _, s := range instance.SecurityGroups {
   667  				if s.GroupId != nil {
   668  					secgroups.SecurityGroupId = append(secgroups.SecurityGroupId, *s.GroupId)
   669  				}
   670  			}
   671  
   672  			networkInterfaces := []SNetworkInterface{}
   673  			eipAddress := SEipAddress{}
   674  			for _, n := range instance.NetworkInterfaces {
   675  				i := SNetworkInterface{
   676  					MacAddress:         *n.MacAddress,
   677  					NetworkInterfaceId: *n.NetworkInterfaceId,
   678  					PrivateIpAddress:   *n.PrivateIpAddress,
   679  				}
   680  				networkInterfaces = append(networkInterfaces, i)
   681  
   682  				// todo: 可能有多个EIP的情况。目前只支持一个EIP
   683  				if n.Association != nil && StrVal(n.Association.IpOwnerId) != "amazon" {
   684  					if eipAddress.PublicIp == "" && len(StrVal(n.Association.PublicIp)) > 0 {
   685  						eipAddress.PublicIp = *n.Association.PublicIp
   686  					}
   687  				}
   688  			}
   689  
   690  			var vpcattr SVpcAttributes
   691  			vpcattr.VpcId = *instance.VpcId
   692  			vpcattr.PrivateIpAddress = SIpAddress{[]string{*instance.PrivateIpAddress}}
   693  			vpcattr.NetworkId = *instance.SubnetId
   694  
   695  			var productCodes []string
   696  			for _, p := range instance.ProductCodes {
   697  				productCodes = append(productCodes, *p.ProductCodeId)
   698  			}
   699  
   700  			publicIpAddress := SIpAddress{}
   701  			if len(*instance.PublicIpAddress) > 0 {
   702  				publicIpAddress.IpAddress = []string{*instance.PublicIpAddress}
   703  			}
   704  
   705  			innerIpAddress := SIpAddress{}
   706  			if len(*instance.PrivateIpAddress) > 0 {
   707  				innerIpAddress.IpAddress = []string{*instance.PrivateIpAddress}
   708  			}
   709  
   710  			szone, err := self.getZoneById(*instance.Placement.AvailabilityZone)
   711  			if err != nil {
   712  				log.Errorf("getZoneById %s fail %s", *instance.Placement.AvailabilityZone, err)
   713  				return nil, 0, err
   714  			}
   715  
   716  			osType := "Linux"
   717  			if instance.Platform != nil && len(*instance.Platform) > 0 {
   718  				osType = *instance.Platform
   719  			}
   720  
   721  			host := szone.getHost()
   722  			vcpu := int(*instance.CpuOptions.CoreCount) * int(*instance.CpuOptions.ThreadsPerCore)
   723  			sinstance := SInstance{
   724  				RegionId:          self.RegionId,
   725  				host:              host,
   726  				ZoneId:            *instance.Placement.AvailabilityZone,
   727  				InstanceId:        *instance.InstanceId,
   728  				ImageId:           *instance.ImageId,
   729  				InstanceType:      *instance.InstanceType,
   730  				Cpu:               vcpu,
   731  				IoOptimized:       *instance.EbsOptimized,
   732  				KeyPairName:       *instance.KeyName,
   733  				CreationTime:      *instance.LaunchTime,
   734  				PublicDNSName:     *instance.PublicDnsName,
   735  				RootDeviceName:    *instance.RootDeviceName,
   736  				Status:            *instance.State.Name,
   737  				InnerIpAddress:    innerIpAddress,
   738  				PublicIpAddress:   publicIpAddress,
   739  				EipAddress:        eipAddress,
   740  				InstanceName:      tagspec.GetNameTag(),
   741  				Description:       tagspec.GetDescTag(),
   742  				Disks:             disks,
   743  				DeviceNames:       devicenames,
   744  				SecurityGroupIds:  secgroups,
   745  				NetworkInterfaces: networkInterfaces,
   746  				VpcAttributes:     vpcattr,
   747  				ProductCodes:      productCodes,
   748  				OSName:            osType, // todo: 这里在model层回写OSName信息
   749  				OSType:            osType,
   750  
   751  				TagSpec: tagspec,
   752  				// ExpiredTime:
   753  				// VlanId:
   754  				// OSType:
   755  			}
   756  
   757  			instances = append(instances, sinstance)
   758  		}
   759  	}
   760  
   761  	return instances, len(instances), nil
   762  }
   763  
   764  func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) {
   765  	if len(instanceId) == 0 {
   766  		return nil, fmt.Errorf("GetInstance instanceId should not be empty.")
   767  	}
   768  
   769  	instances, _, err := self.GetInstances("", []string{instanceId}, 0, 1)
   770  	if err != nil {
   771  		log.Errorf("GetInstances %s: %s", instanceId, err)
   772  		return nil, errors.Wrap(err, "GetInstances")
   773  	}
   774  	if len(instances) == 0 {
   775  		return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetInstances")
   776  	}
   777  	return &instances[0], nil
   778  }
   779  
   780  func (self *SRegion) GetInstanceIdByImageId(imageId string) (string, error) {
   781  	params := &ec2.DescribeInstancesInput{}
   782  	filters := []*ec2.Filter{}
   783  	filters = AppendSingleValueFilter(filters, "image-id", imageId)
   784  	params.SetFilters(filters)
   785  
   786  	ec2Client, err := self.getEc2Client()
   787  	if err != nil {
   788  		return "", errors.Wrap(err, "getEc2Client")
   789  	}
   790  	ret, err := ec2Client.DescribeInstances(params)
   791  	if err != nil {
   792  		return "", err
   793  	}
   794  
   795  	for _, item := range ret.Reservations {
   796  		for _, instance := range item.Instances {
   797  			return *instance.InstanceId, nil
   798  		}
   799  	}
   800  	return "", fmt.Errorf("instance launch with image %s not found", imageId)
   801  }
   802  
   803  func (self *SRegion) CreateInstance(name string, image *SImage, instanceType string, SubnetId string, securityGroupId string,
   804  	zoneId string, desc string, disks []SDisk, ipAddr string,
   805  	keypair string, userData string, ntags map[string]string,
   806  ) (string, error) {
   807  	var count int64 = 1
   808  	// disk
   809  	blockDevices := []*ec2.BlockDeviceMapping{}
   810  	for i := range disks {
   811  		var ebs ec2.EbsBlockDevice
   812  		var deviceName string
   813  		var err error
   814  		disk := disks[i]
   815  
   816  		if i == 0 {
   817  			var size int64
   818  			deleteOnTermination := true
   819  			size = int64(disk.Size)
   820  			ebs = ec2.EbsBlockDevice{
   821  				DeleteOnTermination: &deleteOnTermination,
   822  				// The st1 volume type cannot be used for boot volumes. Please use a supported boot volume type: standard,io1,gp2.
   823  				// the encrypted flag cannot be specified since device /dev/sda1 has a snapshot specified.
   824  				// Encrypted:           &disk.Encrypted,
   825  				VolumeSize: &size,
   826  				VolumeType: &disk.Category,
   827  			}
   828  
   829  			if len(image.RootDeviceName) > 0 {
   830  				deviceName = image.RootDeviceName
   831  			} else {
   832  				deviceName = fmt.Sprintf("/dev/sda1")
   833  			}
   834  		} else {
   835  			var size int64
   836  			size = int64(disk.Size)
   837  			ebs = ec2.EbsBlockDevice{
   838  				DeleteOnTermination: &disk.DeleteWithInstance,
   839  				Encrypted:           &disk.Encrypted,
   840  				VolumeSize:          &size,
   841  				VolumeType:          &disk.Category,
   842  			}
   843  
   844  			deviceName, err = NextDeviceName(image.BlockDevicesNames)
   845  			if err != nil {
   846  				return "", errors.Wrap(err, "NextDeviceName")
   847  			}
   848  		}
   849  
   850  		if iops := GenDiskIops(disk.Category, disk.Size); iops > 0 {
   851  			ebs.SetIops(iops)
   852  		}
   853  
   854  		blockDevice := &ec2.BlockDeviceMapping{
   855  			DeviceName: &deviceName,
   856  			Ebs:        &ebs,
   857  		}
   858  
   859  		blockDevices = append(blockDevices, blockDevice)
   860  	}
   861  
   862  	// tags
   863  	tags := TagSpec{ResourceType: "instance"}
   864  	tags.SetNameTag(name)
   865  	tags.SetDescTag(desc)
   866  
   867  	if len(ntags) > 0 {
   868  		for k, v := range ntags {
   869  			tags.SetTag(k, v)
   870  		}
   871  	}
   872  
   873  	ec2TagSpec, err := tags.GetTagSpecifications()
   874  	if err != nil {
   875  		return "", err
   876  	}
   877  
   878  	imgId := image.GetId()
   879  	params := ec2.RunInstancesInput{
   880  		ImageId:             &imgId,
   881  		InstanceType:        &instanceType,
   882  		MaxCount:            &count,
   883  		MinCount:            &count,
   884  		BlockDeviceMappings: blockDevices,
   885  		Placement:           &ec2.Placement{AvailabilityZone: &zoneId},
   886  		TagSpecifications:   []*ec2.TagSpecification{ec2TagSpec},
   887  	}
   888  	params.Monitoring = &ec2.RunInstancesMonitoringEnabled{
   889  		Enabled: &options.Options.EnableAwsMonitorAgent,
   890  	}
   891  
   892  	// keypair
   893  	if len(keypair) > 0 {
   894  		params.SetKeyName(keypair)
   895  	}
   896  
   897  	// user data
   898  	if len(userData) > 0 {
   899  		params.SetUserData(userData)
   900  	}
   901  
   902  	// ip address
   903  	if len(ipAddr) > 0 {
   904  		params.SetPrivateIpAddress(ipAddr)
   905  	}
   906  
   907  	// subnet id
   908  	if len(SubnetId) > 0 {
   909  		params.SetSubnetId(SubnetId)
   910  	}
   911  
   912  	// security group
   913  	if len(securityGroupId) > 0 {
   914  		params.SetSecurityGroupIds([]*string{&securityGroupId})
   915  	}
   916  
   917  	ec2Client, err := self.getEc2Client()
   918  	if err != nil {
   919  		return "", errors.Wrap(err, "getEc2Client")
   920  	}
   921  	res, err := ec2Client.RunInstances(&params)
   922  	if err != nil {
   923  		log.Errorf("CreateInstance fail %s", err)
   924  		return "", err
   925  	}
   926  
   927  	if len(res.Instances) == 1 {
   928  		return *res.Instances[0].InstanceId, nil
   929  	} else {
   930  		msg := fmt.Sprintf("CreateInstance fail: %d instance created. ", len(res.Instances))
   931  		log.Errorf(msg)
   932  		return "", fmt.Errorf(msg)
   933  	}
   934  }
   935  
   936  func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) {
   937  	instance, err := self.GetInstance(instanceId)
   938  	if err != nil {
   939  		return "", err
   940  	}
   941  	return instance.Status, nil
   942  }
   943  
   944  func (self *SRegion) StartVM(instanceId string) error {
   945  	params := &ec2.StartInstancesInput{}
   946  	params.SetInstanceIds([]*string{&instanceId})
   947  	ec2Client, err := self.getEc2Client()
   948  	if err != nil {
   949  		return errors.Wrap(err, "getEc2Client")
   950  	}
   951  	_, err = ec2Client.StartInstances(params)
   952  	return errors.Wrap(err, "StartInstances")
   953  }
   954  
   955  func (self *SRegion) StopVM(instanceId string, isForce bool) error {
   956  	params := &ec2.StopInstancesInput{}
   957  	params.SetInstanceIds([]*string{&instanceId})
   958  	ec2Client, err := self.getEc2Client()
   959  	if err != nil {
   960  		return errors.Wrap(err, "getEc2Client")
   961  	}
   962  	_, err = ec2Client.StopInstances(params)
   963  	return errors.Wrap(err, "StopInstances")
   964  }
   965  
   966  func (self *SRegion) DeleteVM(instanceId string) error {
   967  	// 检查删除保护状态.如果已开启则先关闭删除保护再进行删除操作
   968  	protect, err := self.deleteProtectStatusVM(instanceId)
   969  	if err != nil {
   970  		return err
   971  	}
   972  
   973  	if protect {
   974  		log.Warningf("DeleteVM instance %s which termination protect is in open status", instanceId)
   975  		err = self.deleteProtectVM(instanceId, false)
   976  		if err != nil {
   977  			return err
   978  		}
   979  	}
   980  
   981  	params := &ec2.TerminateInstancesInput{}
   982  	params.SetInstanceIds([]*string{&instanceId})
   983  	ec2Client, err := self.getEc2Client()
   984  	if err != nil {
   985  		return errors.Wrap(err, "getEc2Client")
   986  	}
   987  	_, err = ec2Client.TerminateInstances(params)
   988  	return errors.Wrap(err, "TerminateInstances")
   989  }
   990  
   991  func (self *SRegion) DeployVM(instanceId string, name string, password string, keypairName string, deleteKeypair bool, description string) error {
   992  	params := &ec2.CreateTagsInput{}
   993  	params.SetResources([]*string{&instanceId})
   994  	tagspec := TagSpec{ResourceType: "instance"}
   995  
   996  	if len(keypairName) > 0 {
   997  		return fmt.Errorf("aws not support reset publickey")
   998  	}
   999  
  1000  	if len(password) > 0 {
  1001  		return fmt.Errorf("aws not support set password, use publickey instead")
  1002  	}
  1003  
  1004  	if deleteKeypair {
  1005  		return fmt.Errorf("aws not support delete publickey")
  1006  	}
  1007  
  1008  	if len(name) > 0 {
  1009  		tagspec.SetNameTag(name)
  1010  	}
  1011  
  1012  	if len(description) > 0 {
  1013  		tagspec.SetDescTag(description)
  1014  	}
  1015  
  1016  	ec2Client, err := self.getEc2Client()
  1017  	if err != nil {
  1018  		return errors.Wrap(err, "getEc2Client")
  1019  	}
  1020  
  1021  	ec2Tag, _ := tagspec.GetTagSpecifications()
  1022  	if len(ec2Tag.Tags) > 0 {
  1023  		params.SetTags(ec2Tag.Tags)
  1024  		_, err := ec2Client.CreateTags(params)
  1025  		if err != nil {
  1026  			return err
  1027  		}
  1028  	} else {
  1029  		log.Debugf("no changes")
  1030  	}
  1031  
  1032  	return nil
  1033  }
  1034  
  1035  func (self *SRegion) UpdateVM(instanceId string, hostname string) error {
  1036  	// https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/set-hostname.html
  1037  	return cloudprovider.ErrNotSupported
  1038  }
  1039  
  1040  func (self *SRegion) ReplaceSystemDisk(ctx context.Context, instanceId string, image *SImage, sysDiskSizeGB int, keypair string, userdata string) (string, error) {
  1041  	instance, err := self.GetInstance(instanceId)
  1042  	if err != nil {
  1043  		return "", err
  1044  	}
  1045  	disks, _, err := self.GetDisks(instanceId, instance.ZoneId, "", nil, 0, 0)
  1046  	if err != nil {
  1047  		return "", err
  1048  	}
  1049  
  1050  	var rootDisk *SDisk
  1051  	for _, disk := range disks {
  1052  		if disk.Type == api.DISK_TYPE_SYS {
  1053  			rootDisk = &disk
  1054  			break
  1055  		}
  1056  	}
  1057  
  1058  	if rootDisk == nil {
  1059  		return "", fmt.Errorf("can not find root disk of instance %s", instanceId)
  1060  	}
  1061  	log.Debugf("ReplaceSystemDisk replace root disk %s", rootDisk.DiskId)
  1062  
  1063  	subnetId := ""
  1064  	if len(instance.VpcAttributes.NetworkId) > 0 {
  1065  		subnetId = instance.VpcAttributes.NetworkId
  1066  	}
  1067  
  1068  	// create tmp server
  1069  	tempName := fmt.Sprintf("__tmp_%s", instance.GetName())
  1070  	_id, err := self.CreateInstance(tempName,
  1071  		image,
  1072  		instance.InstanceType,
  1073  		subnetId,
  1074  		"",
  1075  		instance.ZoneId,
  1076  		instance.Description,
  1077  		[]SDisk{{Size: sysDiskSizeGB, Category: rootDisk.Category}},
  1078  		"",
  1079  		keypair,
  1080  		userdata,
  1081  		nil,
  1082  	)
  1083  	if err == nil {
  1084  		defer self.DeleteVM(_id)
  1085  	} else {
  1086  		log.Debugf("ReplaceSystemDisk create temp server failed. %s", err)
  1087  		return "", fmt.Errorf("ReplaceSystemDisk create temp server failed.")
  1088  	}
  1089  
  1090  	ec2Client, err := self.getEc2Client()
  1091  	if err != nil {
  1092  		return "", errors.Wrap(err, "getEc2Client")
  1093  	}
  1094  
  1095  	ec2Client.WaitUntilInstanceRunning(&ec2.DescribeInstancesInput{InstanceIds: []*string{&_id}})
  1096  	err = self.StopVM(_id, true)
  1097  	if err != nil {
  1098  		log.Debugf("ReplaceSystemDisk stop temp server failed %s", err)
  1099  		return "", fmt.Errorf("ReplaceSystemDisk stop temp server failed")
  1100  	}
  1101  	ec2Client.WaitUntilInstanceStopped(&ec2.DescribeInstancesInput{InstanceIds: []*string{&_id}})
  1102  
  1103  	// detach disks
  1104  	tempInstance, err := self.GetInstance(_id)
  1105  	if err != nil {
  1106  		log.Debugf("ReplaceSystemDisk get temp server failed %s", err)
  1107  		return "", fmt.Errorf("ReplaceSystemDisk get temp server failed")
  1108  	}
  1109  
  1110  	err = self.DetachDisk(instance.GetId(), rootDisk.DiskId)
  1111  	if err != nil {
  1112  		log.Debugf("ReplaceSystemDisk detach disk %s: %s", rootDisk.DiskId, err)
  1113  		return "", err
  1114  	}
  1115  
  1116  	err = self.DetachDisk(tempInstance.GetId(), tempInstance.Disks[0])
  1117  	if err != nil {
  1118  		log.Debugf("ReplaceSystemDisk detach disk %s: %s", tempInstance.Disks[0], err)
  1119  		return "", err
  1120  	}
  1121  	ec2Client.WaitUntilVolumeAvailable(&ec2.DescribeVolumesInput{VolumeIds: []*string{&rootDisk.DiskId}})
  1122  	ec2Client.WaitUntilVolumeAvailable(&ec2.DescribeVolumesInput{VolumeIds: []*string{&tempInstance.Disks[0]}})
  1123  
  1124  	err = self.AttachDisk(instance.GetId(), tempInstance.Disks[0], rootDisk.Device)
  1125  	if err != nil {
  1126  		log.Debugf("ReplaceSystemDisk attach disk %s: %s", tempInstance.Disks[0], err)
  1127  		return "", err
  1128  	}
  1129  	ec2Client.WaitUntilInstanceStopped(&ec2.DescribeInstancesInput{InstanceIds: []*string{&instanceId}})
  1130  	ec2Client.WaitUntilVolumeInUse(&ec2.DescribeVolumesInput{VolumeIds: []*string{&tempInstance.Disks[0]}})
  1131  
  1132  	userdataText, err := base64.StdEncoding.DecodeString(userdata)
  1133  	if err != nil {
  1134  		return "", errors.Wrap(err, "SRegion.ReplaceSystemDisk.DecodeString")
  1135  	}
  1136  	err = instance.UpdateUserData(string(userdataText))
  1137  	if err != nil {
  1138  		log.Debugf("ReplaceSystemDisk update user data %s", err)
  1139  		return "", fmt.Errorf("ReplaceSystemDisk update user data failed")
  1140  	}
  1141  
  1142  	err = self.DeleteDisk(rootDisk.DiskId)
  1143  	if err != nil {
  1144  		log.Debugf("ReplaceSystemDisk delete old disk %s: %s", rootDisk.DiskId, err)
  1145  	}
  1146  	return tempInstance.Disks[0], nil
  1147  }
  1148  
  1149  func (self *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceType string, disks []*SDisk) error {
  1150  	params := &ec2.ModifyInstanceAttributeInput{}
  1151  	params.SetInstanceId(instanceId)
  1152  
  1153  	t := &ec2.AttributeValue{Value: &instanceType}
  1154  	params.SetInstanceType(t)
  1155  
  1156  	ec2Client, err := self.getEc2Client()
  1157  	if err != nil {
  1158  		return errors.Wrap(err, "getEc2Client")
  1159  	}
  1160  	_, err = ec2Client.ModifyInstanceAttribute(params)
  1161  	if err != nil {
  1162  		return fmt.Errorf("Failed to change vm config, specification not supported. %s", err.Error())
  1163  	} else {
  1164  		return nil
  1165  	}
  1166  }
  1167  
  1168  func (self *SRegion) DetachDisk(instanceId string, diskId string) error {
  1169  	params := &ec2.DetachVolumeInput{}
  1170  	params.SetInstanceId(instanceId)
  1171  	params.SetVolumeId(diskId)
  1172  	log.Debugf("DetachDisk %s", params.String())
  1173  
  1174  	ec2Client, err := self.getEc2Client()
  1175  	if err != nil {
  1176  		return errors.Wrap(err, "getEc2Client")
  1177  	}
  1178  
  1179  	_, err = ec2Client.DetachVolume(params)
  1180  	if err != nil {
  1181  		if strings.Contains(err.Error(), fmt.Sprintf("'%s'is in the 'available' state", diskId)) {
  1182  			return nil
  1183  		}
  1184  		//InvalidVolume.NotFound: The volume 'vol-0a9eeda0a70a8d7fe' does not exist
  1185  		if strings.Contains(err.Error(), "InvalidVolume.NotFound") {
  1186  			return nil
  1187  		}
  1188  		return errors.Wrap(err, "ec2Client.DetachVolume")
  1189  	}
  1190  	return nil
  1191  }
  1192  
  1193  func (self *SRegion) AttachDisk(instanceId string, diskId string, deviceName string) error {
  1194  	params := &ec2.AttachVolumeInput{}
  1195  	params.SetInstanceId(instanceId)
  1196  	params.SetVolumeId(diskId)
  1197  	params.SetDevice(deviceName)
  1198  	log.Debugf("AttachDisk %s", params.String())
  1199  	ec2Client, err := self.getEc2Client()
  1200  	if err != nil {
  1201  		return errors.Wrap(err, "getEc2Client")
  1202  	}
  1203  	_, err = ec2Client.AttachVolume(params)
  1204  	return errors.Wrap(err, "AttachVolume")
  1205  }
  1206  
  1207  func (self *SRegion) deleteProtectStatusVM(instanceId string) (bool, error) {
  1208  	p := &ec2.DescribeInstanceAttributeInput{}
  1209  	p.SetInstanceId(instanceId)
  1210  	p.SetAttribute("disableApiTermination")
  1211  	ec2Client, err := self.getEc2Client()
  1212  	if err != nil {
  1213  		return false, errors.Wrap(err, "getEc2Client")
  1214  	}
  1215  	ret, err := ec2Client.DescribeInstanceAttribute(p)
  1216  	if err != nil {
  1217  		return false, err
  1218  	}
  1219  
  1220  	return *ret.DisableApiTermination.Value, nil
  1221  }
  1222  
  1223  func (self *SRegion) deleteProtectVM(instanceId string, disableDelete bool) error {
  1224  	p2 := &ec2.ModifyInstanceAttributeInput{
  1225  		DisableApiTermination: &ec2.AttributeBooleanValue{Value: &disableDelete},
  1226  		InstanceId:            &instanceId,
  1227  	}
  1228  	ec2Client, err := self.getEc2Client()
  1229  	if err != nil {
  1230  		return errors.Wrap(err, "getEc2Client")
  1231  	}
  1232  	_, err = ec2Client.ModifyInstanceAttribute(p2)
  1233  	return errors.Wrap(err, "ModifyInstanceAttribute")
  1234  }
  1235  
  1236  func (self *SRegion) getPasswordData(instanceId string) (string, error) {
  1237  	params := &ec2.GetPasswordDataInput{}
  1238  	params.SetInstanceId(instanceId)
  1239  
  1240  	ec2Client, err := self.getEc2Client()
  1241  	if err != nil {
  1242  		return "", errors.Wrap(err, "getEc2Client")
  1243  	}
  1244  	ret, err := ec2Client.GetPasswordData(params)
  1245  	if err != nil {
  1246  		return "", err
  1247  	}
  1248  
  1249  	return *ret.PasswordData, nil
  1250  }
  1251  
  1252  func (self *SInstance) Renew(bc billing.SBillingCycle) error {
  1253  	return cloudprovider.ErrNotSupported
  1254  }
  1255  
  1256  func (self *SInstance) GetProjectId() string {
  1257  	return ""
  1258  }
  1259  
  1260  func (self *SInstance) GetError() error {
  1261  	return nil
  1262  }
  1263  
  1264  func (self *SInstance) SetTags(tags map[string]string, replace bool) error {
  1265  	ec2Client, err := self.host.zone.region.getEc2Client()
  1266  	if err != nil {
  1267  		return errors.Wrap(err, "getEc2Client")
  1268  	}
  1269  	oldTagsJson, err := FetchTags(ec2Client, self.InstanceId)
  1270  	if err != nil {
  1271  		return errors.Wrapf(err, "FetchTags(self.host.zone.region.ec2Client, %s)", self.InstanceId)
  1272  	}
  1273  	oldTags := map[string]string{}
  1274  	err = oldTagsJson.Unmarshal(oldTags)
  1275  	if err != nil {
  1276  		return errors.Wrapf(err, "(%s).Unmarshal(oldTags)", oldTagsJson.String())
  1277  	}
  1278  	addTags := map[string]string{}
  1279  	for k, v := range tags {
  1280  		if strings.HasPrefix(k, "aws:") {
  1281  			return errors.Wrap(cloudprovider.ErrNotSupported, "The aws: prefix is reserved for AWS use")
  1282  		}
  1283  		if _, ok := oldTags[k]; !ok {
  1284  			addTags[k] = v
  1285  		} else {
  1286  			if oldTags[k] != v {
  1287  				addTags[k] = v
  1288  			}
  1289  		}
  1290  	}
  1291  	delTags := []string{}
  1292  	if replace {
  1293  		for k := range oldTags {
  1294  			if _, ok := tags[k]; !ok {
  1295  				if !strings.HasPrefix(k, "aws:") && k != "Name" {
  1296  					delTags = append(delTags, k)
  1297  				}
  1298  			}
  1299  		}
  1300  	}
  1301  	Arn := self.GetArn()
  1302  	err = self.host.zone.region.UntagResources([]string{Arn}, delTags)
  1303  	if err != nil {
  1304  		return errors.Wrapf(err, "self.host.zone.region.UntagResources([]string{%s}, %s)", Arn, jsonutils.Marshal(delTags).String())
  1305  	}
  1306  	delete(addTags, "Name")
  1307  	err = self.host.zone.region.TagResources([]string{Arn}, addTags)
  1308  	if err != nil {
  1309  		return errors.Wrapf(err, "self.host.zone.region.TagResources([]string{%s}, %s)", Arn, jsonutils.Marshal(addTags).String())
  1310  	}
  1311  	return nil
  1312  }
  1313  
  1314  func (self *SInstance) GetAccountId() string {
  1315  	identity, err := self.host.zone.region.client.GetCallerIdentity()
  1316  	if err != nil {
  1317  		log.Errorf(err.Error() + "self.region.client.GetCallerIdentity()")
  1318  		return ""
  1319  	}
  1320  	return identity.Account
  1321  }
  1322  
  1323  func (self *SInstance) GetArn() string {
  1324  	partition := ""
  1325  	switch self.host.zone.region.client.GetAccessEnv() {
  1326  	case api.CLOUD_ACCESS_ENV_AWS_GLOBAL:
  1327  		partition = "aws"
  1328  	case api.CLOUD_ACCESS_ENV_AWS_CHINA:
  1329  		partition = "aws-cn"
  1330  	default:
  1331  		partition = "aws"
  1332  	}
  1333  	return fmt.Sprintf("arn:%s:ec2:%s:%s:instance/%s", partition, self.host.zone.region.GetId(), self.GetAccountId(), self.InstanceId)
  1334  }
  1335  
  1336  func (self *SRegion) SaveImage(instanceId string, opts *cloudprovider.SaveImageOptions) (*SImage, error) {
  1337  	params := map[string]string{
  1338  		"Description": opts.Notes,
  1339  		"InstanceId":  instanceId,
  1340  		"Name":        opts.Name,
  1341  	}
  1342  	ret := struct {
  1343  		ImageId string `xml:"imageId"`
  1344  	}{}
  1345  	err := self.ec2Request("CreateImage", params, &ret)
  1346  	if err != nil {
  1347  		return nil, errors.Wrapf(err, "CreateImage")
  1348  	}
  1349  	err = cloudprovider.Wait(time.Second*10, time.Minute*5, func() (bool, error) {
  1350  		_, err := self.GetImage(ret.ImageId)
  1351  		if err != nil {
  1352  			if errors.Cause(err) == cloudprovider.ErrNotFound {
  1353  				return false, nil
  1354  			}
  1355  			return false, errors.Wrapf(err, "GetImage(%s)", ret.ImageId)
  1356  		}
  1357  		return true, nil
  1358  	})
  1359  	if err != nil {
  1360  		return nil, errors.Wrapf(err, "wait for image created")
  1361  	}
  1362  	image, err := self.GetImage(ret.ImageId)
  1363  	if err != nil {
  1364  		return nil, errors.Wrapf(err, "GetImage(%s)", ret.ImageId)
  1365  	}
  1366  	image.storageCache = self.getStoragecache()
  1367  	return image, nil
  1368  }
  1369  
  1370  func (self *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
  1371  	image, err := self.host.zone.region.SaveImage(self.InstanceId, opts)
  1372  	if err != nil {
  1373  		return nil, errors.Wrapf(err, "SaveImage")
  1374  	}
  1375  	return image, nil
  1376  }