github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/libvirttools/ceph_flexvolume.go (about) 1 /* 2 Copyright 2017 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package libvirttools 18 19 import ( 20 "encoding/base64" 21 "fmt" 22 "strings" 23 24 "github.com/golang/glog" 25 26 libvirtxml "github.com/libvirt/libvirt-go-xml" 27 28 "github.com/Mirantis/virtlet/pkg/metadata/types" 29 "github.com/Mirantis/virtlet/pkg/utils" 30 "github.com/Mirantis/virtlet/pkg/virt" 31 ) 32 33 type cephFlexvolumeOptions struct { 34 // Type field is needed here so it gets written during 35 // remarshalling after removing the contents of 'Secret' field 36 Type string `json:"type"` 37 Monitor string `json:"monitor"` 38 Pool string `json:"pool"` 39 Volume string `json:"volume"` 40 Secret string `json:"secret"` 41 User string `json:"user"` 42 Protocol string `json:"protocol"` 43 UUID string `json:"uuid"` 44 } 45 46 // cephVolume denotes a Ceph RBD volume 47 type cephVolume struct { 48 volumeBase 49 volumeName string 50 opts *cephFlexvolumeOptions 51 } 52 53 var _ VMVolume = &cephVolume{} 54 55 func newCephVolume(volumeName, configPath string, config *types.VMConfig, owner volumeOwner) (VMVolume, error) { 56 v := &cephVolume{ 57 volumeBase: volumeBase{config, owner}, 58 } 59 if err := utils.ReadJSON(configPath, &v.opts); err != nil { 60 return nil, fmt.Errorf("failed to parse ceph flexvolume config %q: %v", configPath, err) 61 } 62 v.volumeName = volumeName 63 // Remove the key from flexvolume options to limit exposure. 64 // The file itself will be needed to recreate cephVolume during the teardown, 65 // but we don't need secret content at that time anymore 66 safeOpts := *v.opts 67 safeOpts.Secret = "" 68 if err := utils.WriteJSON(configPath, safeOpts, 0700); err != nil { 69 return nil, fmt.Errorf("failed to overwrite ceph flexvolume config %q: %v", configPath, err) 70 } 71 return v, nil 72 } 73 74 func (v *cephVolume) secretUsageName() string { 75 return v.opts.User + "-" + utils.NewUUID5(ContainerNsUUID, v.config.PodSandboxID) + "-" + v.volumeName 76 } 77 78 func (v *cephVolume) secretDef() *libvirtxml.Secret { 79 return &libvirtxml.Secret{ 80 Ephemeral: "no", 81 Private: "no", 82 // Both secret UUID and Usage name must be unique across all definitions 83 // As Usage name is a string and can be used to lookup secret 84 // it's more convenient to use it for manipulating secrets 85 // and preserve using UUIDv5 as part of value 86 // UUID value is generated randomly 87 UUID: utils.NewUUID(), 88 Usage: &libvirtxml.SecretUsage{Name: v.secretUsageName(), Type: "ceph"}, 89 } 90 } 91 92 func (v *cephVolume) IsDisk() bool { return true } 93 94 func (v *cephVolume) UUID() string { 95 return v.opts.UUID 96 } 97 98 func (v *cephVolume) Setup() (*libvirtxml.DomainDisk, *libvirtxml.DomainFilesystem, error) { 99 ipPortPair := strings.Split(v.opts.Monitor, ":") 100 if len(ipPortPair) != 2 { 101 return nil, nil, fmt.Errorf("invalid format of ceph monitor setting: %s. Expected ip:port", v.opts.Monitor) 102 } 103 104 secret, err := v.owner.DomainConnection().DefineSecret(v.secretDef()) 105 if err != nil { 106 return nil, nil, fmt.Errorf("error defining ceph secret: %v", err) 107 } 108 109 key, err := base64.StdEncoding.DecodeString(v.opts.Secret) 110 if err != nil { 111 return nil, nil, fmt.Errorf("error decoding ceph secret: %v", err) 112 } 113 114 if err := secret.SetValue([]byte(key)); err != nil { 115 return nil, nil, fmt.Errorf("error setting value of secret %q: %v", v.secretUsageName(), err) 116 } 117 118 return &libvirtxml.DomainDisk{ 119 Device: "disk", 120 Driver: &libvirtxml.DomainDiskDriver{Name: "qemu", Type: "raw"}, 121 Auth: &libvirtxml.DomainDiskAuth{ 122 Username: v.opts.User, 123 Secret: &libvirtxml.DomainDiskSecret{ 124 Type: "ceph", 125 Usage: v.secretUsageName(), 126 }, 127 }, 128 Source: &libvirtxml.DomainDiskSource{ 129 Network: &libvirtxml.DomainDiskSourceNetwork{ 130 Protocol: "rbd", 131 Name: v.opts.Pool + "/" + v.opts.Volume, 132 Hosts: []libvirtxml.DomainDiskSourceHost{ 133 { 134 Name: ipPortPair[0], 135 Port: ipPortPair[1], 136 }, 137 }, 138 }, 139 }, 140 }, nil, nil 141 } 142 143 func (v *cephVolume) Teardown() error { 144 secret, err := v.owner.DomainConnection().LookupSecretByUsageName("ceph", v.secretUsageName()) 145 switch { 146 case err == virt.ErrSecretNotFound: 147 // ok, no need to delete the secret 148 glog.V(3).Infof("No secret with usage name %q for ceph volume was found", v.secretUsageName()) 149 return nil 150 case err == nil: 151 glog.V(3).Infof("Removing secret with usage name: %q", v.secretUsageName()) 152 err = secret.Remove() 153 } 154 if err != nil { 155 return fmt.Errorf("error deleting secret with usage name %q: %v", v.secretUsageName(), err) 156 } 157 return nil 158 } 159 160 func init() { 161 addFlexvolumeSource("ceph", newCephVolume) 162 } 163 164 // TODO: this file needs a test