github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/mountprobe.go (about) 1 /* 2 Copyright 2019 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package probe 18 19 import ( 20 "k8s.io/klog/v2" 21 22 "github.com/openebs/node-disk-manager/blockdevice" 23 "github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller" 24 "github.com/openebs/node-disk-manager/pkg/epoll" 25 "github.com/openebs/node-disk-manager/pkg/features" 26 "github.com/openebs/node-disk-manager/pkg/mount" 27 "github.com/openebs/node-disk-manager/pkg/mount/libmount" 28 "github.com/openebs/node-disk-manager/pkg/util" 29 ) 30 31 // mountProbe contains required variables for populating diskInfo 32 type mountProbe struct { 33 // Every new probe needs a controller object to register itself. 34 // Here Controller consists of Clientset, kubeClientset, probes, etc which is used to 35 // create, update, delete, deactivate the disk resources or list the probes already registered. 36 Controller *controller.Controller 37 MountIdentifier *mount.Identifier 38 epoll *epoll.Epoll 39 destination chan controller.EventMessage 40 mountsFileName string 41 mountTable *libmount.MountTab 42 } 43 44 const ( 45 mountProbePriority = 4 46 mountConfigKey = "mount-probe" 47 ) 48 49 var ( 50 mountProbeName = "mount probe" 51 mountProbeState = defaultEnabled 52 ) 53 54 // init is used to get a controller object and then register itself 55 var mountProbeRegister = func() { 56 // Get a controller object 57 ctrl := <-controller.ControllerBroadcastChannel 58 if ctrl == nil { 59 klog.Error("unable to configure", mountProbeName) 60 return 61 } 62 if ctrl.NDMConfig != nil { 63 for _, probeConfig := range ctrl.NDMConfig.ProbeConfigs { 64 if probeConfig.Key == mountConfigKey { 65 mountProbeName = probeConfig.Name 66 mountProbeState = util.CheckTruthy(probeConfig.State) 67 break 68 } 69 } 70 } 71 newRegisterProbe := ®isterProbe{ 72 priority: mountProbePriority, 73 name: mountProbeName, 74 state: mountProbeState, 75 pi: newMountProbeForRegistration(ctrl), 76 controller: ctrl, 77 } 78 // Here we register the probe (mount probe in this case) 79 newRegisterProbe.register() 80 } 81 82 // newMountProbeForRegistration returns mountprobe struct which helps 83 // register the probe and start mount-point and fs change detection loop 84 func newMountProbeForRegistration(c *controller.Controller) *mountProbe { 85 return &mountProbe{ 86 Controller: c, 87 mountsFileName: mount.HostMountsFilePath, 88 destination: controller.EventMessageChannel, 89 } 90 } 91 92 // newMountProbe returns mountProbe struct which helps populate diskInfo struct 93 // with the mount related details like mountpoint 94 func newMountProbe(devPath string) *mountProbe { 95 mountIdentifier := &mount.Identifier{ 96 DevPath: devPath, 97 } 98 mountProbe := &mountProbe{ 99 MountIdentifier: mountIdentifier, 100 } 101 return mountProbe 102 } 103 104 // Start initializes moutprobe and sets up necessary watchers 105 // for mount change detection 106 func (mp *mountProbe) Start() { 107 if !features.FeatureGates. 108 IsEnabled(features.ChangeDetection) { 109 return 110 } 111 if err := mp.setupEpoll(); err != nil { 112 klog.Errorf("failed to setup epoll: %v", err) 113 return 114 } 115 mt, err := mp.newMountTable() 116 if err != nil { 117 klog.Errorf("failed to generate mount table") 118 return 119 } 120 mp.mountTable = mt 121 go mp.listen() 122 } 123 124 // FillBlockDeviceDetails fills details in diskInfo struct using information it gets from probe 125 func (mp *mountProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevice) { 126 if blockDevice.DevPath == "" { 127 klog.Error("mountIdentifier is found empty, mount probe will not fetch mount information.") 128 return 129 } 130 mountProbe := newMountProbe(blockDevice.DevPath) 131 basicMountInfo, err := mountProbe.MountIdentifier.DeviceBasicMountInfo(mount.HostMountsFilePath) 132 if err != nil { 133 if err == mount.ErrAttributesNotFound { 134 klog.Infof("no mount point found for %s. clearing mount points if any", 135 blockDevice.DevPath) 136 blockDevice.FSInfo.MountPoint = nil 137 return 138 } 139 klog.Error(err) 140 return 141 } 142 143 blockDevice.FSInfo.MountPoint = basicMountInfo.MountPoint 144 if blockDevice.FSInfo.FileSystem == "" { 145 blockDevice.FSInfo.FileSystem = basicMountInfo.FileSystem 146 } 147 } 148 149 func (mp *mountProbe) setupEpoll() error { 150 // create a buffered epoll so that we don't miss any change events 151 // due to slow consumption downstream 152 ep, err := epoll.New(epoll.BufferSize(10)) 153 if err != nil { 154 return err 155 } 156 mp.epoll = &ep 157 return ep.AddWatcher(epoll.Watcher{ 158 FileName: mp.mountsFileName, 159 EventTypes: []epoll.EventType{epoll.EPOLLERR, epoll.EPOLLPRI}, 160 }) 161 } 162 163 func (mp *mountProbe) listen() { 164 eventCh, err := mp.epoll.Start() 165 if err != nil { 166 klog.Errorf("error while starting epoll: %v", err) 167 return 168 } 169 defer mp.epoll.Stop() 170 klog.Info("started mount change detection loop") 171 defaultMsg := controller.EventMessage{ 172 Action: string(ChangeEA), 173 Devices: nil, 174 AllBlockDevices: true, 175 } 176 177 for range eventCh { 178 // regenerate mounts table and get the changes 179 newMountTable, err := mp.newMountTable() 180 if err != nil { 181 klog.Error("failed to generate mounts table.") 182 mp.destination <- defaultMsg 183 } 184 mtDiff := libmount.GenerateDiff(mp.mountTable, newMountTable) 185 mp.mountTable = newMountTable 186 mp.processDiff(mtDiff) 187 } 188 } 189 190 func (mp *mountProbe) newMountTable() (*libmount.MountTab, error) { 191 return libmount.NewMountTab(libmount.FromFile(mp.mountsFileName, 192 libmount.MntFmtFstab), 193 libmount.WithAllowFilter(libmount.SourceContainsFilter("/dev/")), 194 libmount.WithDenyFilter(libmount.SourceFilter("overlay")), 195 libmount.WithDenyFilter(libmount.TargetContainsFilter("/var/lib/kubelet/pod")), 196 libmount.WithDenyFilter(libmount.TargetContainsFilter("/var/lib/docker")), 197 libmount.WithDenyFilter(libmount.TargetContainsFilter("/run/docker"))) 198 } 199 200 func (mp *mountProbe) processDiff(diff libmount.MountTabDiff) { 201 devices := make([]*blockdevice.BlockDevice, 0) 202 changedDevices := diff.ListSources() 203 for _, dev := range changedDevices { 204 bd := new(blockdevice.BlockDevice) 205 bd.DevPath = dev 206 devices = append(devices, bd) 207 } 208 if len(devices) == 0 { 209 return 210 } 211 klog.V(4).Infof("detected mount/fs changes in %d devices", len(devices)) 212 mp.destination <- controller.EventMessage{ 213 Action: string(ChangeEA), 214 Devices: devices, 215 RequestedProbes: []string{mountProbeName}, 216 } 217 218 }