kernel_validator.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. Copyright 2016 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 system
  14. import (
  15. "bufio"
  16. "bytes"
  17. "compress/gzip"
  18. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "os"
  22. "os/exec"
  23. "path/filepath"
  24. "regexp"
  25. "strings"
  26. "github.com/pkg/errors"
  27. errorsutil "k8s.io/apimachinery/pkg/util/errors"
  28. "k8s.io/klog"
  29. )
  30. var _ Validator = &KernelValidator{}
  31. // KernelValidator validates kernel. Currently only validate kernel version
  32. // and kernel configuration.
  33. type KernelValidator struct {
  34. kernelRelease string
  35. Reporter Reporter
  36. }
  37. // Name is part of the system.Validator interface.
  38. func (k *KernelValidator) Name() string {
  39. return "kernel"
  40. }
  41. // kConfigOption is the possible kernel config option.
  42. type kConfigOption string
  43. const (
  44. builtIn kConfigOption = "y"
  45. asModule kConfigOption = "m"
  46. leftOut kConfigOption = "n"
  47. // validKConfigRegex is the regex matching kernel configuration line.
  48. validKConfigRegex = "^CONFIG_[A-Z0-9_]+=[myn]"
  49. // kernelConfigPrefix is the prefix of kernel configuration.
  50. kernelConfigPrefix = "CONFIG_"
  51. )
  52. // Validate is part of the system.Validator interface.
  53. func (k *KernelValidator) Validate(spec SysSpec) (error, error) {
  54. helper := KernelValidatorHelperImpl{}
  55. release, err := helper.GetKernelReleaseVersion()
  56. if err != nil {
  57. return nil, errors.Wrap(err, "failed to get kernel release")
  58. }
  59. k.kernelRelease = release
  60. var errs []error
  61. errs = append(errs, k.validateKernelVersion(spec.KernelSpec))
  62. // only validate kernel config when necessary (currently no kernel config for windows)
  63. if len(spec.KernelSpec.Required) > 0 || len(spec.KernelSpec.Forbidden) > 0 || len(spec.KernelSpec.Optional) > 0 {
  64. errs = append(errs, k.validateKernelConfig(spec.KernelSpec))
  65. }
  66. return nil, errorsutil.NewAggregate(errs)
  67. }
  68. // validateKernelVersion validates the kernel version.
  69. func (k *KernelValidator) validateKernelVersion(kSpec KernelSpec) error {
  70. versionRegexps := kSpec.Versions
  71. for _, versionRegexp := range versionRegexps {
  72. r := regexp.MustCompile(versionRegexp)
  73. if r.MatchString(k.kernelRelease) {
  74. k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, good)
  75. return nil
  76. }
  77. }
  78. k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, bad)
  79. return errors.Errorf("unsupported kernel release: %s", k.kernelRelease)
  80. }
  81. // validateKernelConfig validates the kernel configurations.
  82. func (k *KernelValidator) validateKernelConfig(kSpec KernelSpec) error {
  83. allConfig, err := k.getKernelConfig()
  84. if err != nil {
  85. return errors.Wrap(err, "failed to parse kernel config")
  86. }
  87. return k.validateCachedKernelConfig(allConfig, kSpec)
  88. }
  89. // validateCachedKernelConfig validates the kernel confgiurations cached in internal data type.
  90. func (k *KernelValidator) validateCachedKernelConfig(allConfig map[string]kConfigOption, kSpec KernelSpec) error {
  91. badConfigs := []string{}
  92. // reportAndRecord is a helper function to record bad config when
  93. // report.
  94. reportAndRecord := func(name, msg, desc string, result ValidationResultType) {
  95. if result == bad {
  96. badConfigs = append(badConfigs, name)
  97. }
  98. // report description when the config is bad or warn.
  99. if result != good && desc != "" {
  100. msg = msg + " - " + desc
  101. }
  102. k.Reporter.Report(name, msg, result)
  103. }
  104. const (
  105. required = iota
  106. optional
  107. forbidden
  108. )
  109. validateOpt := func(config KernelConfig, expect int) {
  110. var found, missing ValidationResultType
  111. switch expect {
  112. case required:
  113. found, missing = good, bad
  114. case optional:
  115. found, missing = good, warn
  116. case forbidden:
  117. found, missing = bad, good
  118. }
  119. var name string
  120. var opt kConfigOption
  121. var ok bool
  122. for _, name = range append([]string{config.Name}, config.Aliases...) {
  123. name = kernelConfigPrefix + name
  124. if opt, ok = allConfig[name]; ok {
  125. break
  126. }
  127. }
  128. if !ok {
  129. reportAndRecord(name, "not set", config.Description, missing)
  130. return
  131. }
  132. switch opt {
  133. case builtIn:
  134. reportAndRecord(name, "enabled", config.Description, found)
  135. case asModule:
  136. reportAndRecord(name, "enabled (as module)", config.Description, found)
  137. case leftOut:
  138. reportAndRecord(name, "disabled", config.Description, missing)
  139. default:
  140. reportAndRecord(name, fmt.Sprintf("unknown option: %s", opt), config.Description, missing)
  141. }
  142. }
  143. for _, config := range kSpec.Required {
  144. validateOpt(config, required)
  145. }
  146. for _, config := range kSpec.Optional {
  147. validateOpt(config, optional)
  148. }
  149. for _, config := range kSpec.Forbidden {
  150. validateOpt(config, forbidden)
  151. }
  152. if len(badConfigs) > 0 {
  153. return errors.Errorf("unexpected kernel config: %s", strings.Join(badConfigs, " "))
  154. }
  155. return nil
  156. }
  157. // getKernelConfigReader search kernel config file in a predefined list. Once the kernel config
  158. // file is found it will read the configurations into a byte buffer and return. If the kernel
  159. // config file is not found, it will try to load kernel config module and retry again.
  160. func (k *KernelValidator) getKernelConfigReader() (io.Reader, error) {
  161. possibePaths := []string{
  162. "/proc/config.gz",
  163. "/boot/config-" + k.kernelRelease,
  164. "/usr/src/linux-" + k.kernelRelease + "/.config",
  165. "/usr/src/linux/.config",
  166. "/usr/lib/modules/" + k.kernelRelease + "/config",
  167. "/usr/lib/ostree-boot/config-" + k.kernelRelease,
  168. "/usr/lib/kernel/config-" + k.kernelRelease,
  169. "/usr/src/linux-headers-" + k.kernelRelease + "/.config",
  170. "/lib/modules/" + k.kernelRelease + "/build/.config",
  171. }
  172. configsModule := "configs"
  173. modprobeCmd := "modprobe"
  174. // loadModule indicates whether we've tried to load kernel config module ourselves.
  175. loadModule := false
  176. for {
  177. for _, path := range possibePaths {
  178. _, err := os.Stat(path)
  179. if err != nil {
  180. continue
  181. }
  182. // Buffer the whole file, so that we can close the file and unload
  183. // kernel config module in this function.
  184. b, err := ioutil.ReadFile(path)
  185. if err != nil {
  186. return nil, err
  187. }
  188. var r io.Reader
  189. r = bytes.NewReader(b)
  190. // This is a gzip file (config.gz), unzip it.
  191. if filepath.Ext(path) == ".gz" {
  192. r, err = gzip.NewReader(r)
  193. if err != nil {
  194. return nil, err
  195. }
  196. }
  197. return r, nil
  198. }
  199. // If we've tried to load kernel config module, break and return error.
  200. if loadModule {
  201. break
  202. }
  203. // If the kernel config file is not found, try to load the kernel
  204. // config module and check again.
  205. output, err := exec.Command(modprobeCmd, configsModule).CombinedOutput()
  206. if err != nil {
  207. return nil, errors.Wrapf(err, "unable to load kernel module: %q, output: %q, err",
  208. configsModule, output)
  209. }
  210. // Unload the kernel config module to make sure the validation have no side effect.
  211. defer exec.Command(modprobeCmd, "-r", configsModule).Run()
  212. loadModule = true
  213. }
  214. return nil, errors.Errorf("no config path in %v is available", possibePaths)
  215. }
  216. // getKernelConfig gets kernel config from kernel config file and convert kernel config to internal type.
  217. func (k *KernelValidator) getKernelConfig() (map[string]kConfigOption, error) {
  218. r, err := k.getKernelConfigReader()
  219. if err != nil {
  220. return nil, err
  221. }
  222. return k.parseKernelConfig(r)
  223. }
  224. // parseKernelConfig converts kernel config to internal type.
  225. func (k *KernelValidator) parseKernelConfig(r io.Reader) (map[string]kConfigOption, error) {
  226. config := map[string]kConfigOption{}
  227. regex := regexp.MustCompile(validKConfigRegex)
  228. s := bufio.NewScanner(r)
  229. for s.Scan() {
  230. if err := s.Err(); err != nil {
  231. return nil, err
  232. }
  233. line := strings.TrimSpace(s.Text())
  234. if !regex.MatchString(line) {
  235. continue
  236. }
  237. fields := strings.Split(line, "=")
  238. if len(fields) != 2 {
  239. klog.Errorf("Unexpected fields number in config %q", line)
  240. continue
  241. }
  242. config[fields[0]] = kConfigOption(fields[1])
  243. }
  244. return config, nil
  245. }