yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/keypair.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 qcloud
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"time"
    21  
    22  	"github.com/aokoli/goutils"
    23  	"golang.org/x/crypto/ssh"
    24  
    25  	"yunion.io/x/log"
    26  
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  )
    29  
    30  type SKeypair struct {
    31  	AssociatedInstanceIds []string
    32  	CreateTime            time.Time
    33  	Description           string
    34  	KeyId                 string
    35  	KeyName               string
    36  	PublicKey             string
    37  }
    38  
    39  func (self *SRegion) GetKeypairs(name string, keyIds []string, offset int, limit int) ([]SKeypair, int, error) {
    40  	if limit > 50 || limit <= 0 {
    41  		limit = 50
    42  	}
    43  	params := map[string]string{}
    44  	params["Limit"] = fmt.Sprintf("%d", limit)
    45  	params["Offset"] = fmt.Sprintf("%d", offset)
    46  
    47  	if len(keyIds) > 0 {
    48  		for i := 0; i < len(keyIds); i++ {
    49  			params[fmt.Sprintf("KeyIds.%d", i)] = keyIds[i]
    50  		}
    51  	} else {
    52  		if len(name) > 0 {
    53  			params["Filters.0.Name"] = "key-name"
    54  			params["Filters.0.Values.0"] = name
    55  		}
    56  	}
    57  
    58  	body, err := self.cvmRequest("DescribeKeyPairs", params, true)
    59  	if err != nil {
    60  		log.Errorf("GetKeypairs fail %s", err)
    61  		return nil, 0, err
    62  	}
    63  
    64  	keypairs := []SKeypair{}
    65  	err = body.Unmarshal(&keypairs, "KeyPairSet")
    66  	if err != nil {
    67  		log.Errorf("Unmarshal keypair fail %s", err)
    68  		return nil, 0, err
    69  	}
    70  	total, _ := body.Float("TotalCount")
    71  	return keypairs, int(total), nil
    72  }
    73  
    74  func (self *SRegion) ImportKeypair(name string, pubKey string) (*SKeypair, error) {
    75  	params := map[string]string{}
    76  	params["PublicKey"] = pubKey
    77  	params["ProjectId"] = "0"
    78  	params["KeyName"] = name
    79  
    80  	body, err := self.cvmRequest("ImportKeyPair", params, true)
    81  	if err != nil {
    82  		log.Errorf("ImportKeypair fail %s", err)
    83  		return nil, err
    84  	}
    85  
    86  	keypairID, err := body.GetString("KeyId")
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	keypairs, total, err := self.GetKeypairs("", []string{keypairID}, 0, 1)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	if total != 1 {
    95  		return nil, cloudprovider.ErrNotFound
    96  	}
    97  	return &keypairs[0], nil
    98  }
    99  
   100  func (self *SRegion) AttachKeypair(instanceId string, keypairId string) error {
   101  	params := map[string]string{}
   102  	params["InstanceIds.0"] = instanceId
   103  	params["KeyIds.0"] = keypairId
   104  	_, err := self.cvmRequest("AssociateInstancesKeyPairs", params, true)
   105  	return err
   106  }
   107  
   108  func (self *SRegion) DetachKeyPair(instanceId string, keypairId string) error {
   109  	params := make(map[string]string)
   110  	params["InstanceIds.0"] = instanceId
   111  	params["KeyIds.0"] = keypairId
   112  	_, err := self.cvmRequest("DisassociateInstancesKeyPairs", params, true)
   113  	return err
   114  }
   115  
   116  func (self *SRegion) CreateKeyPair(name string) (*SKeypair, error) {
   117  	params := make(map[string]string)
   118  	params["KeyName"] = name
   119  	params["ProjectId"] = "0"
   120  	body, err := self.cvmRequest("CreateKeyPair", params, true)
   121  	keypair := SKeypair{}
   122  	err = body.Unmarshal(&keypair, "KeyPair")
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	return &keypair, err
   127  }
   128  
   129  func (self *SRegion) getKeypairs() ([]SKeypair, error) {
   130  	keypairs := []SKeypair{}
   131  	for {
   132  		parts, total, err := self.GetKeypairs("", []string{}, 0, 50)
   133  		if err != nil {
   134  			log.Errorf("Get keypairs fail %v", err)
   135  			return nil, err
   136  		}
   137  		keypairs = append(keypairs, parts...)
   138  		if len(keypairs) >= total {
   139  			break
   140  		}
   141  	}
   142  	return keypairs, nil
   143  }
   144  
   145  func (self *SRegion) getFingerprint(publicKey string) (string, error) {
   146  	pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey))
   147  	if err != nil {
   148  		return "", fmt.Errorf("publicKey error %s", err)
   149  	}
   150  	return ssh.FingerprintLegacyMD5(pk), nil
   151  }
   152  
   153  func (self *SRegion) lookUpQcloudKeypair(publicKey string) (string, error) {
   154  	keypairs, err := self.getKeypairs()
   155  	if err != nil {
   156  		return "", err
   157  	}
   158  
   159  	localFiger, err := self.getFingerprint(publicKey)
   160  	if err != nil {
   161  		return "", err
   162  	}
   163  
   164  	for i := 0; i < len(keypairs); i++ {
   165  		finger, err := self.getFingerprint(keypairs[i].PublicKey)
   166  		if err != nil {
   167  			continue
   168  		}
   169  		if finger == localFiger {
   170  			return keypairs[i].KeyId, nil
   171  		}
   172  	}
   173  	return "", cloudprovider.ErrNotFound
   174  }
   175  
   176  func (self *SRegion) syncKeypair(publicKey string) (string, error) {
   177  	keypairId, err := self.lookUpQcloudKeypair(publicKey)
   178  	if err == nil {
   179  		return keypairId, nil
   180  	}
   181  
   182  	prefix, e := goutils.RandomAlphabetic(6)
   183  	if e != nil {
   184  		return "", fmt.Errorf("publicKey error %s", e)
   185  	}
   186  
   187  	name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
   188  	keypair, err := self.ImportKeypair(name, publicKey)
   189  	if err != nil {
   190  		return "", err
   191  	}
   192  	return keypair.KeyId, nil
   193  }