hostfiles.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // Copyright 2019 Yunion
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package hostinfo
  15. import (
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. api "yunion.io/x/onecloud/pkg/apis/compute"
  23. "yunion.io/x/onecloud/pkg/hostman/options"
  24. "yunion.io/x/onecloud/pkg/hostman/system_service"
  25. computemodules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  26. "yunion.io/x/onecloud/pkg/util/apparmorutils"
  27. "yunion.io/x/onecloud/pkg/util/fileutils2"
  28. "yunion.io/x/onecloud/pkg/util/procutils"
  29. )
  30. func (h *SHostInfo) loadExistingHostFiles() ([]api.SHostFile, error) {
  31. if !fileutils2.Exists(options.HostOptions.HostFilesPath) {
  32. return nil, nil
  33. }
  34. hostFilesContent, err := os.ReadFile(options.HostOptions.HostFilesPath)
  35. if err != nil {
  36. return nil, errors.Wrap(err, "os.ReadFile")
  37. }
  38. hostFilesJson, err := jsonutils.Parse(hostFilesContent)
  39. if err != nil {
  40. return nil, errors.Wrap(err, "jsonutils.Parse")
  41. }
  42. hostFiles := make([]api.SHostFile, 0)
  43. err = hostFilesJson.Unmarshal(&hostFiles)
  44. if err != nil {
  45. return nil, errors.Wrap(err, "json.Unmarshal")
  46. }
  47. return hostFiles, nil
  48. }
  49. func (h *SHostInfo) saveHostFiles(hostfiles []api.SHostFile) error {
  50. hostFilesJson := jsonutils.Marshal(hostfiles)
  51. err := os.WriteFile(options.HostOptions.HostFilesPath, []byte(hostFilesJson.String()), 0644)
  52. if err != nil {
  53. return errors.Wrap(err, "os.WriteFile")
  54. }
  55. return nil
  56. }
  57. func (h *SHostInfo) clearHostFiles() error {
  58. return os.Remove(options.HostOptions.HostFilesPath)
  59. }
  60. type hostFilePaire struct {
  61. old *api.SHostFile
  62. new *api.SHostFile
  63. }
  64. func (h *SHostInfo) OnHostFilesChanged(hostfiles []api.SHostFile) error {
  65. existing, err := h.loadExistingHostFiles()
  66. if err != nil {
  67. return errors.Wrap(err, "loadExistingHostFiles")
  68. }
  69. pairMap := make(map[string]*hostFilePaire)
  70. for i := range existing {
  71. hf := fixTelegrafConfPath(&existing[i])
  72. pairMap[hf.Id] = &hostFilePaire{
  73. old: hf,
  74. }
  75. }
  76. for i := range hostfiles {
  77. hf := fixTelegrafConfPath(&hostfiles[i])
  78. if _, ok := pairMap[hf.Id]; !ok {
  79. pairMap[hf.Id] = &hostFilePaire{
  80. new: hf,
  81. }
  82. } else {
  83. pairMap[hf.Id].new = hf
  84. }
  85. }
  86. for _, pair := range pairMap {
  87. if pair.new == nil {
  88. // delete file
  89. err := handleHostFileRemove(pair.old)
  90. if err != nil {
  91. return errors.Wrap(err, "handleHostFileRemove")
  92. }
  93. } else {
  94. // update file
  95. err := handleHostFileChanged(pair)
  96. if err != nil {
  97. return errors.Wrap(err, "handleHostFileChanged")
  98. }
  99. }
  100. }
  101. err = h.saveHostFiles(hostfiles)
  102. if err != nil {
  103. return errors.Wrap(err, "saveHostFiles")
  104. }
  105. return nil
  106. }
  107. func (h *SHostInfo) initHostFiles() error {
  108. hostFilesObj, err := computemodules.Hosts.GetSpecific(h.GetSession(), h.GetId(), "host-files", nil)
  109. if err != nil {
  110. return errors.Wrap(err, "computemodules.Hosts.GetSpecific")
  111. }
  112. hostFiles := []api.SHostFile{}
  113. err = hostFilesObj.Unmarshal(&hostFiles, "host_files")
  114. if err != nil {
  115. return errors.Wrap(err, "Unmarshal")
  116. }
  117. if err := h.clearHostFiles(); err != nil {
  118. return errors.Wrap(err, "clearHostFiles")
  119. }
  120. return h.OnHostFilesChanged(hostFiles)
  121. }
  122. func handleHostFileChanged(pair *hostFilePaire) error {
  123. switch pair.new.Type {
  124. case string(api.ApparmorProfile):
  125. if pair.old == nil || pair.old.Content != pair.new.Content {
  126. if !apparmorutils.IsEnabled() {
  127. log.Warningf("apparmor is not enabled, skip loading profile %s", pair.new.Name)
  128. } else {
  129. log.Infof("load apparmor profile %s", pair.new.Name)
  130. err := apparmorutils.Parser(pair.new.Content)
  131. if err != nil {
  132. return errors.Wrap(err, "apparmorutils.Parser")
  133. }
  134. }
  135. }
  136. default:
  137. changed := false
  138. if pair.old == nil || pair.old.Path != pair.new.Path || pair.old.Content != pair.new.Content {
  139. // new or changed file
  140. log.Infof("update host file %s", pair.new.Name)
  141. err := procutils.FilePutContents(pair.new.Path, pair.new.Content)
  142. if err != nil {
  143. return errors.Wrap(err, "procutils.FilePutContents")
  144. }
  145. changed = true
  146. }
  147. if pair.old != nil && pair.old.Path != pair.new.Path {
  148. // remove old file
  149. log.Infof("remove obsoleted host file %s", pair.old.Name)
  150. err := procutils.NewRemoteCommandAsFarAsPossible("rm", "-f", pair.old.Path).Run()
  151. if err != nil {
  152. return errors.Wrapf(err, "remove file %s", pair.old.Path)
  153. }
  154. changed = true
  155. }
  156. if changed {
  157. err := finalizeFileChange(pair.new)
  158. if err != nil {
  159. return errors.Wrap(err, "finalizeFileChange")
  160. }
  161. }
  162. }
  163. return nil
  164. }
  165. func handleHostFileRemove(hostFile *api.SHostFile) error {
  166. switch hostFile.Type {
  167. case string(api.ApparmorProfile):
  168. // do nothing
  169. default:
  170. log.Infof("remove file %s", hostFile.Path)
  171. err := procutils.NewRemoteCommandAsFarAsPossible("rm", "-f", hostFile.Path).Run()
  172. if err != nil {
  173. return errors.Wrapf(err, "remove file %s", hostFile.Path)
  174. }
  175. {
  176. err := finalizeFileChange(hostFile)
  177. if err != nil {
  178. return errors.Wrap(err, "finalizeFileChange")
  179. }
  180. }
  181. }
  182. return nil
  183. }
  184. func finalizeFileChange(hostFile *api.SHostFile) error {
  185. switch hostFile.Type {
  186. case string(api.TelegrafConf):
  187. telegrafService := system_service.NewTelegrafService()
  188. err := telegrafService.ReloadTelegraf()
  189. if err != nil {
  190. log.Warningf("failed to reload telegraf: %s", err)
  191. }
  192. case string(api.ScriptFile):
  193. err := procutils.NewRemoteCommandAsFarAsPossible("chmod", "+x", hostFile.Path).Run()
  194. if err != nil {
  195. log.Warningf("failed to chmod script file %s: %s", hostFile.Path, err)
  196. }
  197. }
  198. return nil
  199. }
  200. func fixTelegrafConfPath(hostFile *api.SHostFile) *api.SHostFile {
  201. if hostFile.Type != string(api.TelegrafConf) {
  202. return hostFile
  203. }
  204. var baseFile string
  205. if len(hostFile.Path) > 0 {
  206. baseFile = filepath.Base(hostFile.Path)
  207. }
  208. if len(baseFile) == 0 {
  209. baseFile = fmt.Sprintf("%s.conf", hostFile.Name)
  210. }
  211. telegrafDDir := system_service.GetTelegrafConfDDir()
  212. procutils.NewRemoteCommandAsFarAsPossible("mkdir", "-p", telegrafDDir).Run()
  213. hostFile.Path = filepath.Join(telegrafDDir, baseFile)
  214. return hostFile
  215. }