1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #13198 from rhvgoyal/extend-docker-inspect

docker-inspect: Extend docker inspect to export image metadata related to graph driver
This commit is contained in:
Vincent Batts 2015-06-16 15:03:14 -05:00
commit e69df2589c
14 changed files with 353 additions and 205 deletions

View file

@ -75,6 +75,11 @@ type Image struct {
Labels map[string]string Labels map[string]string
} }
type GraphDriverData struct {
Name string
Data map[string]string
}
// GET "/images/{name:.*}/json" // GET "/images/{name:.*}/json"
type ImageInspect struct { type ImageInspect struct {
Id string Id string
@ -90,6 +95,7 @@ type ImageInspect struct {
Os string Os string
Size int64 Size int64
VirtualSize int64 VirtualSize int64
GraphDriver GraphDriverData
} }
// GET "/containers/json" // GET "/containers/json"
@ -218,6 +224,7 @@ type ContainerJSONBase struct {
AppArmorProfile string AppArmorProfile string
ExecIDs []string ExecIDs []string
HostConfig *runconfig.HostConfig HostConfig *runconfig.HostConfig
GraphDriver GraphDriverData
} }
type ContainerJSON struct { type ContainerJSON struct {

View file

@ -162,6 +162,10 @@ func (a *Driver) Status() [][2]string {
} }
} }
func (a *Driver) GetMetadata(id string) (map[string]string, error) {
return nil, nil
}
// Exists returns true if the given id is registered with // Exists returns true if the given id is registered with
// this driver // this driver
func (a *Driver) Exists(id string) bool { func (a *Driver) Exists(id string) bool {

View file

@ -70,6 +70,10 @@ func (d *Driver) Status() [][2]string {
return status return status
} }
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
return nil, nil
}
func (d *Driver) Cleanup() error { func (d *Driver) Cleanup() error {
return mount.Unmount(d.home) return mount.Unmount(d.home)
} }

View file

@ -127,6 +127,13 @@ type Status struct {
DeferredRemoveEnabled bool DeferredRemoveEnabled bool
} }
// Structure used to export image/container metadata in docker inspect.
type DeviceMetadata struct {
deviceId int
deviceSize uint64 // size in bytes
deviceName string // Device name as used during activation
}
type DevStatus struct { type DevStatus struct {
DeviceId int DeviceId int
Size uint64 Size uint64
@ -1700,6 +1707,20 @@ func (devices *DeviceSet) Status() *Status {
return status return status
} }
// Status returns the current status of this deviceset
func (devices *DeviceSet) ExportDeviceMetadata(hash string) (*DeviceMetadata, error) {
info, err := devices.lookupDevice(hash)
if err != nil {
return nil, err
}
info.lock.Lock()
defer info.lock.Unlock()
metadata := &DeviceMetadata{info.DeviceId, info.Size, info.Name()}
return metadata, nil
}
func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) { func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) {
devicemapper.SetDevDir("/dev") devicemapper.SetDevDir("/dev")

View file

@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"strconv"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/daemon/graphdriver"
@ -91,6 +92,20 @@ func (d *Driver) Status() [][2]string {
return status 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 { func (d *Driver) Cleanup() error {
err := d.DeviceSet.Shutdown() err := d.DeviceSet.Shutdown()

View file

@ -56,6 +56,9 @@ type ProtoDriver interface {
// Status returns a set of key-value pairs which give low // Status returns a set of key-value pairs which give low
// level diagnostic status about this driver. // level diagnostic status about this driver.
Status() [][2]string Status() [][2]string
// Returns a set of key-value pairs which give low level information
// about the image/container driver is managing.
GetMetadata(id string) (map[string]string, error)
// Cleanup performs necessary tasks to release resources // Cleanup performs necessary tasks to release resources
// held by the driver, e.g., unmounting all layered filesystems // held by the driver, e.g., unmounting all layered filesystems
// known to this driver. // known to this driver.

View file

@ -167,6 +167,10 @@ func (d *Driver) Status() [][2]string {
} }
} }
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
return nil, nil
}
func (d *Driver) Cleanup() error { func (d *Driver) Cleanup() error {
return nil return nil
} }

View file

@ -36,6 +36,10 @@ func (d *Driver) Status() [][2]string {
return nil return nil
} }
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
return nil, nil
}
func (d *Driver) Cleanup() error { func (d *Driver) Cleanup() error {
return nil return nil
} }

View file

@ -186,6 +186,10 @@ func (d *Driver) Status() [][2]string {
} }
} }
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
return nil, nil
}
func (d *Driver) cloneFilesystem(name, parentName string) error { func (d *Driver) cloneFilesystem(name, parentName string) error {
snapshotName := fmt.Sprintf("%d", time.Now().Nanosecond()) snapshotName := fmt.Sprintf("%d", time.Now().Nanosecond())
parentDataset := zfs.Dataset{Name: parentName} parentDataset := zfs.Dataset{Name: parentName}

View file

@ -109,6 +109,13 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON
HostConfig: &hostConfig, HostConfig: &hostConfig,
} }
contJSONBase.GraphDriver.Name = container.Driver
graphDriverData, err := daemon.driver.GetMetadata(container.ID)
if err != nil {
return nil, err
}
contJSONBase.GraphDriver.Data = graphDriverData
return contJSONBase, nil return contJSONBase, nil
} }

View file

@ -45,6 +45,13 @@ func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) {
VirtualSize: s.graph.GetParentsSize(image, 0) + image.Size, VirtualSize: s.graph.GetParentsSize(image, 0) + image.Size,
} }
imageInspect.GraphDriver.Name = s.graph.driver.String()
graphDriverData, err := s.graph.driver.GetMetadata(image.ID)
if err != nil {
return nil, err
}
imageInspect.GraphDriver.Data = graphDriverData
return imageInspect, nil return imageInspect, nil
} }

View file

@ -28,7 +28,7 @@ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) {
c.Fatalf("unable to unmarshal body for latest version: %v", err) c.Fatalf("unable to unmarshal body for latest version: %v", err)
} }
keys := []string{"State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings", "ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "ExecDriver", "MountLabel", "ProcessLabel", "Volumes", "VolumesRW"} keys := []string{"State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings", "ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "ExecDriver", "MountLabel", "ProcessLabel", "Volumes", "VolumesRW", "GraphDriver"}
keys = append(keys, "Id") keys = append(keys, "Id")

View file

@ -89,3 +89,70 @@ func (s *DockerSuite) TestInspectContainerFilterInt(c *check.C) {
c.Fatalf("Expected exitcode: %d for container: %s", exitCode, id) c.Fatalf("Expected exitcode: %d for container: %s", exitCode, id)
} }
} }
func (s *DockerSuite) TestInspectImageGraphDriver(c *check.C) {
imageTest := "emptyfs"
name, err := inspectField(imageTest, "GraphDriver.Name")
c.Assert(err, check.IsNil)
if name != "devicemapper" && name != "overlay" && name != "vfs" && name != "zfs" && name != "btrfs" && name != "aufs" {
c.Fatalf("%v is not a valid graph driver name", name)
}
if name != "devicemapper" {
return
}
deviceId, err := inspectField(imageTest, "GraphDriver.Data.DeviceId")
c.Assert(err, check.IsNil)
_, err = strconv.Atoi(deviceId)
if err != nil {
c.Fatalf("failed to inspect DeviceId of the image: %s, %v", deviceId, err)
}
deviceSize, err := inspectField(imageTest, "GraphDriver.Data.DeviceSize")
c.Assert(err, check.IsNil)
_, err = strconv.ParseUint(deviceSize, 10, 64)
if err != nil {
c.Fatalf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
}
}
func (s *DockerSuite) TestInspectContainerGraphDriver(c *check.C) {
runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true")
out, _, _, err := runCommandWithStdoutStderr(runCmd)
if err != nil {
c.Fatalf("failed to run container: %v, output: %q", err, out)
}
out = strings.TrimSpace(out)
name, err := inspectField(out, "GraphDriver.Name")
c.Assert(err, check.IsNil)
if name != "devicemapper" && name != "overlay" && name != "vfs" && name != "zfs" && name != "btrfs" && name != "aufs" {
c.Fatalf("%v is not a valid graph driver name", name)
}
if name != "devicemapper" {
return
}
deviceId, err := inspectField(out, "GraphDriver.Data.DeviceId")
c.Assert(err, check.IsNil)
_, err = strconv.Atoi(deviceId)
if err != nil {
c.Fatalf("failed to inspect DeviceId of the image: %s, %v", deviceId, err)
}
deviceSize, err := inspectField(out, "GraphDriver.Data.DeviceSize")
c.Assert(err, check.IsNil)
_, err = strconv.ParseUint(deviceSize, 10, 64)
if err != nil {
c.Fatalf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
}
}

View file

@ -30,143 +30,144 @@ each result.
To get information on a container use its ID or instance name: To get information on a container use its ID or instance name:
$ docker inspect 1eb5fabf5a03 $ docker inspect d2cc496561d6
[{ [{
"AppArmorProfile": "", "Id": "d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47",
"Args": [], "Created": "2015-06-08T16:18:02.505155285Z",
"Config": { "Path": "bash",
"AttachStderr": false, "Args": [],
"AttachStdin": false, "State": {
"AttachStdout": false, "Running": false,
"Cmd": [ "Paused": false,
"/usr/sbin/nginx" "Restarting": false,
], "OOMKilled": false,
"Domainname": "", "Dead": false,
"Entrypoint": null, "Pid": 0,
"Env": [ "ExitCode": 0,
"HOME=/", "Error": "",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" "StartedAt": "2015-06-08T16:18:03.643865954Z",
], "FinishedAt": "2015-06-08T16:57:06.448552862Z"
"ExposedPorts": { },
"80/tcp": {} "Image": "ded7cd95e059788f2586a51c275a4f151653779d6a7f4dad77c2bd34601d94e4",
}, "NetworkSettings": {
"Hostname": "1eb5fabf5a03", "Bridge": "",
"Image": "summit/nginx", "EndpointID": "",
"Labels": { "Gateway": "",
"com.example.vendor": "Acme", "GlobalIPv6Address": "",
"com.example.license": "GPL", "GlobalIPv6PrefixLen": 0,
"com.example.version": "1.0" "HairpinMode": false,
}, "IPAddress": "",
"MacAddress": "", "IPPrefixLen": 0,
"NetworkDisabled": false, "IPv6Gateway": "",
"OnBuild": null, "LinkLocalIPv6Address": "",
"OpenStdin": false, "LinkLocalIPv6PrefixLen": 0,
"StdinOnce": false, "MacAddress": "",
"Tty": true, "NetworkID": "",
"User": "", "PortMapping": null,
"Volumes": null, "Ports": null,
"WorkingDir": "", "SandboxKey": "",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null
},
"ResolvConfPath": "/var/lib/docker/containers/d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47/hostname",
"HostsPath": "/var/lib/docker/containers/d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47/hosts",
"LogPath": "/var/lib/docker/containers/d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47/d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47-json.log",
"Name": "/adoring_wozniak",
"RestartCount": 0,
"Driver": "devicemapper",
"ExecDriver": "native-0.2",
"MountLabel": "",
"ProcessLabel": "",
"Volumes": {},
"VolumesRW": {},
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LxcConf": [],
"Memory": 0,
"MemorySwap": 0,
"CpuShares": 0,
"CpuPeriod": 0,
"CpusetCpus": "",
"CpusetMems": "",
"CpuQuota": 0,
"BlkioWeight": 0,
"OomKillDisable": false,
"Privileged": false,
"PortBindings": {},
"Links": null,
"PublishAllPorts": false,
"Dns": null,
"DnsSearch": null,
"ExtraHosts": null,
"VolumesFrom": null,
"Devices": [],
"NetworkMode": "bridge",
"IpcMode": "",
"PidMode": "",
"UTSMode": "",
"CapAdd": null,
"CapDrop": null,
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
}, },
"Created": "2014-04-04T21:33:52.02361335Z", "SecurityOpt": null,
"Driver": "devicemapper", "ReadonlyRootfs": false,
"ExecDriver": "native-0.1", "Ulimits": null,
"ExecIDs": null, "LogConfig": {
"HostConfig": { "Type": "json-file",
"Binds": null, "Config": {}
"CapAdd": null, },
"CapDrop": null, "CgroupParent": ""
"CgroupParent": "", },
"ContainerIDFile": "", "GraphDriver": {
"CpuShares": 512, "Name": "devicemapper",
"CpusetCpus": "0,1", "Data": {
"CpusetMems": "", "DeviceId": "5",
"Devices": [], "DeviceName": "docker-253:1-2763198-d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47",
"Dns": null, "DeviceSize": "171798691840"
"DnsSearch": null,
"ExtraHosts": null,
"IpcMode": "",
"Links": null,
"LogConfig": {
"Config": null,
"Type": "json-file"
},
"LxcConf": null,
"Memory": 16777216,
"MemorySwap": -1,
"NetworkMode": "",
"PidMode": "",
"PortBindings": {
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "80"
}
]
},
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"RestartPolicy": {
"MaximumRetryCount": 0,
"Name": ""
},
"SecurityOpt": null,
"Ulimits": null,
"VolumesFrom": null
} }
"HostnamePath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/hostname", },
"HostsPath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/hosts", "Config": {
"ID": "1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b", "Hostname": "d2cc496561d6",
"Image": "df53773a4390e25936f9fd3739e0c0e60a62d024ea7b669282b27e65ae8458e6", "Domainname": "",
"LogPath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log", "User": "",
"MountLabel": "", "AttachStdin": true,
"Name": "/ecstatic_ptolemy", "AttachStdout": true,
"NetworkSettings": { "AttachStderr": true,
"Bridge": "docker0", "ExposedPorts": null,
"Gateway": "172.17.42.1", "Tty": true,
"GlobalIPv6Address": "", "OpenStdin": true,
"GlobalIPv6PrefixLen": 0, "StdinOnce": true,
"IPAddress": "172.17.0.2", "Env": null,
"IPPrefixLen": 16, "Cmd": [
"IPv6Gateway": "", "bash"
"LinkLocalIPv6Address": "", ],
"LinkLocalIPv6PrefixLen": 0, "Image": "fedora",
"MacAddress": "", "Volumes": null,
"PortMapping": null, "VolumeDriver": "",
"Ports": { "WorkingDir": "",
"80/tcp": [ "Entrypoint": null,
{ "NetworkDisabled": false,
"HostIp": "0.0.0.0", "MacAddress": "",
"HostPort": "80" "OnBuild": null,
} "Labels": {},
] "Memory": 0,
} "MemorySwap": 0,
}, "CpuShares": 0,
"Path": "/usr/sbin/nginx", "Cpuset": ""
"ProcessLabel": "",
"ResolvConfPath": "/etc/resolv.conf",
"RestartCount": 0,
"State": {
"Dead": false,
"Error": "",
"ExitCode": 0,
"FinishedAt": "0001-01-01T00:00:00Z",
"OOMKilled": false,
"Paused": false,
"Pid": 858,
"Restarting": false,
"Running": true,
"StartedAt": "2014-04-04T21:33:54.16259207Z",
},
"Volumes": {},
"VolumesRW": {},
} }
}
]
## Getting the IP address of a container instance ## Getting the IP address of a container instance
To get the IP address of a container use: To get the IP address of a container use:
$ docker inspect --format='{{.NetworkSettings.IPAddress}}' 1eb5fabf5a03 $ docker inspect --format='{{.NetworkSettings.IPAddress}}' d2cc496561d6
172.17.0.2 172.17.0.2
## Listing all port bindings ## Listing all port bindings
@ -175,7 +176,7 @@ One can loop over arrays and maps in the results to produce simple text
output: output:
$ docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} \ $ docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} \
{{$p}} -> {{(index $conf 0).HostPort}} {{end}}' 1eb5fabf5a03 {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' d2cc496561d6
80/tcp -> 80 80/tcp -> 80
You can get more information about how to write a go template from: You can get more information about how to write a go template from:
@ -186,79 +187,79 @@ http://golang.org/pkg/text/template/.
Use an image's ID or name (e.g., repository/name[:tag]) to get information Use an image's ID or name (e.g., repository/name[:tag]) to get information
on it. on it.
$ docker inspect fc1203419df2 $ docker inspect ded7cd95e059
[{ [{
"Architecture": "amd64", "Id": "ded7cd95e059788f2586a51c275a4f151653779d6a7f4dad77c2bd34601d94e4",
"Author": "", "Parent": "48ecf305d2cf7046c1f5f8fcbcd4994403173441d4a7f125b1bb0ceead9de731",
"Comment": "", "Comment": "",
"Config": { "Created": "2015-05-27T16:58:22.937503085Z",
"AttachStderr": false, "Container": "76cf7f67d83a7a047454b33007d03e32a8f474ad332c3a03c94537edd22b312b",
"AttachStdin": false, "ContainerConfig": {
"AttachStdout": false, "Hostname": "76cf7f67d83a",
"Cmd": [ "Domainname": "",
"make", "User": "",
"direct-test" "AttachStdin": false,
], "AttachStdout": false,
"Domainname": "", "AttachStderr": false,
"Entrypoint": [ "ExposedPorts": null,
"/dind" "Tty": false,
], "OpenStdin": false,
"Env": [ "StdinOnce": false,
"PATH=/go/bin:/usr/src/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "Env": null,
], "Cmd": [
"ExposedPorts": null, "/bin/sh",
"Hostname": "242978536a06", "-c",
"Image": "c2b774c744afc5bea603b5e6c5218539e506649326de3ea0135182f299d0519a", "#(nop) ADD file:4be46382bcf2b095fcb9fe8334206b584eff60bb3fad8178cbd97697fcb2ea83 in /"
"Labels": {}, ],
"MacAddress": "", "Image": "48ecf305d2cf7046c1f5f8fcbcd4994403173441d4a7f125b1bb0ceead9de731",
"NetworkDisabled": false, "Volumes": null,
"OnBuild": [], "VolumeDriver": "",
"OpenStdin": false, "WorkingDir": "",
"StdinOnce": false, "Entrypoint": null,
"Tty": false, "NetworkDisabled": false,
"User": "", "MacAddress": "",
"Volumes": null, "OnBuild": null,
"WorkingDir": "/go/src/github.com/docker/libcontainer" "Labels": {}
}, },
"Container": "1c00417f3812a96d3ebc29e7fdee69f3d586d703ab89c8233fd4678d50707b39", "DockerVersion": "1.6.0",
"ContainerConfig": { "Author": "Lokesh Mandvekar \u003clsm5@fedoraproject.org\u003e",
"AttachStderr": false, "Config": {
"AttachStdin": false, "Hostname": "76cf7f67d83a",
"AttachStdout": false, "Domainname": "",
"Cmd": [ "User": "",
"/bin/sh", "AttachStdin": false,
"-c", "AttachStdout": false,
"#(nop) CMD [\"make\" \"direct-test\"]" "AttachStderr": false,
], "ExposedPorts": null,
"Domainname": "", "Tty": false,
"Entrypoint": [ "OpenStdin": false,
"/dind" "StdinOnce": false,
], "Env": null,
"Env": [ "Cmd": null,
"PATH=/go/bin:/usr/src/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "Image": "48ecf305d2cf7046c1f5f8fcbcd4994403173441d4a7f125b1bb0ceead9de731",
], "Volumes": null,
"ExposedPorts": null, "VolumeDriver": "",
"Hostname": "242978536a06", "WorkingDir": "",
"Image": "c2b774c744afc5bea603b5e6c5218539e506649326de3ea0135182f299d0519a", "Entrypoint": null,
"Labels": {}, "NetworkDisabled": false,
"MacAddress": "", "MacAddress": "",
"NetworkDisabled": false, "OnBuild": null,
"OnBuild": [], "Labels": {}
"OpenStdin": false, },
"StdinOnce": false, "Architecture": "amd64",
"Tty": false, "Os": "linux",
"User": "", "Size": 186507296,
"Volumes": null, "VirtualSize": 186507296,
"WorkingDir": "/go/src/github.com/docker/libcontainer" "GraphDriver": {
}, "Name": "devicemapper",
"Created": "2015-04-07T05:34:39.079489206Z", "Data": {
"DockerVersion": "1.5.0-dev", "DeviceId": "3",
"Id": "fc1203419df26ca82cad1dd04c709cb1b8a8a947bd5bcbdfbef8241a76f031db", "DeviceName": "docker-253:1-2763198-ded7cd95e059788f2586a51c275a4f151653779d6a7f4dad77c2bd34601d94e4",
"Os": "linux", "DeviceSize": "171798691840"
"Parent": "c2b774c744afc5bea603b5e6c5218539e506649326de3ea0135182f299d0519a", }
"Size": 0, }
"VirtualSize": 613136466 }
}] ]
# HISTORY # HISTORY
April 2014, originally compiled by William Henry (whenry at redhat dot com) April 2014, originally compiled by William Henry (whenry at redhat dot com)