sio_util.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package scaleio
  14. import (
  15. "encoding/gob"
  16. "errors"
  17. "fmt"
  18. "os"
  19. "path"
  20. "strconv"
  21. "k8s.io/klog"
  22. api "k8s.io/api/core/v1"
  23. "k8s.io/kubernetes/pkg/volume"
  24. volutil "k8s.io/kubernetes/pkg/volume/util"
  25. )
  26. type volSourceAttribs struct {
  27. volName,
  28. fsType string
  29. readOnly bool
  30. }
  31. var (
  32. confKey = struct {
  33. gateway,
  34. sslEnabled,
  35. secretName,
  36. system,
  37. protectionDomain,
  38. storagePool,
  39. storageMode,
  40. sdcRootPath,
  41. volumeName,
  42. volSpecName,
  43. fsType,
  44. readOnly,
  45. username,
  46. password,
  47. secretNamespace,
  48. sdcGUID string
  49. }{
  50. gateway: "gateway",
  51. sslEnabled: "sslEnabled",
  52. secretName: "secretRef",
  53. secretNamespace: "secretNamespace",
  54. system: "system",
  55. protectionDomain: "protectionDomain",
  56. storagePool: "storagePool",
  57. storageMode: "storageMode",
  58. sdcRootPath: "sdcRootPath",
  59. volumeName: "volumeName",
  60. volSpecName: "volSpecName",
  61. fsType: "fsType",
  62. readOnly: "readOnly",
  63. username: "username",
  64. password: "password",
  65. sdcGUID: "sdcGUID",
  66. }
  67. sdcGUIDLabelName = "scaleio.sdcGUID"
  68. sdcRootPath = "/opt/emc/scaleio/sdc/bin"
  69. secretNotFoundErr = errors.New("secret not found")
  70. configMapNotFoundErr = errors.New("configMap not found")
  71. gatewayNotProvidedErr = errors.New("ScaleIO gateway not provided")
  72. secretRefNotProvidedErr = errors.New("secret ref not provided")
  73. systemNotProvidedErr = errors.New("ScaleIO system not provided")
  74. storagePoolNotProvidedErr = errors.New("ScaleIO storage pool not provided")
  75. protectionDomainNotProvidedErr = errors.New("ScaleIO protection domain not provided")
  76. )
  77. // mapVolumeSpec maps attributes from either ScaleIOVolumeSource or ScaleIOPersistentVolumeSource to config
  78. func mapVolumeSpec(config map[string]string, spec *volume.Spec) {
  79. if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
  80. config[confKey.gateway] = source.Gateway
  81. config[confKey.system] = source.System
  82. config[confKey.volumeName] = source.VolumeName
  83. config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
  84. config[confKey.protectionDomain] = source.ProtectionDomain
  85. config[confKey.storagePool] = source.StoragePool
  86. config[confKey.storageMode] = source.StorageMode
  87. config[confKey.fsType] = source.FSType
  88. config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
  89. }
  90. if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
  91. config[confKey.gateway] = source.Gateway
  92. config[confKey.system] = source.System
  93. config[confKey.volumeName] = source.VolumeName
  94. config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
  95. config[confKey.protectionDomain] = source.ProtectionDomain
  96. config[confKey.storagePool] = source.StoragePool
  97. config[confKey.storageMode] = source.StorageMode
  98. config[confKey.fsType] = source.FSType
  99. config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
  100. }
  101. //optionals
  102. applyConfigDefaults(config)
  103. }
  104. func validateConfigs(config map[string]string) error {
  105. if config[confKey.gateway] == "" {
  106. return gatewayNotProvidedErr
  107. }
  108. if config[confKey.secretName] == "" {
  109. return secretRefNotProvidedErr
  110. }
  111. if config[confKey.system] == "" {
  112. return systemNotProvidedErr
  113. }
  114. if config[confKey.storagePool] == "" {
  115. return storagePoolNotProvidedErr
  116. }
  117. if config[confKey.protectionDomain] == "" {
  118. return protectionDomainNotProvidedErr
  119. }
  120. return nil
  121. }
  122. // applyConfigDefaults apply known defaults to incoming spec for dynamic PVCs.
  123. func applyConfigDefaults(config map[string]string) {
  124. b, err := strconv.ParseBool(config[confKey.sslEnabled])
  125. if err != nil {
  126. klog.Warning(log("failed to parse param sslEnabled, setting it to false"))
  127. b = false
  128. }
  129. config[confKey.sslEnabled] = strconv.FormatBool(b)
  130. config[confKey.storageMode] = defaultString(config[confKey.storageMode], "ThinProvisioned")
  131. config[confKey.fsType] = defaultString(config[confKey.fsType], "xfs")
  132. b, err = strconv.ParseBool(config[confKey.readOnly])
  133. if err != nil {
  134. klog.Warning(log("failed to parse param readOnly, setting it to false"))
  135. b = false
  136. }
  137. config[confKey.readOnly] = strconv.FormatBool(b)
  138. }
  139. func defaultString(val, defVal string) string {
  140. if val == "" {
  141. return defVal
  142. }
  143. return val
  144. }
  145. // loadConfig loads configuration data from a file on disk
  146. func loadConfig(configName string) (map[string]string, error) {
  147. klog.V(4).Info(log("loading config file %s", configName))
  148. file, err := os.Open(configName)
  149. if err != nil {
  150. klog.Error(log("failed to open config file %s: %v", configName, err))
  151. return nil, err
  152. }
  153. defer file.Close()
  154. data := map[string]string{}
  155. if err := gob.NewDecoder(file).Decode(&data); err != nil {
  156. klog.Error(log("failed to parse config data %s: %v", configName, err))
  157. return nil, err
  158. }
  159. applyConfigDefaults(data)
  160. if err := validateConfigs(data); err != nil {
  161. klog.Error(log("failed to load ConfigMap %s: %v", err))
  162. return nil, err
  163. }
  164. return data, nil
  165. }
  166. // saveConfig saves the configuration data to local disk
  167. func saveConfig(configName string, data map[string]string) error {
  168. klog.V(4).Info(log("saving config file %s", configName))
  169. dir := path.Dir(configName)
  170. if _, err := os.Stat(dir); err != nil {
  171. if !os.IsNotExist(err) {
  172. return err
  173. }
  174. klog.V(4).Info(log("creating config dir for config data: %s", dir))
  175. if err := os.MkdirAll(dir, 0750); err != nil {
  176. klog.Error(log("failed to create config data dir %v", err))
  177. return err
  178. }
  179. }
  180. file, err := os.Create(configName)
  181. if err != nil {
  182. klog.V(4).Info(log("failed to save config data file %s: %v", configName, err))
  183. return err
  184. }
  185. defer file.Close()
  186. if err := gob.NewEncoder(file).Encode(data); err != nil {
  187. klog.Error(log("failed to save config %s: %v", configName, err))
  188. return err
  189. }
  190. klog.V(4).Info(log("config data file saved successfully as %s", configName))
  191. return nil
  192. }
  193. // attachSecret loads secret object and attaches to configData
  194. func attachSecret(plug *sioPlugin, namespace string, configData map[string]string) error {
  195. // load secret
  196. secretRefName := configData[confKey.secretName]
  197. kubeClient := plug.host.GetKubeClient()
  198. secretMap, err := volutil.GetSecretForPV(namespace, secretRefName, sioPluginName, kubeClient)
  199. if err != nil {
  200. klog.Error(log("failed to get secret: %v", err))
  201. return secretNotFoundErr
  202. }
  203. // merge secret data
  204. for key, val := range secretMap {
  205. configData[key] = val
  206. }
  207. return nil
  208. }
  209. // attachSdcGUID injects the sdc guid node label value into config
  210. func attachSdcGUID(plug *sioPlugin, conf map[string]string) error {
  211. guid, err := getSdcGUIDLabel(plug)
  212. if err != nil {
  213. return err
  214. }
  215. conf[confKey.sdcGUID] = guid
  216. return nil
  217. }
  218. // getSdcGUIDLabel fetches the scaleio.sdcGuid node label
  219. // associated with the node executing this code.
  220. func getSdcGUIDLabel(plug *sioPlugin) (string, error) {
  221. nodeLabels, err := plug.host.GetNodeLabels()
  222. if err != nil {
  223. return "", err
  224. }
  225. label, ok := nodeLabels[sdcGUIDLabelName]
  226. if !ok {
  227. klog.V(4).Info(log("node label %s not found", sdcGUIDLabelName))
  228. return "", nil
  229. }
  230. klog.V(4).Info(log("found node label %s=%s", sdcGUIDLabelName, label))
  231. return label, nil
  232. }
  233. // getVolumeSourceFromSpec safely extracts ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec
  234. func getVolumeSourceFromSpec(spec *volume.Spec) (interface{}, error) {
  235. if spec.Volume != nil && spec.Volume.ScaleIO != nil {
  236. return spec.Volume.ScaleIO, nil
  237. }
  238. if spec.PersistentVolume != nil &&
  239. spec.PersistentVolume.Spec.ScaleIO != nil {
  240. return spec.PersistentVolume.Spec.ScaleIO, nil
  241. }
  242. return nil, fmt.Errorf("ScaleIO not defined in spec")
  243. }
  244. func getVolumeSourceAttribs(spec *volume.Spec) (*volSourceAttribs, error) {
  245. attribs := new(volSourceAttribs)
  246. if pvSource, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
  247. attribs.volName = pvSource.VolumeName
  248. attribs.fsType = pvSource.FSType
  249. attribs.readOnly = pvSource.ReadOnly
  250. } else if pSource, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
  251. attribs.volName = pSource.VolumeName
  252. attribs.fsType = pSource.FSType
  253. attribs.readOnly = pSource.ReadOnly
  254. } else {
  255. msg := log("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec")
  256. klog.Error(msg)
  257. return nil, errors.New(msg)
  258. }
  259. return attribs, nil
  260. }
  261. func getScaleIOPersistentVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOPersistentVolumeSource, error) {
  262. source, err := getVolumeSourceFromSpec(spec)
  263. if err != nil {
  264. return nil, err
  265. }
  266. if val, ok := source.(*api.ScaleIOPersistentVolumeSource); ok {
  267. return val, nil
  268. }
  269. return nil, fmt.Errorf("spec is not a valid ScaleIOPersistentVolume type")
  270. }
  271. func getScaleIOVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOVolumeSource, error) {
  272. source, err := getVolumeSourceFromSpec(spec)
  273. if err != nil {
  274. return nil, err
  275. }
  276. if val, ok := source.(*api.ScaleIOVolumeSource); ok {
  277. return val, nil
  278. }
  279. return nil, fmt.Errorf("spec is not a valid ScaleIOVolume type")
  280. }
  281. func getSecretAndNamespaceFromSpec(spec *volume.Spec, pod *api.Pod) (secretName string, secretNS string, err error) {
  282. if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
  283. secretName = source.SecretRef.Name
  284. if pod != nil {
  285. secretNS = pod.Namespace
  286. }
  287. } else if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
  288. if source.SecretRef != nil {
  289. secretName = source.SecretRef.Name
  290. secretNS = source.SecretRef.Namespace
  291. if secretNS == "" && pod != nil {
  292. secretNS = pod.Namespace
  293. }
  294. }
  295. } else {
  296. return "", "", errors.New("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource")
  297. }
  298. return secretName, secretNS, nil
  299. }
  300. func log(msg string, parts ...interface{}) string {
  301. return fmt.Sprintf(fmt.Sprintf("scaleio: %s", msg), parts...)
  302. }