commit
4910466050
16
cadvisor.go
16
cadvisor.go
@ -63,12 +63,12 @@ func main() {
|
||||
|
||||
// Register the raw driver.
|
||||
if err := raw.Register(containerManager); err != nil {
|
||||
glog.Fatalf("raw registration failed: %v.", err)
|
||||
glog.Fatalf("Raw registration failed: %v.", err)
|
||||
}
|
||||
|
||||
// Basic health handler.
|
||||
if err := healthz.RegisterHandler(); err != nil {
|
||||
glog.Fatalf("failed to register healthz handler: %s", err)
|
||||
glog.Fatalf("Failed to register healthz handler: %s", err)
|
||||
}
|
||||
|
||||
// Handler for static content.
|
||||
@ -81,19 +81,15 @@ func main() {
|
||||
|
||||
// Register API handler.
|
||||
if err := api.RegisterHandlers(containerManager); err != nil {
|
||||
glog.Fatalf("failed to register API handlers: %s", err)
|
||||
glog.Fatalf("Failed to register API handlers: %s", err)
|
||||
}
|
||||
|
||||
// Redirect / to containers page.
|
||||
http.Handle("/", http.RedirectHandler(pages.ContainersPage, http.StatusTemporaryRedirect))
|
||||
|
||||
// Register the handler for the containers page.
|
||||
http.HandleFunc(pages.ContainersPage, func(w http.ResponseWriter, r *http.Request) {
|
||||
err := pages.ServerContainersPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
})
|
||||
if err := pages.RegisterHandlers(containerManager); err != nil {
|
||||
glog.Fatalf("Failed to register pages handlers: %s", err)
|
||||
}
|
||||
|
||||
// Start the manager.
|
||||
if err := containerManager.Start(); err != nil {
|
||||
|
@ -82,6 +82,26 @@ func IsDockerContainerName(name string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the Docker ID from the full container name.
|
||||
func ContainerNameToDockerId(name string) string {
|
||||
id := path.Base(name)
|
||||
|
||||
// Turn systemd cgroup name into Docker ID.
|
||||
if useSystemd {
|
||||
const systemdDockerPrefix = "docker-"
|
||||
if strings.HasPrefix(id, systemdDockerPrefix) {
|
||||
id = id[len(systemdDockerPrefix):]
|
||||
}
|
||||
|
||||
const systemdScopeSuffix = ".scope"
|
||||
if strings.HasSuffix(id, systemdScopeSuffix) {
|
||||
id = id[:len(id)-len(systemdScopeSuffix)]
|
||||
}
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// Returns a list of possible full container names for the specified Docker container name.
|
||||
func FullDockerContainerNames(dockerName string) []string {
|
||||
names := make([]string, 0, 2)
|
||||
@ -107,7 +127,7 @@ func (self *dockerFactory) CanHandle(name string) (bool, error) {
|
||||
}
|
||||
|
||||
// Check if the container is known to docker and it is active.
|
||||
id := containerNameToDockerId(name)
|
||||
id := ContainerNameToDockerId(name)
|
||||
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
ctnr, err := self.client.InspectContainer(id)
|
||||
|
@ -85,7 +85,7 @@ func newDockerContainerHandler(
|
||||
if handler.isDockerRoot() {
|
||||
return handler, nil
|
||||
}
|
||||
id := containerNameToDockerId(name)
|
||||
id := ContainerNameToDockerId(name)
|
||||
handler.id = id
|
||||
ctnr, err := client.InspectContainer(id)
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
@ -96,25 +96,6 @@ func newDockerContainerHandler(
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func containerNameToDockerId(name string) string {
|
||||
id := path.Base(name)
|
||||
|
||||
// Turn systemd cgroup name into Docker ID.
|
||||
if useSystemd {
|
||||
const systemdDockerPrefix = "docker-"
|
||||
if strings.HasPrefix(id, systemdDockerPrefix) {
|
||||
id = id[len(systemdDockerPrefix):]
|
||||
}
|
||||
|
||||
const systemdScopeSuffix = ".scope"
|
||||
if strings.HasSuffix(id, systemdScopeSuffix) {
|
||||
id = id[:len(id)-len(systemdScopeSuffix)]
|
||||
}
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
return info.ContainerReference{
|
||||
Name: self.name,
|
||||
|
@ -88,7 +88,6 @@ func (b ByteSize) Unit() string {
|
||||
}
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"containerLink": containerLink,
|
||||
"printMask": printMask,
|
||||
"printCores": printCores,
|
||||
"printShares": printShares,
|
||||
@ -102,52 +101,6 @@ var funcMap = template.FuncMap{
|
||||
"getFsUsagePercent": getFsUsagePercent,
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Consider housekeeping Spec too so we can show changes through time. We probably don't need it ever second though.
|
||||
|
||||
var pageTemplate *template.Template
|
||||
|
||||
type pageData struct {
|
||||
ContainerName string
|
||||
ParentContainers []info.ContainerReference
|
||||
Subcontainers []info.ContainerReference
|
||||
Spec info.ContainerSpec
|
||||
Stats []*info.ContainerStats
|
||||
MachineInfo *info.MachineInfo
|
||||
ResourcesAvailable bool
|
||||
CpuAvailable bool
|
||||
MemoryAvailable bool
|
||||
NetworkAvailable bool
|
||||
FsAvailable bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
pageTemplate = template.New("containersTemplate").Funcs(funcMap)
|
||||
_, err := pageTemplate.Parse(containersHtmlTemplate)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to parse template: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Escape this correctly.
|
||||
func containerLink(container info.ContainerReference, basenameOnly bool, cssClasses string) interface{} {
|
||||
var displayName string
|
||||
containerName := container.Name
|
||||
if len(container.Aliases) > 0 {
|
||||
displayName = container.Aliases[0]
|
||||
} else if basenameOnly {
|
||||
displayName = path.Base(string(container.Name))
|
||||
} else {
|
||||
displayName = string(container.Name)
|
||||
}
|
||||
if container.Name == "root" {
|
||||
containerName = "/"
|
||||
} else if strings.Contains(container.Name, " ") {
|
||||
// If it has a space, it is an a.k.a, so keep the base-name
|
||||
containerName = container.Name[:strings.Index(container.Name, " ")]
|
||||
}
|
||||
return template.HTML(fmt.Sprintf("<a class=\"%s\" href=\"%s%s\">%s</a>", cssClasses, ContainersPage[:len(ContainersPage)-1], containerName, displayName))
|
||||
}
|
||||
|
||||
func printMask(mask string, numCores int) interface{} {
|
||||
masks := make([]string, numCores)
|
||||
activeCores := getActiveCores(mask)
|
||||
@ -266,7 +219,7 @@ func getFsUsagePercent(limit, used uint64) uint64 {
|
||||
return uint64((float64(used) / float64(limit)) * 100)
|
||||
}
|
||||
|
||||
func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
func serveContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
start := time.Now()
|
||||
|
||||
// The container name is the path after the handler
|
||||
@ -278,8 +231,9 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL)
|
||||
}
|
||||
cont, err := m.GetContainerInfo(containerName, &reqParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get container \"%s\" with error: %s", containerName, err)
|
||||
return fmt.Errorf("Failed to get container %q with error: %v", containerName, err)
|
||||
}
|
||||
displayName := getContainerDisplayName(cont.ContainerReference)
|
||||
|
||||
// Get the MachineInfo
|
||||
machineInfo, err := m.GetMachineInfo()
|
||||
@ -288,41 +242,41 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL)
|
||||
}
|
||||
|
||||
// Make a list of the parent containers and their links
|
||||
var parentContainers []info.ContainerReference
|
||||
parentContainers = append(parentContainers, info.ContainerReference{Name: "root"})
|
||||
parentName := ""
|
||||
for _, part := range strings.Split(string(cont.Name), "/") {
|
||||
if part == "" {
|
||||
pathParts := strings.Split(string(cont.Name), "/")
|
||||
parentContainers := make([]link, 0, len(pathParts))
|
||||
parentContainers = append(parentContainers, link{
|
||||
Text: "root",
|
||||
Link: ContainersPage,
|
||||
})
|
||||
for i := 1; i < len(pathParts); i++ {
|
||||
// Skip empty parts.
|
||||
if pathParts[i] == "" {
|
||||
continue
|
||||
}
|
||||
parentName += "/" + part
|
||||
parentContainers = append(parentContainers, info.ContainerReference{Name: parentName})
|
||||
parentContainers = append(parentContainers, link{
|
||||
Text: pathParts[i],
|
||||
Link: path.Join(ContainersPage, path.Join(pathParts[1:i+1]...)),
|
||||
})
|
||||
}
|
||||
|
||||
// Pick the shortest name of the container as the display name.
|
||||
displayName := cont.Name
|
||||
for _, alias := range cont.Aliases {
|
||||
if len(displayName) >= len(alias) {
|
||||
displayName = alias
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the last part of the parent containers with the displayName.
|
||||
if displayName != cont.Name {
|
||||
parentContainers[len(parentContainers)-1] = info.ContainerReference{
|
||||
Name: fmt.Sprintf("%s (%s)", displayName, path.Base(cont.Name)),
|
||||
}
|
||||
// Build the links for the subcontainers.
|
||||
subcontainerLinks := make([]link, 0, len(cont.Subcontainers))
|
||||
for _, sub := range cont.Subcontainers {
|
||||
subcontainerLinks = append(subcontainerLinks, link{
|
||||
Text: getContainerDisplayName(sub),
|
||||
Link: path.Join(ContainersPage, sub.Name),
|
||||
})
|
||||
}
|
||||
|
||||
data := &pageData{
|
||||
ContainerName: displayName,
|
||||
// TODO(vmarmol): Only use strings for this.
|
||||
DisplayName: displayName,
|
||||
ContainerName: cont.Name,
|
||||
ParentContainers: parentContainers,
|
||||
Subcontainers: cont.Subcontainers,
|
||||
Subcontainers: subcontainerLinks,
|
||||
Spec: cont.Spec,
|
||||
Stats: cont.Stats,
|
||||
MachineInfo: machineInfo,
|
||||
ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork,
|
||||
ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork || cont.Spec.HasFilesystem,
|
||||
CpuAvailable: cont.Spec.HasCpu,
|
||||
MemoryAvailable: cont.Spec.HasMemory,
|
||||
NetworkAvailable: cont.Spec.HasNetwork,
|
||||
|
@ -17,7 +17,7 @@ package pages
|
||||
const containersHtmlTemplate = `
|
||||
<html>
|
||||
<head>
|
||||
<title>cAdvisor - Container {{.ContainerName}}</title>
|
||||
<title>cAdvisor - {{.DisplayName}}</title>
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
|
||||
|
||||
@ -39,11 +39,11 @@ const containersHtmlTemplate = `
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h1>{{.ContainerName}}</h1>
|
||||
<h1>{{.DisplayName}}</h1>
|
||||
</div>
|
||||
<ol class="breadcrumb">
|
||||
{{range $parentContainer := .ParentContainers}}
|
||||
<li>{{containerLink $parentContainer true ""}}</li>
|
||||
<li><a href="{{$parentContainer.Link}}">{{$parentContainer.Text}}</a></li>
|
||||
{{end}}
|
||||
</ol>
|
||||
</div>
|
||||
@ -54,7 +54,7 @@ const containersHtmlTemplate = `
|
||||
</div>
|
||||
<div class="list-group">
|
||||
{{range $subcontainer := .Subcontainers}}
|
||||
{{containerLink $subcontainer false "list-group-item"}}
|
||||
<a href="{{$subcontainer.Link}}" class="list-group-item">{{$subcontainer.Text}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
106
pages/docker.go
Normal file
106
pages/docker.go
Normal file
@ -0,0 +1,106 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
"github.com/google/cadvisor/info"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
const DockerPage = "/docker/"
|
||||
|
||||
func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
start := time.Now()
|
||||
|
||||
// The container name is the path after the handler
|
||||
containerName := u.Path[len(DockerPage)-1:]
|
||||
|
||||
var data *pageData
|
||||
if containerName == "/" {
|
||||
// Get the containers.
|
||||
reqParams := info.ContainerInfoRequest{
|
||||
NumStats: 0,
|
||||
}
|
||||
conts, err := m.DockerContainersInfo("/", &reqParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get container %q with error: %v", containerName, err)
|
||||
}
|
||||
subcontainers := make([]link, 0, len(conts))
|
||||
for _, cont := range conts {
|
||||
subcontainers = append(subcontainers, link{
|
||||
Text: getContainerDisplayName(cont.ContainerReference),
|
||||
Link: path.Join("/docker", docker.ContainerNameToDockerId(cont.ContainerReference.Name)),
|
||||
})
|
||||
}
|
||||
|
||||
dockerContainersText := "Docker Containers"
|
||||
data = &pageData{
|
||||
DisplayName: dockerContainersText,
|
||||
ParentContainers: []link{
|
||||
link{
|
||||
Text: dockerContainersText,
|
||||
Link: DockerPage,
|
||||
}},
|
||||
Subcontainers: subcontainers,
|
||||
}
|
||||
} else {
|
||||
// Get the container.
|
||||
reqParams := info.ContainerInfoRequest{
|
||||
NumStats: 60,
|
||||
}
|
||||
conts, err := m.DockerContainersInfo(containerName, &reqParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get container %q with error: %v", containerName, err)
|
||||
}
|
||||
if len(conts) != 1 {
|
||||
return fmt.Errorf("received unexpected container for %q: %v", conts)
|
||||
}
|
||||
cont := conts[0]
|
||||
displayName := getContainerDisplayName(cont.ContainerReference)
|
||||
|
||||
// Make a list of the parent containers and their links
|
||||
var parentContainers []link
|
||||
parentContainers = append(parentContainers, link{
|
||||
Text: "Docker containers",
|
||||
Link: DockerPage,
|
||||
})
|
||||
parentContainers = append(parentContainers, link{
|
||||
Text: displayName,
|
||||
Link: path.Join(DockerPage, docker.ContainerNameToDockerId(cont.Name)),
|
||||
})
|
||||
|
||||
// Get the MachineInfo
|
||||
machineInfo, err := m.GetMachineInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data = &pageData{
|
||||
DisplayName: displayName,
|
||||
ContainerName: cont.Name,
|
||||
ParentContainers: parentContainers,
|
||||
Spec: cont.Spec,
|
||||
Stats: cont.Stats,
|
||||
MachineInfo: machineInfo,
|
||||
ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork,
|
||||
CpuAvailable: cont.Spec.HasCpu,
|
||||
MemoryAvailable: cont.Spec.HasMemory,
|
||||
NetworkAvailable: cont.Spec.HasNetwork,
|
||||
FsAvailable: cont.Spec.HasFilesystem,
|
||||
}
|
||||
}
|
||||
|
||||
err := pageTemplate.Execute(w, data)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to apply template: %s", err)
|
||||
}
|
||||
|
||||
glog.V(1).Infof("Request took %s", time.Since(start))
|
||||
return nil
|
||||
}
|
82
pages/pages.go
Normal file
82
pages/pages.go
Normal file
@ -0,0 +1,82 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/info"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
var pageTemplate *template.Template
|
||||
|
||||
type link struct {
|
||||
// Text to show in the link.
|
||||
Text string
|
||||
|
||||
// Web address to link to.
|
||||
Link string
|
||||
}
|
||||
|
||||
type pageData struct {
|
||||
DisplayName string
|
||||
ContainerName string
|
||||
ParentContainers []link
|
||||
Subcontainers []link
|
||||
Spec info.ContainerSpec
|
||||
Stats []*info.ContainerStats
|
||||
MachineInfo *info.MachineInfo
|
||||
ResourcesAvailable bool
|
||||
CpuAvailable bool
|
||||
MemoryAvailable bool
|
||||
NetworkAvailable bool
|
||||
FsAvailable bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
pageTemplate = template.New("containersTemplate").Funcs(funcMap)
|
||||
_, err := pageTemplate.Parse(containersHtmlTemplate)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to parse template: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Register http handlers for pages.
|
||||
func RegisterHandlers(containerManager manager.Manager) error {
|
||||
// Register the handler for the containers page.
|
||||
http.HandleFunc(ContainersPage, func(w http.ResponseWriter, r *http.Request) {
|
||||
err := serveContainersPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
})
|
||||
|
||||
// Register the handler for the docker page.
|
||||
http.HandleFunc(DockerPage, func(w http.ResponseWriter, r *http.Request) {
|
||||
err := serveDockerPage(containerManager, w, r.URL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getContainerDisplayName(cont info.ContainerReference) string {
|
||||
// Pick the shortest name of the container as the display name.
|
||||
displayName := cont.Name
|
||||
for _, alias := range cont.Aliases {
|
||||
if len(displayName) >= len(alias) {
|
||||
displayName = alias
|
||||
}
|
||||
}
|
||||
|
||||
// Add the full container name to the display name.
|
||||
if displayName != cont.Name {
|
||||
displayName = fmt.Sprintf("%s (%s)", displayName, cont.Name)
|
||||
}
|
||||
|
||||
return displayName
|
||||
}
|
Loading…
Reference in New Issue
Block a user