sio_util.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. errSecretNotFound = errors.New("secret not found")
  70. errGatewayNotProvided = errors.New("ScaleIO gateway not provided")
  71. errSecretRefNotProvided = errors.New("secret ref not provided")
  72. errSystemNotProvided = errors.New("ScaleIO system not provided")
  73. errStoragePoolNotProvided = errors.New("ScaleIO storage pool not provided")
  74. errProtectionDomainNotProvided = errors.New("ScaleIO protection domain not provided")
  75. )
  76. // mapVolumeSpec maps attributes from either ScaleIOVolumeSource or ScaleIOPersistentVolumeSource to config
  77. func mapVolumeSpec(config map[string]string, spec *volume.Spec) {
  78. if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
  79. config[confKey.gateway] = source.Gateway
  80. config[confKey.system] = source.System
  81. config[confKey.volumeName] = source.VolumeName
  82. config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
  83. config[confKey.protectionDomain] = source.ProtectionDomain
  84. config[confKey.storagePool] = source.StoragePool
  85. config[confKey.storageMode] = source.StorageMode
  86. config[confKey.fsType] = source.FSType
  87. config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
  88. }
  89. if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
  90. config[confKey.gateway] = source.Gateway
  91. config[confKey.system] = source.System
  92. config[confKey.volumeName] = source.VolumeName
  93. config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
  94. config[confKey.protectionDomain] = source.ProtectionDomain
  95. config[confKey.storagePool] = source.StoragePool
  96. config[confKey.storageMode] = source.StorageMode
  97. config[confKey.fsType] = source.FSType
  98. config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
  99. }
  100. //optionals
  101. applyConfigDefaults(config)
  102. }
  103. func validateConfigs(config map[string]string) error {
  104. if config[confKey.gateway] == "" {
  105. return errGatewayNotProvided
  106. }
  107. if config[confKey.secretName] == "" {
  108. return errSecretRefNotProvided
  109. }
  110. if config[confKey.system] == "" {
  111. return errSystemNotProvided
  112. }
  113. if config[confKey.storagePool] == "" {
  114. return errStoragePoolNotProvided
  115. }
  116. if config[confKey.protectionDomain] == "" {
  117. return errProtectionDomainNotProvided
  118. }
  119. return nil
  120. }
  121. // applyConfigDefaults apply known defaults to incoming spec for dynamic PVCs.
  122. func applyConfigDefaults(config map[string]string) {
  123. b, err := strconv.ParseBool(config[confKey.sslEnabled])
  124. if err != nil {
  125. klog.Warning(log("failed to parse param sslEnabled, setting it to false"))
  126. b = false
  127. }
  128. config[confKey.sslEnabled] = strconv.FormatBool(b)
  129. config[confKey.storageMode] = defaultString(config[confKey.storageMode], "ThinProvisioned")
  130. config[confKey.fsType] = defaultString(config[confKey.fsType], "xfs")
  131. b, err = strconv.ParseBool(config[confKey.readOnly])
  132. if err != nil {
  133. klog.Warning(log("failed to parse param readOnly, setting it to false"))
  134. b = false
  135. }
  136. config[confKey.readOnly] = strconv.FormatBool(b)
  137. }
  138. func defaultString(val, defVal string) string {
  139. if val == "" {
  140. return defVal
  141. }
  142. return val
  143. }
  144. // loadConfig loads configuration data from a file on disk
  145. func loadConfig(configName string) (map[string]string, error) {
  146. klog.V(4).Info(log("loading config file %s", configName))
  147. file, err := os.Open(configName)
  148. if err != nil {
  149. klog.Error(log("failed to open config file %s: %v", configName, err))
  150. return nil, err
  151. }
  152. defer file.Close()
  153. data := map[string]string{}
  154. if err := gob.NewDecoder(file).Decode(&data); err != nil {
  155. klog.Error(log("failed to parse config data %s: %v", configName, err))
  156. return nil, err
  157. }
  158. applyConfigDefaults(data)
  159. if err := validateConfigs(data); err != nil {
  160. klog.Error(log("failed to load ConfigMap %s: %v", err))
  161. return nil, err
  162. }
  163. return data, nil
  164. }
  165. // saveConfig saves the configuration data to local disk
  166. func saveConfig(configName string, data map[string]string) error {
  167. klog.V(4).Info(log("saving config file %s", configName))
  168. dir := path.Dir(configName)
  169. if _, err := os.Stat(dir); err != nil {
  170. if !os.IsNotExist(err) {
  171. return err
  172. }
  173. klog.V(4).Info(log("creating config dir for config data: %s", dir))
  174. if err := os.MkdirAll(dir, 0750); err != nil {
  175. klog.Error(log("failed to create config data dir %v", err))
  176. return err
  177. }
  178. }
  179. file, err := os.Create(configName)
  180. if err != nil {
  181. klog.V(4).Info(log("failed to save config data file %s: %v", configName, err))
  182. return err
  183. }
  184. defer file.Close()
  185. if err := gob.NewEncoder(file).Encode(data); err != nil {
  186. klog.Error(log("failed to save config %s: %v", configName, err))
  187. return err
  188. }
  189. klog.V(4).Info(log("config data file saved successfully as %s", configName))
  190. return nil
  191. }
  192. // attachSecret loads secret object and attaches to configData
  193. func attachSecret(plug *sioPlugin, namespace string, configData map[string]string) error {
  194. // load secret
  195. secretRefName := configData[confKey.secretName]
  196. kubeClient := plug.host.GetKubeClient()
  197. secretMap, err := volutil.GetSecretForPV(namespace, secretRefName, sioPluginName, kubeClient)
  198. if err != nil {
  199. klog.Error(log("failed to get secret: %v", err))
  200. return errSecretNotFound
  201. }
  202. // merge secret data
  203. for key, val := range secretMap {
  204. configData[key] = val
  205. }
  206. return nil
  207. }
  208. // attachSdcGUID injects the sdc guid node label value into config
  209. func attachSdcGUID(plug *sioPlugin, conf map[string]string) error {
  210. guid, err := getSdcGUIDLabel(plug)
  211. if err != nil {
  212. return err
  213. }
  214. conf[confKey.sdcGUID] = guid
  215. return nil
  216. }
  217. // getSdcGUIDLabel fetches the scaleio.sdcGuid node label
  218. // associated with the node executing this code.
  219. func getSdcGUIDLabel(plug *sioPlugin) (string, error) {
  220. nodeLabels, err := plug.host.GetNodeLabels()
  221. if err != nil {
  222. return "", err
  223. }
  224. label, ok := nodeLabels[sdcGUIDLabelName]
  225. if !ok {
  226. klog.V(4).Info(log("node label %s not found", sdcGUIDLabelName))
  227. return "", nil
  228. }
  229. klog.V(4).Info(log("found node label %s=%s", sdcGUIDLabelName, label))
  230. return label, nil
  231. }
  232. // getVolumeSourceFromSpec safely extracts ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec
  233. func getVolumeSourceFromSpec(spec *volume.Spec) (interface{}, error) {
  234. if spec.Volume != nil && spec.Volume.ScaleIO != nil {
  235. return spec.Volume.ScaleIO, nil
  236. }
  237. if spec.PersistentVolume != nil &&
  238. spec.PersistentVolume.Spec.ScaleIO != nil {
  239. return spec.PersistentVolume.Spec.ScaleIO, nil
  240. }
  241. return nil, fmt.Errorf("ScaleIO not defined in spec")
  242. }
  243. func getVolumeSourceAttribs(spec *volume.Spec) (*volSourceAttribs, error) {
  244. attribs := new(volSourceAttribs)
  245. if pvSource, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
  246. attribs.volName = pvSource.VolumeName
  247. attribs.fsType = pvSource.FSType
  248. attribs.readOnly = pvSource.ReadOnly
  249. } else if pSource, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
  250. attribs.volName = pSource.VolumeName
  251. attribs.fsType = pSource.FSType
  252. attribs.readOnly = pSource.ReadOnly
  253. } else {
  254. msg := log("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec")
  255. klog.Error(msg)
  256. return nil, errors.New(msg)
  257. }
  258. return attribs, nil
  259. }
  260. func getScaleIOPersistentVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOPersistentVolumeSource, error) {
  261. source, err := getVolumeSourceFromSpec(spec)
  262. if err != nil {
  263. return nil, err
  264. }
  265. if val, ok := source.(*api.ScaleIOPersistentVolumeSource); ok {
  266. return val, nil
  267. }
  268. return nil, fmt.Errorf("spec is not a valid ScaleIOPersistentVolume type")
  269. }
  270. func getScaleIOVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOVolumeSource, error) {
  271. source, err := getVolumeSourceFromSpec(spec)
  272. if err != nil {
  273. return nil, err
  274. }
  275. if val, ok := source.(*api.ScaleIOVolumeSource); ok {
  276. return val, nil
  277. }
  278. return nil, fmt.Errorf("spec is not a valid ScaleIOVolume type")
  279. }
  280. func getSecretAndNamespaceFromSpec(spec *volume.Spec, pod *api.Pod) (secretName string, secretNS string, err error) {
  281. if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
  282. secretName = source.SecretRef.Name
  283. if pod != nil {
  284. secretNS = pod.Namespace
  285. }
  286. } else if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
  287. if source.SecretRef != nil {
  288. secretName = source.SecretRef.Name
  289. secretNS = source.SecretRef.Namespace
  290. if secretNS == "" && pod != nil {
  291. secretNS = pod.Namespace
  292. }
  293. }
  294. } else {
  295. return "", "", errors.New("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource")
  296. }
  297. return secretName, secretNS, nil
  298. }
  299. func log(msg string, parts ...interface{}) string {
  300. return fmt.Sprintf(fmt.Sprintf("scaleio: %s", msg), parts...)
  301. }