func isEphemeralColumnData(cinfo shared.ContainerInfo) string { if cinfo.State.Ephemeral { return i18n.G("YES") } else { return i18n.G("NO") } }
func main() { if err := run(); err != nil { // The action we take depends on the error we get. msg := fmt.Sprintf(i18n.G("error: %v"), err) switch t := err.(type) { case *url.Error: switch u := t.Err.(type) { case *net.OpError: if u.Op == "dial" && u.Net == "unix" { switch errno := u.Err.(type) { case syscall.Errno: switch errno { case syscall.ENOENT: msg = i18n.G("LXD socket not found; is LXD running?") case syscall.ECONNREFUSED: msg = i18n.G("Connection refused; is LXD running?") case syscall.EACCES: msg = i18n.G("Permisson denied, are you in the lxd group?") default: msg = fmt.Sprintf("%d %s", uintptr(errno), errno.Error()) } } } } } fmt.Fprintln(os.Stderr, fmt.Sprintf("%s", msg)) os.Exit(1) } }
func execIfAliases(config *lxd.Config, origArgs []string) { newArgs := []string{} expandedAlias := false for _, arg := range origArgs { changed := false for k, v := range config.Aliases { if k == arg { expandedAlias = true changed = true newArgs = append(newArgs, strings.Split(v, " ")...) break } } if !changed { newArgs = append(newArgs, arg) } } if expandedAlias { path, err := exec.LookPath(origArgs[0]) if err != nil { fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), err) os.Exit(5) } ret := syscall.Exec(path, newArgs, syscall.Environ()) fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), ret) os.Exit(5) } }
func (c *actionCmd) run(config *lxd.Config, args []string) error { if len(args) == 0 { return errArgs } for _, nameArg := range args { remote, name := config.ParseRemoteAndContainer(nameArg) d, err := lxd.NewClient(config, remote) if err != nil { return err } resp, err := d.Action(name, c.action, timeout, force) if err != nil { return err } if resp.Type != lxd.Async { return fmt.Errorf(i18n.G("bad result type from action")) } if err := d.WaitForSuccess(resp.Operation); err != nil { return fmt.Errorf("%s\n"+i18n.G("Try `lxc info --show-log %s` for more info"), err, name) } } return nil }
func (c *Client) ProfileDeviceAdd(profile, devname, devtype string, props []string) (*Response, error) { st, err := c.ProfileConfig(profile) if err != nil { return nil, err } newdev := shared.Device{} for _, p := range props { results := strings.SplitN(p, "=", 2) if len(results) != 2 { return nil, fmt.Errorf(i18n.G("no value found in %q"), p) } k := results[0] v := results[1] newdev[k] = v } if st.Devices != nil && st.Devices.ContainsName(devname) { return nil, fmt.Errorf(i18n.G("device already exists")) } newdev["type"] = devtype if st.Devices == nil { st.Devices = shared.Devices{} } st.Devices[devname] = newdev body := shared.Jmap{"config": st.Config, "name": st.Name, "devices": st.Devices} return c.put(fmt.Sprintf("profiles/%s", profile), body, Sync) }
func (c *Client) UserAuthServerCert(name string, acceptCert bool) error { if !c.scertDigestSet { if err := c.Finger(); err != nil { return err } if !c.scertDigestSet { return fmt.Errorf(i18n.G("No certificate on this connection")) } } if c.scert != nil { return nil } _, err := c.scertWire.Verify(x509.VerifyOptions{ DNSName: name, Intermediates: c.scertIntermediates, }) if err == nil { // Server trusted by system certificate return nil } if acceptCert == false { fmt.Printf(i18n.G("Certificate fingerprint: %x")+"\n", c.scertDigest) fmt.Printf(i18n.G("ok (y/n)?") + " ") line, err := shared.ReadStdin() if err != nil { return err } if len(line) < 1 || line[0] != 'y' && line[0] != 'Y' { return fmt.Errorf(i18n.G("Server certificate NACKed by user")) } } // User acked the cert, now add it to our store dnam := c.Config.ConfigPath("servercerts") err = os.MkdirAll(dnam, 0750) if err != nil { return fmt.Errorf(i18n.G("Could not create server cert dir")) } certf := fmt.Sprintf("%s/%s.crt", dnam, c.Name) certOut, err := os.Create(certf) if err != nil { return err } pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: c.scertWire.Raw}) certOut.Close() return err }
func doProfileApply(client *lxd.Client, c string, p string) error { resp, err := client.ApplyProfile(c, p) if err == nil { if p == "" { p = i18n.G("(none)") } fmt.Printf(i18n.G("Profile %s applied to %s")+"\n", p, c) } else { return err } return client.WaitForSuccess(resp.Operation) }
func (c *deleteCmd) run(config *lxd.Config, args []string) error { if len(args) == 0 { return errArgs } for _, nameArg := range args { remote, name := config.ParseRemoteAndContainer(nameArg) d, err := lxd.NewClient(config, remote) if err != nil { return err } if shared.IsSnapshot(name) { return c.doDelete(d, name) } ct, err := d.ContainerStatus(name) if err != nil { return err } if ct.Status.StatusCode != 0 && ct.Status.StatusCode != shared.Stopped { if !c.force { return fmt.Errorf(i18n.G("The container is currently running, stop it first or pass --force.")) } resp, err := d.Action(name, shared.Stop, -1, true) if err != nil { return err } op, err := d.WaitFor(resp.Operation) if err != nil { return err } if op.StatusCode == shared.Failure { return fmt.Errorf(i18n.G("Stopping container failed!")) } if ct.Ephemeral == true { return nil } } if err := c.doDelete(d, name); err != nil { return err } } return nil }
func execIfAliases(config *lxd.Config, origArgs []string) { newArgs, expanded := expandAlias(config, origArgs) if !expanded { return } path, err := exec.LookPath(origArgs[0]) if err != nil { fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), err) os.Exit(5) } ret := syscall.Exec(path, newArgs, syscall.Environ()) fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), ret) os.Exit(5) }
func (c *snapshotCmd) run(config *lxd.Config, args []string) error { if len(args) < 1 { return errArgs } var snapname string if len(args) < 2 { snapname = "" } else { snapname = args[1] } remote, name := config.ParseRemoteAndContainer(args[0]) d, err := lxd.NewClient(config, remote) if err != nil { return err } // we don't allow '/' in snapshot names if shared.IsSnapshot(snapname) { return fmt.Errorf(i18n.G("'/' not allowed in snapshot name")) } resp, err := d.Snapshot(name, snapname, c.stateful) if err != nil { return err } return d.WaitForSuccess(resp.Operation) }
func (c *profileCmd) usage() string { return i18n.G( `Manage configuration profiles. lxc profile list [filters] List available profiles. lxc profile show <profile> Show details of a profile. lxc profile create <profile> Create a profile. lxc profile copy <profile> <remote> Copy the profile to the specified remote. lxc profile set <profile> <key> <value> Set profile configuration. lxc profile delete <profile> Delete a profile. lxc profile edit <profile> Edit profile, either by launching external editor or reading STDIN. Example: lxc profile edit <profile> # launch editor cat profile.yml | lxc profile edit <profile> # read from profile.yml lxc profile apply <container> <profiles> Apply a comma-separated list of profiles to a container, in order. All profiles passed in this call (and only those) will be applied to the specified container. Example: lxc profile apply foo default,bar # Apply default and bar lxc profile apply foo default # Only default is active lxc profile apply '' # no profiles are applied anymore lxc profile apply bar,default # Apply default second now Devices: lxc profile device list <profile> List devices in the given profile. lxc profile device show <profile> Show full device details in the given profile. lxc profile device remove <profile> <name> Remove a device from a profile. lxc profile device add <profile name> <device name> <device type> [key=value]... Add a profile device, such as a disk or a nic, to the containers using the specified profile.`) }
func (c *imageCmd) usage() string { return i18n.G( `Manipulate container images. lxc image import <tarball> [rootfs tarball|URL] [target] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [prop=value] lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public] lxc image delete [remote:]<image> lxc image export [remote:]<image> lxc image info [remote:]<image> lxc image list [remote:] [filter] lxc image show [remote:]<image> lxc image edit [remote:]<image> Edit image, either by launching external editor or reading STDIN. Example: lxc image edit <image> # launch editor cat image.yml | lxc image edit <image> # read from image.yml Lists the images at specified remote, or local images. Filters are not yet supported. lxc image alias create <alias> <target> lxc image alias delete <alias> lxc image alias list [remote:] Create, delete, list image aliases. Example: lxc remote add store2 images.linuxcontainers.org lxc image alias list store2:`) }
func doProfileDelete(client *lxd.Client, p string) error { err := client.ProfileDelete(p) if err == nil { fmt.Printf(i18n.G("Profile %s deleted")+"\n", p) } return err }
func (c *Client) ImageFromContainer(cname string, public bool, aliases []string, properties map[string]string) (string, error) { source := shared.Jmap{"type": "container", "name": cname} if shared.IsSnapshot(cname) { source["type"] = "snapshot" } body := shared.Jmap{"public": public, "source": source, "properties": properties} resp, err := c.post("images", body, Async) if err != nil { return "", err } jmap, err := c.AsyncWaitMeta(resp) if err != nil { return "", err } fingerprint, err := jmap.GetString("fingerprint") if err != nil { return "", err } /* add new aliases */ for _, alias := range aliases { c.DeleteAlias(alias) err = c.PostAlias(alias, alias, fingerprint) if err != nil { fmt.Printf(i18n.G("Error adding alias %s")+"\n", alias) } } return fingerprint, nil }
func deviceAdd(config *lxd.Config, which string, args []string) error { if len(args) < 5 { return errArgs } remote, name := config.ParseRemoteAndContainer(args[2]) client, err := lxd.NewClient(config, remote) if err != nil { return err } devname := args[3] devtype := args[4] var props []string if len(args) > 5 { props = args[5:] } else { props = []string{} } var resp *lxd.Response if which == "profile" { resp, err = client.ProfileDeviceAdd(name, devname, devtype, props) } else { resp, err = client.ContainerDeviceAdd(name, devname, devtype, props) } if err != nil { return err } fmt.Printf(i18n.G("Device %s added to %s")+"\n", devname, name) if which == "profile" { return nil } return client.WaitForSuccess(resp.Operation) }
func (c *Client) baseGet(getUrl string) (*Response, error) { req, err := http.NewRequest("GET", getUrl, nil) if err != nil { return nil, err } req.Header.Set("User-Agent", shared.UserAgent) resp, err := c.Http.Do(req) if err != nil { return nil, err } if c.scert != nil && resp.TLS != nil { if !bytes.Equal(resp.TLS.PeerCertificates[0].Raw, c.scert.Raw) { pUrl, _ := url.Parse(getUrl) return nil, fmt.Errorf(i18n.G("Server certificate for host %s has changed. Add correct certificate or remove certificate in %s"), pUrl.Host, c.Config.ConfigPath("servercerts")) } } if c.scertDigestSet == false && resp.TLS != nil { c.scertWire = resp.TLS.PeerCertificates[0] c.scertIntermediates = x509.NewCertPool() for _, cert := range resp.TLS.PeerCertificates { c.scertIntermediates.AddCert(cert) } c.scertDigest = sha256.Sum256(resp.TLS.PeerCertificates[0].Raw) c.scertDigestSet = true } return HoistResponse(resp, Sync) }
func deviceRm(config *lxd.Config, which string, args []string) error { if len(args) < 4 { return errArgs } remote, name := config.ParseRemoteAndContainer(args[2]) client, err := lxd.NewClient(config, remote) if err != nil { return err } devname := args[3] var resp *lxd.Response if which == "profile" { resp, err = client.ProfileDeviceDelete(name, devname) } else { resp, err = client.ContainerDeviceDelete(name, devname) } if err != nil { return err } fmt.Printf(i18n.G("Device %s removed from %s")+"\n", devname, name) if which == "profile" { return nil } return client.WaitForSuccess(resp.Operation) }
func containerInfo(d *lxd.Client, name string, showLog bool) error { ct, err := d.ContainerStatus(name) if err != nil { return err } fmt.Printf(i18n.G("Name: %s")+"\n", ct.Name) fmt.Printf(i18n.G("Status: %s")+"\n", ct.Status.Status) if ct.Status.Init != 0 { fmt.Printf(i18n.G("Init: %d")+"\n", ct.Status.Init) fmt.Printf(i18n.G("Processcount: %d")+"\n", ct.Status.Processcount) fmt.Printf(i18n.G("Ips:") + "\n") foundone := false for _, ip := range ct.Status.Ips { vethStr := "" if ip.HostVeth != "" { vethStr = fmt.Sprintf("\t%s", ip.HostVeth) } fmt.Printf(" %s:\t%s\t%s%s\n", ip.Interface, ip.Protocol, ip.Address, vethStr) foundone = true } if !foundone { fmt.Println(i18n.G("(none)")) } } // 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:")) } fmt.Printf(" %s\n", snap) 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 }
func (c *infoCmd) usage() string { return i18n.G( `List information on containers. This will support remotes and images as well, but only containers for now. lxc info [<remote>:]container [--show-log]`) }
func (c *Client) PutProfile(name string, profile shared.ProfileConfig) error { if profile.Name != name { return fmt.Errorf(i18n.G("Cannot change profile name")) } body := shared.Jmap{"name": name, "config": profile.Config, "devices": profile.Devices} _, err := c.put(fmt.Sprintf("profiles/%s", name), body, Sync) return err }
func (c *deleteCmd) usage() string { return i18n.G( `Delete containers or container snapshots. lxc delete [remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]...] Destroy containers or snapshots with any attached data (configuration, snapshots, ...).`) }
func (f *configList) Set(value string) error { if value == "" { return fmt.Errorf(i18n.G("Invalid configuration key")) } items := strings.SplitN(value, "=", 2) if len(items) < 2 { return fmt.Errorf(i18n.G("Invalid configuration key")) } if configMap == nil { configMap = map[string]string{} } configMap[items[0]] = items[1] return nil }
func (c *imageCmd) usage() string { return i18n.G( `Manipulate container images. In LXD containers are created from images. Those images were themselves either generated from an existing container or downloaded from an image server. When using remote images, LXD will automatically cache images for you and remove them upon expiration. The image unique identifier is the hash (sha-256) of its representation as a compressed tarball (or for split images, the concatenation of the metadata and rootfs tarballs). Images can be referenced by their full hash, shortest unique partial hash or alias name (if one is set). lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [prop=value] Import an image tarball (or tarballs) into the LXD image store. lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public] Copy an image from one LXD daemon to another over the network. lxc image delete [remote:]<image> Delete an image from the LXD image store. lxc image export [remote:]<image> Export an image from the LXD image store into a distributable tarball. lxc image info [remote:]<image> Print everything LXD knows about a given image. lxc image list [remote:] [filter] List images in the LXD image store. Filters may be of the <key>=<value> form for property based filtering, or part of the image hash or part of the image alias name. lxc image show [remote:]<image> Yaml output of the user modifiable properties of an image. lxc image edit [remote:]<image> Edit image, either by launching external editor or reading STDIN. Example: lxc image edit <image> # launch editor cat image.yml | lxc image edit <image> # read from image.yml lxc image alias create [remote:]<alias> <fingerprint> Create a new alias for an existing image. lxc image alias delete [remote:]<alias> Delete an alias. lxc image alias list [remote:] List the aliases. `) }
func showAliases(aliases []shared.ImageAlias) error { data := [][]string{} for _, alias := range aliases { data = append(data, []string{alias.Description, alias.Name[0:12]}) } table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{ i18n.G("ALIAS"), i18n.G("FINGERPRINT")}) for _, v := range data { table.Append(v) } table.Render() return nil }
func (c *deleteCmd) doDelete(d *lxd.Client, name string) error { if c.interactive { reader := bufio.NewReader(os.Stdin) fmt.Printf(i18n.G("Remove %s (yes/no): "), name) input, _ := reader.ReadString('\n') input = strings.TrimSuffix(input, "\n") if !shared.StringInSlice(strings.ToLower(input), []string{i18n.G("yes")}) { return fmt.Errorf(i18n.G("User aborted delete operation.")) } } resp, err := d.Delete(name) if err != nil { return err } return d.WaitForSuccess(resp.Operation) }
func (c *fileCmd) usage() string { return i18n.G( `Manage files on a container. lxc file pull <source> [<source>...] <target> lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target> lxc file edit <file> <source> in the case of pull, <target> in the case of push and <file> in the case of edit are <container name>/<path>`) }
func showImages(images []shared.ImageInfo, filters []string) error { data := [][]string{} for _, image := range images { if !imageShouldShow(filters, &image) { continue } shortest := shortestAlias(image.Aliases) if len(image.Aliases) > 1 { shortest = fmt.Sprintf(i18n.G("%s (%d more)"), shortest, len(image.Aliases)-1) } fp := image.Fingerprint[0:12] public := i18n.G("no") description := findDescription(image.Properties) // FIXME: InterfaceToBool is there for backward compatibility if shared.InterfaceToBool(image.Public) { public = i18n.G("yes") } const layout = "Jan 2, 2006 at 3:04pm (MST)" uploaded := time.Unix(image.UploadDate, 0).Format(layout) arch, _ := shared.ArchitectureName(image.Architecture) size := fmt.Sprintf("%.2fMB", float64(image.Size)/1024.0/1024.0) data = append(data, []string{shortest, fp, public, description, arch, size, uploaded}) } table := tablewriter.NewWriter(os.Stdout) table.SetColWidth(50) table.SetHeader([]string{ i18n.G("ALIAS"), i18n.G("FINGERPRINT"), i18n.G("PUBLIC"), i18n.G("DESCRIPTION"), i18n.G("ARCH"), i18n.G("SIZE"), i18n.G("UPLOAD DATE")}) sort.Sort(ByName(data)) table.AppendBulk(data) table.Render() return nil }
// summaryLine returns the first line of the help text. Conventionally, this // should be a one-line command summary, potentially followed by a longer // explanation. func summaryLine(usage string) string { usage = strings.TrimSpace(usage) s := bufio.NewScanner(bytes.NewBufferString(usage)) if s.Scan() { if len(s.Text()) > 1 { return s.Text() } } return i18n.G("Missing summary.") }
func (c *fileCmd) usage() string { return i18n.G( `Manage files on a container. lxc file pull <source> [<source>...] <target> lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target> lxc file edit <file> <source> in the case of pull, <target> in the case of push and <file> in the case of edit are <container name>/<path> This operation is only supported on containers that are currently running`) }
func (c *moveCmd) usage() string { return i18n.G( `Move containers within or in between lxd instances. lxc move [remote:]<source container> [remote:]<destination container> Move a container between two hosts, renaming it if destination name differs. lxc move <old name> <new name> Rename a local container. `) }