diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index 227bc8ad8c..4e34a92e62 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -57,6 +57,16 @@ type Status struct { MetadataLoopback string Data DiskUsage Metadata DiskUsage + SectorSize uint64 +} + +type DevStatus struct { + DeviceId int + Size uint64 + TransactionId uint64 + SizeInSectors uint64 + MappedSectors uint64 + HighestMappedSector uint64 } func getDevName(name string) string { @@ -357,13 +367,24 @@ func minor(device uint64) uint64 { return (device & 0xff) | ((device >> 12) & 0xfff00) } -func (devices *DeviceSet) initDevmapper() error { +func (devices *DeviceSet) initDevmapper(doInit bool) error { logInit(devices) // Make sure the sparse images exist in /devicemapper/data and // /devicemapper/metadata - createdLoopback := !devices.hasImage("data") || !devices.hasImage("metadata") + hasData := devices.hasImage("data") + hasMetadata := devices.hasImage("metadata") + + if !doInit && !hasData { + return fmt.Errorf("Looback data file not found %s") + } + + if !doInit && !hasMetadata { + return fmt.Errorf("Looback metadata file not found %s") + } + + createdLoopback := !hasData || !hasMetadata data, err := devices.ensureImage("data", DefaultDataLoopbackSize) if err != nil { utils.Debugf("Error device ensureImage (data): %s\n", err) @@ -438,9 +459,11 @@ func (devices *DeviceSet) initDevmapper() error { } // Setup the base image - if err := devices.setupBaseImage(); err != nil { - utils.Debugf("Error device setupBaseImage: %s\n", err) - return err + if doInit { + if err := devices.setupBaseImage(); err != nil { + utils.Debugf("Error device setupBaseImage: %s\n", err) + return err + } } return nil @@ -757,6 +780,69 @@ func (devices *DeviceSet) setInitialized(hash string) error { return nil } +func (devices *DeviceSet) List() []string { + devices.Lock() + defer devices.Unlock() + + ids := make([]string, len(devices.Devices)) + i := 0 + for k := range devices.Devices { + ids[i] = k + i++ + } + return ids +} + +func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) { + var params string + _, sizeInSectors, _, params, err = getStatus(devName) + if err != nil { + return + } + if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil { + return + } + return +} + +func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) { + devices.Lock() + defer devices.Unlock() + + info := devices.Devices[hash] + if info == nil { + return nil, fmt.Errorf("No device %s", hash) + } + + status := &DevStatus{ + DeviceId: info.DeviceId, + Size: info.Size, + TransactionId: info.TransactionId, + } + + if err := devices.activateDeviceIfNeeded(hash); err != nil { + return nil, fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err) + } + + if sizeInSectors, mappedSectors, highestMappedSector, err := devices.deviceStatus(info.DevName()); err != nil { + return nil, err + } else { + status.SizeInSectors = sizeInSectors + status.MappedSectors = mappedSectors + status.HighestMappedSector = highestMappedSector + } + + return status, nil +} + +func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) { + var params string + if _, totalSizeInSectors, _, params, err = getStatus(devices.getPoolName()); err == nil { + _, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal) + } + return +} + func (devices *DeviceSet) Status() *Status { devices.Lock() defer devices.Unlock() @@ -767,26 +853,25 @@ func (devices *DeviceSet) Status() *Status { status.DataLoopback = path.Join(devices.loopbackDir(), "data") status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") - _, totalSizeInSectors, _, params, err := getStatus(devices.getPoolName()) + totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() if err == nil { - var transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64 - if _, err := fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal); err == nil { - // Convert from blocks to bytes - blockSizeInSectors := totalSizeInSectors / dataTotal + // Convert from blocks to bytes + blockSizeInSectors := totalSizeInSectors / dataTotal - status.Data.Used = dataUsed * blockSizeInSectors * 512 - status.Data.Total = dataTotal * blockSizeInSectors * 512 + status.Data.Used = dataUsed * blockSizeInSectors * 512 + status.Data.Total = dataTotal * blockSizeInSectors * 512 - // metadata blocks are always 4k - status.Metadata.Used = metadataUsed * 4096 - status.Metadata.Total = metadataTotal * 4096 - } + // metadata blocks are always 4k + status.Metadata.Used = metadataUsed * 4096 + status.Metadata.Total = metadataTotal * 4096 + + status.SectorSize = blockSizeInSectors * 512 } return status } -func NewDeviceSet(root string) (*DeviceSet, error) { +func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) { SetDevDir("/dev") devices := &DeviceSet{ @@ -795,7 +880,7 @@ func NewDeviceSet(root string) (*DeviceSet, error) { activeMounts: make(map[string]int), } - if err := devices.initDevmapper(); err != nil { + if err := devices.initDevmapper(doInit); err != nil { return nil, err } diff --git a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go b/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go index f66762da5e..bd7831c0c5 100644 --- a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go +++ b/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go @@ -1,59 +1,112 @@ package main import ( + "flag" "fmt" - "github.com/dotcloud/docker/devmapper" + "github.com/dotcloud/docker/graphdriver/devmapper" "os" + "path" + "sort" ) func usage() { - fmt.Printf("Usage: %s [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage: %s [status] | [list] | [device id] | [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0]) + flag.PrintDefaults() os.Exit(1) } func main() { - devices := devmapper.NewDeviceSet("/var/lib/docker") + root := flag.String("r", "/var/lib/docker", "Docker root dir") + flDebug := flag.Bool("D", false, "Debug mode") - if len(os.Args) < 2 { + flag.Parse() + + if *flDebug { + os.Setenv("DEBUG", "1") + } + + if flag.NArg() < 1 { usage() } - cmd := os.Args[1] - if cmd == "snap" { - if len(os.Args) < 4 { + args := flag.Args() + + home := path.Join(*root, "devicemapper") + devices, err := devmapper.NewDeviceSet(home, false) + if err != nil { + fmt.Println("Can't initialize device mapper: ", err) + os.Exit(1) + } + + switch args[0] { + case "status": + status := devices.Status() + fmt.Printf("Pool name: %s\n", status.PoolName) + fmt.Printf("Data Loopback file: %s\n", status.DataLoopback) + fmt.Printf("Metadata Loopback file: %s\n", status.MetadataLoopback) + fmt.Printf("Sector size: %d\n", status.SectorSize) + fmt.Printf("Data use: %d of %d (%.1f %%)\n", status.Data.Used, status.Data.Total, 100.0*float64(status.Data.Used)/float64(status.Data.Total)) + fmt.Printf("Metadata use: %d of %d (%.1f %%)\n", status.Metadata.Used, status.Metadata.Total, 100.0*float64(status.Metadata.Used)/float64(status.Metadata.Total)) + break + case "list": + ids := devices.List() + sort.Strings(ids) + for _, id := range ids { + fmt.Println(id) + } + break + case "device": + if flag.NArg() < 2 { + usage() + } + status, err := devices.GetDeviceStatus(args[1]) + if err != nil { + fmt.Println("Can't get device info: ", err) + os.Exit(1) + } + fmt.Printf("Id: %d\n", status.DeviceId) + fmt.Printf("Size: %d\n", status.Size) + fmt.Printf("Transaction Id: %d\n", status.TransactionId) + fmt.Printf("Size in Sectors: %d\n", status.SizeInSectors) + fmt.Printf("Mapped Sectors: %d\n", status.MappedSectors) + fmt.Printf("Highest Mapped Sector: %d\n", status.HighestMappedSector) + break + case "snap": + if flag.NArg() < 3 { usage() } - err := devices.AddDevice(os.Args[2], os.Args[3]) + err := devices.AddDevice(args[1], args[2]) if err != nil { fmt.Println("Can't create snap device: ", err) os.Exit(1) } - } else if cmd == "remove" { - if len(os.Args) < 3 { + break + case "remove": + if flag.NArg() < 2 { usage() } - err := devices.RemoveDevice(os.Args[2]) + err := devices.RemoveDevice(args[1]) if err != nil { fmt.Println("Can't remove device: ", err) os.Exit(1) } - } else if cmd == "mount" { - if len(os.Args) < 4 { + break + case "mount": + if flag.NArg() < 3 { usage() } - err := devices.MountDevice(os.Args[2], os.Args[3]) + err := devices.MountDevice(args[1], args[2], false) if err != nil { fmt.Println("Can't create snap device: ", err) os.Exit(1) } - } else { - fmt.Printf("Unknown command %s\n", cmd) - if len(os.Args) < 4 { - usage() - } + break + default: + fmt.Printf("Unknown command %s\n", args[0]) + usage() os.Exit(1) } diff --git a/components/engine/graphdriver/devmapper/driver.go b/components/engine/graphdriver/devmapper/driver.go index 315b7b091e..8e94b7bbc0 100644 --- a/components/engine/graphdriver/devmapper/driver.go +++ b/components/engine/graphdriver/devmapper/driver.go @@ -22,7 +22,7 @@ type Driver struct { } func Init(home string) (graphdriver.Driver, error) { - deviceSet, err := NewDeviceSet(home) + deviceSet, err := NewDeviceSet(home, true) if err != nil { return nil, err }