зеркало из https://github.com/microsoft/docker.git
devicemapper: Check loop devices of existing pool
Often it happens that docker is not able to shutdown/remove the thin pool it created because some device has leaked into some mount name space. That means device is in use and that means pool can't be removed. Docker will leave pool as it is and exit. Later when user starts the docker, it finds pool is already there and docker uses it. But docker does not know it is same pool which is using the loop devices. Now docker thinks loop devices are not being used. That means it does not display the data correctly in "docker info", giving user wrong information. This patch tries to detect if loop devices as created by docker are being used for pool and fills in the right details in "docker info". Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
This commit is contained in:
Родитель
2ea4d90da4
Коммит
bebf534439
|
@ -1081,6 +1081,126 @@ func determineDriverCapabilities(version string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Determine the major and minor number of loopback device
|
||||
func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) {
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
dev := stat.Sys().(*syscall.Stat_t).Rdev
|
||||
majorNum := major(dev)
|
||||
minorNum := minor(dev)
|
||||
|
||||
logrus.Debugf("[devmapper]: Major:Minor for device: %s is:%v:%v", file.Name(), majorNum, minorNum)
|
||||
return majorNum, minorNum, nil
|
||||
}
|
||||
|
||||
// Given a file which is backing file of a loop back device, find the
|
||||
// loopback device name and its major/minor number.
|
||||
func getLoopFileDeviceMajMin(filename string) (string, uint64, uint64, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
logrus.Debugf("[devmapper]: Failed to open file %s", filename)
|
||||
return "", 0, 0, err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
loopbackDevice := devicemapper.FindLoopDeviceFor(file)
|
||||
if loopbackDevice == nil {
|
||||
return "", 0, 0, fmt.Errorf("[devmapper]: Unable to find loopback mount for: %s", filename)
|
||||
}
|
||||
defer loopbackDevice.Close()
|
||||
|
||||
Major, Minor, err := getDeviceMajorMinor(loopbackDevice)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
}
|
||||
return loopbackDevice.Name(), Major, Minor, nil
|
||||
}
|
||||
|
||||
// Get the major/minor numbers of thin pool data and metadata devices
|
||||
func (devices *DeviceSet) getThinPoolDataMetaMajMin() (uint64, uint64, uint64, uint64, error) {
|
||||
var params, poolDataMajMin, poolMetadataMajMin string
|
||||
|
||||
_, _, _, params, err := devicemapper.GetTable(devices.getPoolName())
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
if _, err = fmt.Sscanf(params, "%s %s", &poolMetadataMajMin, &poolDataMajMin); err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
logrus.Debugf("[devmapper]: poolDataMajMin=%s poolMetaMajMin=%s\n", poolDataMajMin, poolMetadataMajMin)
|
||||
|
||||
poolDataMajMinorSplit := strings.Split(poolDataMajMin, ":")
|
||||
poolDataMajor, err := strconv.ParseUint(poolDataMajMinorSplit[0], 10, 32)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
poolDataMinor, err := strconv.ParseUint(poolDataMajMinorSplit[1], 10, 32)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
poolMetadataMajMinorSplit := strings.Split(poolMetadataMajMin, ":")
|
||||
poolMetadataMajor, err := strconv.ParseUint(poolMetadataMajMinorSplit[0], 10, 32)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
poolMetadataMinor, err := strconv.ParseUint(poolMetadataMajMinorSplit[1], 10, 32)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
return poolDataMajor, poolDataMinor, poolMetadataMajor, poolMetadataMinor, nil
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) loadThinPoolLoopBackInfo() error {
|
||||
poolDataMajor, poolDataMinor, poolMetadataMajor, poolMetadataMinor, err := devices.getThinPoolDataMetaMajMin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirname := devices.loopbackDir()
|
||||
|
||||
// data device has not been passed in. So there should be a data file
|
||||
// which is being mounted as loop device.
|
||||
if devices.dataDevice == "" {
|
||||
datafilename := path.Join(dirname, "data")
|
||||
dataLoopDevice, dataMajor, dataMinor, err := getLoopFileDeviceMajMin(datafilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Compare the two
|
||||
if poolDataMajor == dataMajor && poolDataMinor == dataMinor {
|
||||
devices.dataDevice = dataLoopDevice
|
||||
devices.dataLoopFile = datafilename
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// metadata device has not been passed in. So there should be a
|
||||
// metadata file which is being mounted as loop device.
|
||||
if devices.metadataDevice == "" {
|
||||
metadatafilename := path.Join(dirname, "metadata")
|
||||
metadataLoopDevice, metadataMajor, metadataMinor, err := getLoopFileDeviceMajMin(metadatafilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if poolMetadataMajor == metadataMajor && poolMetadataMinor == metadataMinor {
|
||||
devices.metadataDevice = metadataLoopDevice
|
||||
devices.metadataLoopFile = metadatafilename
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
||||
// give ourselves to libdm as a log handler
|
||||
devicemapper.LogInit(devices)
|
||||
|
@ -1225,6 +1345,17 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Pool already exists and caller did not pass us a pool. That means
|
||||
// we probably created pool earlier and could not remove it as some
|
||||
// containers were still using it. Detect some of the properties of
|
||||
// pool, like is it using loop devices.
|
||||
if info.Exists != 0 && devices.thinPoolDevice == "" {
|
||||
if err := devices.loadThinPoolLoopBackInfo(); err != nil {
|
||||
logrus.Debugf("Failed to load thin pool loopback device information:%v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't just create the data or metadata image, we need to
|
||||
// load the transaction id and migrate old metadata
|
||||
if !createdLoopback {
|
||||
|
|
|
@ -587,6 +587,31 @@ func GetStatus(name string) (uint64, uint64, string, string, error) {
|
|||
return start, length, targetType, params, nil
|
||||
}
|
||||
|
||||
func GetTable(name string) (uint64, uint64, string, string, error) {
|
||||
task, err := TaskCreateNamed(DeviceTable, name)
|
||||
if task == nil {
|
||||
logrus.Debugf("GetTable: Error TaskCreateNamed: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
if err := task.Run(); err != nil {
|
||||
logrus.Debugf("GetTable: Error Run: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
|
||||
devinfo, err := task.GetInfo()
|
||||
if err != nil {
|
||||
logrus.Debugf("GetTable: Error GetInfo: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
if devinfo.Exists == 0 {
|
||||
logrus.Debugf("GetTable: Non existing device %s", name)
|
||||
return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
|
||||
}
|
||||
|
||||
_, start, length, targetType, params := task.GetNextTarget(unsafe.Pointer(nil))
|
||||
return start, length, targetType, params, nil
|
||||
}
|
||||
|
||||
func SetTransactionId(poolName string, oldId uint64, newId uint64) error {
|
||||
task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
|
||||
if task == nil {
|
||||
|
|
Загрузка…
Ссылка в новой задаче