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 }