1/* 2Copyright 2019 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package plugins 18 19import ( 20 "fmt" 21 "strings" 22 23 v1 "k8s.io/api/core/v1" 24 storage "k8s.io/api/storage/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26) 27 28const ( 29 // CinderDriverName is the name of the CSI driver for Cinder 30 CinderDriverName = "cinder.csi.openstack.org" 31 // CinderTopologyKey is the zonal topology key for Cinder CSI Driver 32 CinderTopologyKey = "topology.cinder.csi.openstack.org/zone" 33 // CinderInTreePluginName is the name of the intree plugin for Cinder 34 CinderInTreePluginName = "kubernetes.io/cinder" 35) 36 37var _ InTreePlugin = (*osCinderCSITranslator)(nil) 38 39// osCinderCSITranslator handles translation of PV spec from In-tree Cinder to CSI Cinder and vice versa 40type osCinderCSITranslator struct{} 41 42// NewOpenStackCinderCSITranslator returns a new instance of osCinderCSITranslator 43func NewOpenStackCinderCSITranslator() InTreePlugin { 44 return &osCinderCSITranslator{} 45} 46 47// TranslateInTreeStorageClassParametersToCSI translates InTree Cinder storage class parameters to CSI storage class 48func (t *osCinderCSITranslator) TranslateInTreeStorageClassToCSI(sc *storage.StorageClass) (*storage.StorageClass, error) { 49 var ( 50 params = map[string]string{} 51 ) 52 for k, v := range sc.Parameters { 53 switch strings.ToLower(k) { 54 case fsTypeKey: 55 params[csiFsTypeKey] = v 56 default: 57 // All other parameters are supported by the CSI driver. 58 // This includes also "availability", therefore do not translate it to sc.AllowedTopologies 59 params[k] = v 60 } 61 } 62 63 if len(sc.AllowedTopologies) > 0 { 64 newTopologies, err := translateAllowedTopologies(sc.AllowedTopologies, CinderTopologyKey) 65 if err != nil { 66 return nil, fmt.Errorf("failed translating allowed topologies: %v", err) 67 } 68 sc.AllowedTopologies = newTopologies 69 } 70 71 sc.Parameters = params 72 73 return sc, nil 74} 75 76// TranslateInTreeInlineVolumeToCSI takes a Volume with Cinder set from in-tree 77// and converts the Cinder source to a CSIPersistentVolumeSource 78func (t *osCinderCSITranslator) TranslateInTreeInlineVolumeToCSI(volume *v1.Volume, podNamespace string) (*v1.PersistentVolume, error) { 79 if volume == nil || volume.Cinder == nil { 80 return nil, fmt.Errorf("volume is nil or Cinder not defined on volume") 81 } 82 83 cinderSource := volume.Cinder 84 pv := &v1.PersistentVolume{ 85 ObjectMeta: metav1.ObjectMeta{ 86 // Must be unique per disk as it is used as the unique part of the 87 // staging path 88 Name: fmt.Sprintf("%s-%s", CinderDriverName, cinderSource.VolumeID), 89 }, 90 Spec: v1.PersistentVolumeSpec{ 91 PersistentVolumeSource: v1.PersistentVolumeSource{ 92 CSI: &v1.CSIPersistentVolumeSource{ 93 Driver: CinderDriverName, 94 VolumeHandle: cinderSource.VolumeID, 95 ReadOnly: cinderSource.ReadOnly, 96 FSType: cinderSource.FSType, 97 VolumeAttributes: map[string]string{}, 98 }, 99 }, 100 AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, 101 }, 102 } 103 return pv, nil 104} 105 106// TranslateInTreePVToCSI takes a PV with Cinder set from in-tree 107// and converts the Cinder source to a CSIPersistentVolumeSource 108func (t *osCinderCSITranslator) TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) { 109 if pv == nil || pv.Spec.Cinder == nil { 110 return nil, fmt.Errorf("pv is nil or Cinder not defined on pv") 111 } 112 113 cinderSource := pv.Spec.Cinder 114 115 csiSource := &v1.CSIPersistentVolumeSource{ 116 Driver: CinderDriverName, 117 VolumeHandle: cinderSource.VolumeID, 118 ReadOnly: cinderSource.ReadOnly, 119 FSType: cinderSource.FSType, 120 VolumeAttributes: map[string]string{}, 121 } 122 123 if err := translateTopologyFromInTreeToCSI(pv, CinderTopologyKey); err != nil { 124 return nil, fmt.Errorf("failed to translate topology: %v", err) 125 } 126 127 pv.Spec.Cinder = nil 128 pv.Spec.CSI = csiSource 129 return pv, nil 130} 131 132// TranslateCSIPVToInTree takes a PV with CSIPersistentVolumeSource set and 133// translates the Cinder CSI source to a Cinder In-tree source. 134func (t *osCinderCSITranslator) TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) { 135 if pv == nil || pv.Spec.CSI == nil { 136 return nil, fmt.Errorf("pv is nil or CSI source not defined on pv") 137 } 138 139 csiSource := pv.Spec.CSI 140 141 cinderSource := &v1.CinderPersistentVolumeSource{ 142 VolumeID: csiSource.VolumeHandle, 143 FSType: csiSource.FSType, 144 ReadOnly: csiSource.ReadOnly, 145 } 146 147 // translate CSI topology to In-tree topology for rollback compatibility. 148 // It is not possible to guess Cinder Region from the Zone, therefore leave it empty. 149 if err := translateTopologyFromCSIToInTree(pv, CinderTopologyKey, nil); err != nil { 150 return nil, fmt.Errorf("failed to translate topology. PV:%+v. Error:%v", *pv, err) 151 } 152 153 pv.Spec.CSI = nil 154 pv.Spec.Cinder = cinderSource 155 return pv, nil 156} 157 158// CanSupport tests whether the plugin supports a given persistent volume 159// specification from the API. The spec pointer should be considered 160// const. 161func (t *osCinderCSITranslator) CanSupport(pv *v1.PersistentVolume) bool { 162 return pv != nil && pv.Spec.Cinder != nil 163} 164 165// CanSupportInline tests whether the plugin supports a given inline volume 166// specification from the API. The spec pointer should be considered 167// const. 168func (t *osCinderCSITranslator) CanSupportInline(volume *v1.Volume) bool { 169 return volume != nil && volume.Cinder != nil 170} 171 172// GetInTreePluginName returns the name of the intree plugin driver 173func (t *osCinderCSITranslator) GetInTreePluginName() string { 174 return CinderInTreePluginName 175} 176 177// GetCSIPluginName returns the name of the CSI plugin 178func (t *osCinderCSITranslator) GetCSIPluginName() string { 179 return CinderDriverName 180} 181 182func (t *osCinderCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) { 183 return volumeHandle, nil 184} 185