func progressWrapperRender(op *operation, key string, description string, progressInt int64, speedInt int64) { meta := op.metadata if meta == nil { meta = make(map[string]interface{}) } progress := fmt.Sprintf("%s (%s/s)", shared.GetByteSizeString(progressInt), shared.GetByteSizeString(speedInt)) if description != "" { progress = fmt.Sprintf("%s: %s (%s/s)", description, shared.GetByteSizeString(progressInt), shared.GetByteSizeString(speedInt)) } if meta[key] != progress { meta[key] = progress op.UpdateMetadata(meta) } }
// ImageDownload checks if we have that Image Fingerprint else // downloads the image from a remote server. func (d *Daemon) ImageDownload(op *operation, server string, protocol string, certificate string, secret string, alias string, forContainer bool, autoUpdate bool) (string, error) { var err error var ss *shared.SimpleStreams var ctxMap log.Ctx if protocol == "" { protocol = "lxd" } fp := alias // Expand aliases if protocol == "simplestreams" { imageStreamCacheLock.Lock() entry, _ := imageStreamCache[server] if entry == nil || entry.expiry.Before(time.Now()) { refresh := func() (*imageStreamCacheEntry, error) { // Setup simplestreams client ss, err = shared.SimpleStreamsClient(server, d.proxy) if err != nil { return nil, err } // Get all aliases aliases, err := ss.ListAliases() if err != nil { return nil, err } // Get all fingerprints images, err := ss.ListImages() if err != nil { return nil, err } fingerprints := []string{} for _, image := range images { fingerprints = append(fingerprints, image.Fingerprint) } // Generate cache entry entry = &imageStreamCacheEntry{ss: ss, Aliases: aliases, Fingerprints: fingerprints, expiry: time.Now().Add(time.Hour)} imageStreamCache[server] = entry imageSaveStreamCache() return entry, nil } newEntry, err := refresh() if err == nil { // Cache refreshed entry = newEntry } else if entry != nil { // Failed to fetch entry but existing cache shared.LogWarn("Unable to refresh cache, using stale entry", log.Ctx{"server": server}) entry.expiry = time.Now().Add(time.Hour) } else { // Failed to fetch entry and nothing in cache imageStreamCacheLock.Unlock() return "", err } } else { shared.LogDebug("Using SimpleStreams cache entry", log.Ctx{"server": server, "expiry": entry.expiry}) ss = entry.ss } imageStreamCacheLock.Unlock() // Expand aliases for _, alias := range entry.Aliases { if alias.Name != fp { continue } fp = alias.Target break } // Expand fingerprint for _, fingerprint := range entry.Fingerprints { if !strings.HasPrefix(fingerprint, fp) { continue } if fp == alias { alias = fingerprint } fp = fingerprint break } } else if protocol == "lxd" { target, err := remoteGetImageFingerprint(d, server, certificate, fp) if err == nil && target != "" { fp = target } } if _, _, err := dbImageGet(d.db, fp, false, false); err == nil { shared.LogDebug("Image already exists in the db", log.Ctx{"image": fp}) // already have it return fp, nil } // Now check if we already downloading the image d.imagesDownloadingLock.RLock() if waitChannel, ok := d.imagesDownloading[fp]; ok { // We already download the image d.imagesDownloadingLock.RUnlock() shared.LogDebug( "Already downloading the image, waiting for it to succeed", log.Ctx{"image": fp}) // Wait until the download finishes (channel closes) if _, ok := <-waitChannel; ok { shared.LogWarnf("Value transmitted over image lock semaphore?") } if _, _, err := dbImageGet(d.db, fp, false, true); err != nil { shared.LogError( "Previous download didn't succeed", log.Ctx{"image": fp}) return "", fmt.Errorf("Previous download didn't succeed") } shared.LogDebug( "Previous download succeeded", log.Ctx{"image": fp}) return fp, nil } d.imagesDownloadingLock.RUnlock() if op == nil { ctxMap = log.Ctx{"alias": alias, "server": server} } else { ctxMap = log.Ctx{"trigger": op.url, "image": fp, "operation": op.id, "alias": alias, "server": server} } shared.LogInfo("Downloading image", ctxMap) // Add the download to the queue d.imagesDownloadingLock.Lock() d.imagesDownloading[fp] = make(chan bool) d.imagesDownloadingLock.Unlock() // Unlock once this func ends. defer func() { d.imagesDownloadingLock.Lock() if waitChannel, ok := d.imagesDownloading[fp]; ok { close(waitChannel) delete(d.imagesDownloading, fp) } d.imagesDownloadingLock.Unlock() }() exporturl := server var info shared.ImageInfo info.Fingerprint = fp destDir := shared.VarPath("images") destName := filepath.Join(destDir, fp) if shared.PathExists(destName) { d.Storage.ImageDelete(fp) } progress := func(progressInt int64, speedInt int64) { if op == nil { return } meta := op.metadata if meta == nil { meta = make(map[string]interface{}) } progress := fmt.Sprintf("%d%% (%s/s)", progressInt, shared.GetByteSizeString(speedInt)) if meta["download_progress"] != progress { meta["download_progress"] = progress op.UpdateMetadata(meta) } } if protocol == "lxd" { /* grab the metadata from /1.0/images/%s */ var url string if secret != "" { url = fmt.Sprintf( "%s/%s/images/%s?secret=%s", server, shared.APIVersion, fp, secret) } else { url = fmt.Sprintf("%s/%s/images/%s", server, shared.APIVersion, fp) } resp, err := d.httpGetSync(url, certificate) if err != nil { shared.LogError( "Failed to download image metadata", log.Ctx{"image": fp, "err": err}) return "", err } if err := json.Unmarshal(resp.Metadata, &info); err != nil { return "", err } /* now grab the actual file from /1.0/images/%s/export */ if secret != "" { exporturl = fmt.Sprintf( "%s/%s/images/%s/export?secret=%s", server, shared.APIVersion, fp, secret) } else { exporturl = fmt.Sprintf( "%s/%s/images/%s/export", server, shared.APIVersion, fp) } } else if protocol == "simplestreams" { err := ss.Download(fp, "meta", destName, nil) if err != nil { return "", err } err = ss.Download(fp, "root", destName+".rootfs", progress) if err != nil { return "", err } info, err := ss.GetImageInfo(fp) if err != nil { return "", err } info.Public = false info.AutoUpdate = autoUpdate _, err = imageBuildFromInfo(d, *info) if err != nil { return "", err } if alias != fp { id, _, err := dbImageGet(d.db, fp, false, true) if err != nil { return "", err } err = dbImageSourceInsert(d.db, id, server, protocol, "", alias) if err != nil { return "", err } } shared.LogInfo("Image downloaded", ctxMap) if forContainer { return fp, dbImageLastAccessInit(d.db, fp) } return fp, nil } raw, err := d.httpGetFile(exporturl, certificate) if err != nil { shared.LogError( "Failed to download image", log.Ctx{"image": fp, "err": err}) return "", err } info.Size = raw.ContentLength ctype, ctypeParams, err := mime.ParseMediaType(raw.Header.Get("Content-Type")) if err != nil { ctype = "application/octet-stream" } body := &shared.ProgressReader{ ReadCloser: raw.Body, Tracker: &shared.ProgressTracker{ Length: raw.ContentLength, Handler: progress, }, } if ctype == "multipart/form-data" { // Parse the POST data mr := multipart.NewReader(body, ctypeParams["boundary"]) // Get the metadata tarball part, err := mr.NextPart() if err != nil { shared.LogError( "Invalid multipart image", log.Ctx{"image": fp, "err": err}) return "", err } if part.FormName() != "metadata" { shared.LogError( "Invalid multipart image", log.Ctx{"image": fp, "err": err}) return "", fmt.Errorf("Invalid multipart image") } destName = filepath.Join(destDir, info.Fingerprint) f, err := os.Create(destName) if err != nil { shared.LogError( "Failed to save image", log.Ctx{"image": fp, "err": err}) return "", err } _, err = io.Copy(f, part) f.Close() if err != nil { shared.LogError( "Failed to save image", log.Ctx{"image": fp, "err": err}) return "", err } // Get the rootfs tarball part, err = mr.NextPart() if err != nil { shared.LogError( "Invalid multipart image", log.Ctx{"image": fp, "err": err}) return "", err } if part.FormName() != "rootfs" { shared.LogError( "Invalid multipart image", log.Ctx{"image": fp}) return "", fmt.Errorf("Invalid multipart image") } destName = filepath.Join(destDir, info.Fingerprint+".rootfs") f, err = os.Create(destName) if err != nil { shared.LogError( "Failed to save image", log.Ctx{"image": fp, "err": err}) return "", err } _, err = io.Copy(f, part) f.Close() if err != nil { shared.LogError( "Failed to save image", log.Ctx{"image": fp, "err": err}) return "", err } } else { destName = filepath.Join(destDir, info.Fingerprint) f, err := os.Create(destName) if err != nil { shared.LogError( "Failed to save image", log.Ctx{"image": fp, "err": err}) return "", err } _, err = io.Copy(f, body) f.Close() if err != nil { shared.LogError( "Failed to save image", log.Ctx{"image": fp, "err": err}) return "", err } } if protocol == "direct" { imageMeta, err := getImageMetadata(destName) if err != nil { return "", err } info.Architecture = imageMeta.Architecture info.CreationDate = time.Unix(imageMeta.CreationDate, 0) info.ExpiryDate = time.Unix(imageMeta.ExpiryDate, 0) info.Properties = imageMeta.Properties } // By default, make all downloaded images private info.Public = false if alias != fp && secret == "" { info.AutoUpdate = autoUpdate } _, err = imageBuildFromInfo(d, info) if err != nil { shared.LogError( "Failed to create image", log.Ctx{"image": fp, "err": err}) return "", err } if alias != fp { id, _, err := dbImageGet(d.db, fp, false, true) if err != nil { return "", err } err = dbImageSourceInsert(d.db, id, server, protocol, "", alias) if err != nil { return "", err } } shared.LogInfo("Image downloaded", ctxMap) if forContainer { return fp, dbImageLastAccessInit(d.db, fp) } return fp, nil }
func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error { ct, err := d.ContainerInfo(name) if err != nil { return err } cs, err := d.ContainerState(name) if err != nil { return err } const layout = "2006/01/02 15:04 UTC" fmt.Printf(i18n.G("Name: %s")+"\n", ct.Name) if d.Remote != nil && d.Remote.Addr != "" { fmt.Printf(i18n.G("Remote: %s")+"\n", d.Remote.Addr) } fmt.Printf(i18n.G("Architecture: %s")+"\n", ct.Architecture) if ct.CreationDate.UTC().Unix() != 0 { fmt.Printf(i18n.G("Created: %s")+"\n", ct.CreationDate.UTC().Format(layout)) } fmt.Printf(i18n.G("Status: %s")+"\n", ct.Status) if ct.Ephemeral { fmt.Printf(i18n.G("Type: ephemeral") + "\n") } else { fmt.Printf(i18n.G("Type: persistent") + "\n") } fmt.Printf(i18n.G("Profiles: %s")+"\n", strings.Join(ct.Profiles, ", ")) if cs.Pid != 0 { fmt.Printf(i18n.G("Pid: %d")+"\n", cs.Pid) // IP addresses ipInfo := "" if cs.Network != nil { for netName, net := range cs.Network { vethStr := "" if net.HostName != "" { vethStr = fmt.Sprintf("\t%s", net.HostName) } for _, addr := range net.Addresses { ipInfo += fmt.Sprintf(" %s:\t%s\t%s%s\n", netName, addr.Family, addr.Address, vethStr) } } } if ipInfo != "" { fmt.Println(i18n.G("Ips:")) fmt.Printf(ipInfo) } fmt.Println(i18n.G("Resources:")) // Processes fmt.Printf(" "+i18n.G("Processes: %d")+"\n", cs.Processes) // Disk usage diskInfo := "" if cs.Disk != nil { for entry, disk := range cs.Disk { if disk.Usage != 0 { diskInfo += fmt.Sprintf(" %s: %s\n", entry, shared.GetByteSizeString(disk.Usage)) } } } if diskInfo != "" { fmt.Println(i18n.G(" Disk usage:")) fmt.Printf(diskInfo) } // CPU usage cpuInfo := "" if cs.CPU.Usage != 0 { cpuInfo += fmt.Sprintf(" %s: %v\n", i18n.G("CPU usage (in seconds)"), cs.CPU.Usage/1000000000) } if cpuInfo != "" { fmt.Println(i18n.G(" CPU usage:")) fmt.Printf(cpuInfo) } // Memory usage memoryInfo := "" if cs.Memory.Usage != 0 { memoryInfo += fmt.Sprintf(" %s: %s\n", i18n.G("Memory (current)"), shared.GetByteSizeString(cs.Memory.Usage)) } if cs.Memory.UsagePeak != 0 { memoryInfo += fmt.Sprintf(" %s: %s\n", i18n.G("Memory (peak)"), shared.GetByteSizeString(cs.Memory.UsagePeak)) } if cs.Memory.SwapUsage != 0 { memoryInfo += fmt.Sprintf(" %s: %s\n", i18n.G("Swap (current)"), shared.GetByteSizeString(cs.Memory.SwapUsage)) } if cs.Memory.SwapUsagePeak != 0 { memoryInfo += fmt.Sprintf(" %s: %s\n", i18n.G("Swap (peak)"), shared.GetByteSizeString(cs.Memory.SwapUsagePeak)) } if memoryInfo != "" { fmt.Println(i18n.G(" Memory usage:")) fmt.Printf(memoryInfo) } // Network usage networkInfo := "" if cs.Network != nil { for netName, net := range cs.Network { networkInfo += fmt.Sprintf(" %s:\n", netName) networkInfo += fmt.Sprintf(" %s: %s\n", i18n.G("Bytes received"), shared.GetByteSizeString(net.Counters.BytesReceived)) networkInfo += fmt.Sprintf(" %s: %s\n", i18n.G("Bytes sent"), shared.GetByteSizeString(net.Counters.BytesSent)) networkInfo += fmt.Sprintf(" %s: %d\n", i18n.G("Packets received"), net.Counters.PacketsReceived) networkInfo += fmt.Sprintf(" %s: %d\n", i18n.G("Packets sent"), net.Counters.PacketsSent) } } if networkInfo != "" { fmt.Println(i18n.G(" Network usage:")) fmt.Printf(networkInfo) } } // List snapshots first_snapshot := true snaps, err := d.ListSnapshots(name) if err != nil { return nil } for _, snap := range snaps { if first_snapshot { fmt.Println(i18n.G("Snapshots:")) } fields := strings.Split(snap.Name, shared.SnapshotDelimiter) fmt.Printf(" %s", fields[len(fields)-1]) if snap.CreationDate.UTC().Unix() != 0 { fmt.Printf(" ("+i18n.G("taken at %s")+")", snap.CreationDate.UTC().Format(layout)) } if snap.Stateful { fmt.Printf(" (" + i18n.G("stateful") + ")") } else { fmt.Printf(" (" + i18n.G("stateless") + ")") } fmt.Printf("\n") first_snapshot = false } if showLog { log, err := d.GetLog(name, "lxc.log") if err != nil { return err } stuff, err := ioutil.ReadAll(log) if err != nil { return err } fmt.Printf("\n"+i18n.G("Log:")+"\n\n%s\n", string(stuff)) } return nil }