mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
407a626be6
Export image/container metadata stored in graph driver. Right now 3 fields DeviceId, DeviceSize and DeviceName are being exported from devicemapper. Other graph drivers can export fields as they see fit. This data can be used to mount the thin device outside of docker and tools can look into image/container and do some kind of inspection. Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
190 lines
4.7 KiB
Go
190 lines
4.7 KiB
Go
// +build linux
|
|
|
|
package devmapper
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/daemon/graphdriver"
|
|
"github.com/docker/docker/pkg/devicemapper"
|
|
"github.com/docker/docker/pkg/mount"
|
|
"github.com/docker/docker/pkg/units"
|
|
)
|
|
|
|
func init() {
|
|
graphdriver.Register("devicemapper", Init)
|
|
}
|
|
|
|
// Placeholder interfaces, to be replaced
|
|
// at integration.
|
|
|
|
// End of placeholder interfaces.
|
|
|
|
type Driver struct {
|
|
*DeviceSet
|
|
home string
|
|
}
|
|
|
|
var backingFs = "<unknown>"
|
|
|
|
func Init(home string, options []string) (graphdriver.Driver, error) {
|
|
fsMagic, err := graphdriver.GetFSMagic(home)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
|
|
backingFs = fsName
|
|
}
|
|
|
|
deviceSet, err := NewDeviceSet(home, true, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := mount.MakePrivate(home); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
d := &Driver{
|
|
DeviceSet: deviceSet,
|
|
home: home,
|
|
}
|
|
|
|
return graphdriver.NaiveDiffDriver(d), nil
|
|
}
|
|
|
|
func (d *Driver) String() string {
|
|
return "devicemapper"
|
|
}
|
|
|
|
func (d *Driver) Status() [][2]string {
|
|
s := d.DeviceSet.Status()
|
|
|
|
status := [][2]string{
|
|
{"Pool Name", s.PoolName},
|
|
{"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))},
|
|
{"Backing Filesystem", backingFs},
|
|
{"Data file", s.DataFile},
|
|
{"Metadata file", s.MetadataFile},
|
|
{"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
|
|
{"Data Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Total)))},
|
|
{"Data Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Available)))},
|
|
{"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Used)))},
|
|
{"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Total)))},
|
|
{"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))},
|
|
{"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
|
|
{"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
|
|
}
|
|
if len(s.DataLoopback) > 0 {
|
|
status = append(status, [2]string{"Data loop file", s.DataLoopback})
|
|
}
|
|
if len(s.MetadataLoopback) > 0 {
|
|
status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback})
|
|
}
|
|
if vStr, err := devicemapper.GetLibraryVersion(); err == nil {
|
|
status = append(status, [2]string{"Library Version", vStr})
|
|
}
|
|
return status
|
|
}
|
|
|
|
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
|
m, err := d.DeviceSet.ExportDeviceMetadata(id)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
metadata := make(map[string]string)
|
|
metadata["DeviceId"] = strconv.Itoa(m.deviceId)
|
|
metadata["DeviceSize"] = strconv.FormatUint(m.deviceSize, 10)
|
|
metadata["DeviceName"] = m.deviceName
|
|
return metadata, nil
|
|
}
|
|
|
|
func (d *Driver) Cleanup() error {
|
|
err := d.DeviceSet.Shutdown()
|
|
|
|
if err2 := mount.Unmount(d.home); err == nil {
|
|
err = err2
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (d *Driver) Create(id, parent string) error {
|
|
if err := d.DeviceSet.AddDevice(id, parent); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Driver) Remove(id string) error {
|
|
if !d.DeviceSet.HasDevice(id) {
|
|
// Consider removing a non-existing device a no-op
|
|
// This is useful to be able to progress on container removal
|
|
// if the underlying device has gone away due to earlier errors
|
|
return nil
|
|
}
|
|
|
|
// This assumes the device has been properly Get/Put:ed and thus is unmounted
|
|
if err := d.DeviceSet.DeleteDevice(id); err != nil {
|
|
return err
|
|
}
|
|
|
|
mp := path.Join(d.home, "mnt", id)
|
|
if err := os.RemoveAll(mp); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
|
mp := path.Join(d.home, "mnt", id)
|
|
|
|
// Create the target directories if they don't exist
|
|
if err := os.MkdirAll(mp, 0755); err != nil && !os.IsExist(err) {
|
|
return "", err
|
|
}
|
|
|
|
// Mount the device
|
|
if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
rootFs := path.Join(mp, "rootfs")
|
|
if err := os.MkdirAll(rootFs, 0755); err != nil && !os.IsExist(err) {
|
|
d.DeviceSet.UnmountDevice(id)
|
|
return "", err
|
|
}
|
|
|
|
idFile := path.Join(mp, "id")
|
|
if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) {
|
|
// Create an "id" file with the container/image id in it to help reconscruct this in case
|
|
// of later problems
|
|
if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
|
|
d.DeviceSet.UnmountDevice(id)
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return rootFs, nil
|
|
}
|
|
|
|
func (d *Driver) Put(id string) error {
|
|
err := d.DeviceSet.UnmountDevice(id)
|
|
if err != nil {
|
|
logrus.Errorf("Error unmounting device %s: %s", id, err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *Driver) Exists(id string) bool {
|
|
return d.DeviceSet.HasDevice(id)
|
|
}
|