diff --git a/lxd-bridge/lxd-bridge b/lxd-bridge/lxd-bridge index 5343ae83e381..e02d2c61cf38 100755 --- a/lxd-bridge/lxd-bridge +++ b/lxd-bridge/lxd-bridge @@ -32,6 +32,10 @@ LXD_IPV6_PROXY="true" use_iptables_lock="-w" iptables -w -L -n > /dev/null 2>&1 || use_iptables_lock="" +HAS_IPV6=false +[ -e "/proc/sys/net/ipv6/conf/default/disable_ipv6" ] && \ + [ "$(cat /proc/sys/net/ipv6/conf/default/disable_ipv6)" = "0" ] && HAS_IPV6=true + _netmask2cidr () { # Assumes there's no "255." after a non-255 byte in the mask @@ -47,7 +51,7 @@ ifdown() { } ifup() { - ip addr add fe80::1/64 dev "${1}" + [ "${HAS_IPV6}" = "true" ] && ip addr add fe80::1/64 dev "${1}" if [ -n "${LXD_IPV4_NETMASK}" ] && [ -n "${LXD_IPV4_ADDR}" ]; then MASK=$(_netmask2cidr ${LXD_IPV4_NETMASK}) CIDR_ADDR="${LXD_IPV4_ADDR}/${MASK}" @@ -81,8 +85,10 @@ start() { # set up the lxd network [ ! -d "/sys/class/net/${LXD_BRIDGE}" ] && ip link add dev "${LXD_BRIDGE}" type bridge - echo 0 > "/proc/sys/net/ipv6/conf/${LXD_BRIDGE}/autoconf" || true - echo 0 > "/proc/sys/net/ipv6/conf/${LXD_BRIDGE}/accept_dad" || true + if [ "${HAS_IPV6}" = "true" ]; then + echo 0 > "/proc/sys/net/ipv6/conf/${LXD_BRIDGE}/autoconf" || true + echo 0 > "/proc/sys/net/ipv6/conf/${LXD_BRIDGE}/accept_dad" || true + fi # if we are run from systemd on a system with selinux enabled, # the mkdir will create /run/lxd as init_var_run_t which dnsmasq @@ -113,7 +119,7 @@ start() { fi LXD_IPV6_ARG="" - if [ -n "${LXD_IPV6_ADDR}" ] && [ -n "${LXD_IPV6_MASK}" ] && [ -n "${LXD_IPV6_NETWORK}" ]; then + if [ "${HAS_IPV6}" = "true" ] && [ -n "${LXD_IPV6_ADDR}" ] && [ -n "${LXD_IPV6_MASK}" ] && [ -n "${LXD_IPV6_NETWORK}" ]; then # IPv6 sysctls don't respect the "all" path... for interface in /proc/sys/net/ipv6/conf/*; do echo 2 > "${interface}/accept_ra" @@ -161,7 +167,7 @@ start() { dnsmasq ${LXD_CONFILE_ARG} ${LXD_DOMAIN_ARG} -u "${DNSMASQ_USER}" --strict-order --bind-interfaces --pid-file="${varrun}/dnsmasq.pid" --dhcp-no-override --except-interface=lo --interface="${LXD_BRIDGE}" --dhcp-leasefile="${varlib}/dnsmasq.${LXD_BRIDGE}.leases" --dhcp-authoritative ${LXD_IPV4_ARG} ${LXD_IPV6_ARG} || cleanup fi - if [ "${LXD_IPV6_PROXY}" = "true" ]; then + if [ "${HAS_IPV6}" = "true" ] && [ "${LXD_IPV6_PROXY}" = "true" ]; then PATH="${PATH}:$(dirname "${0}")" lxd-bridge-proxy --addr="[fe80::1%${LXD_BRIDGE}]:3128" & PID=$! echo "${PID}" > "${varrun}/proxy.pid" @@ -188,7 +194,7 @@ stop() { iptables ${use_iptables_lock} -t nat -D POSTROUTING -s ${LXD_IPV4_NETWORK} ! -d ${LXD_IPV4_NETWORK} -j MASQUERADE fi - if [ -n "${LXD_IPV6_NETWORK}" ] && [ "${LXD_IPV6_NAT}" = "true" ]; then + if [ "${HAS_IPV6}" = "true" ] && [ -n "${LXD_IPV6_NETWORK}" ] && [ "${LXD_IPV6_NAT}" = "true" ]; then ip6tables ${use_iptables_lock} -t nat -D POSTROUTING -s ${LXD_IPV6_NETWORK} ! -d ${LXD_IPV6_NETWORK} -j MASQUERADE fi diff --git a/lxd/containers_post.go b/lxd/containers_post.go index 11a01d5a564d..8668dd1c03b5 100644 --- a/lxd/containers_post.go +++ b/lxd/containers_post.go @@ -20,11 +20,12 @@ type containerImageSource struct { Certificate string `json:"certificate"` /* for "image" type */ - Alias string `json:"alias"` - Fingerprint string `json:"fingerprint"` - Server string `json:"server"` - Secret string `json:"secret"` - Protocol string `json:"protocol"` + Alias string `json:"alias"` + Fingerprint string `json:"fingerprint"` + Properties map[string]string `json:"properties"` + Server string `json:"server"` + Secret string `json:"secret"` + Protocol string `json:"protocol"` /* * for "migration" and "copy" types, as an optimization users can @@ -73,8 +74,50 @@ func createFromImage(d *Daemon, req *containerPostReq) Response { } } else if req.Source.Fingerprint != "" { hash = req.Source.Fingerprint + } else if req.Source.Properties != nil { + if req.Source.Server != "" { + return BadRequest(fmt.Errorf("Property match is only supported for local images")) + } + + hashes, err := dbImagesGet(d.db, false) + if err != nil { + return InternalError(err) + } + + var image *shared.ImageInfo + + for _, hash := range hashes { + _, img, err := dbImageGet(d.db, hash, false, true) + if err != nil { + continue + } + + if image != nil && img.CreationDate.Before(image.CreationDate) { + continue + } + + match := true + for key, value := range req.Source.Properties { + if img.Properties[key] != value { + match = false + break + } + } + + if !match { + continue + } + + image = img + } + + if image == nil { + return BadRequest(fmt.Errorf("No matching image could be found")) + } + + hash = image.Fingerprint } else { - return BadRequest(fmt.Errorf("must specify one of alias or fingerprint for init from image")) + return BadRequest(fmt.Errorf("Must specify one of alias, fingerprint or properties for init from image")) } run := func(op *operation) error { diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go index a9ecafd09ac5..438d2a7c553e 100644 --- a/lxd/storage_zfs.go +++ b/lxd/storage_zfs.go @@ -178,7 +178,7 @@ func (s *storageZfs) ContainerCanRestore(container container, sourceContainer co } if snapshots[len(snapshots)-1] != snapName { - return fmt.Errorf("ZFS only supports restoring state to the latest snapshot.") + return fmt.Errorf("ZFS can only restore from the latest snapshot. Delete newer snapshots or copy the snapshot into a new container instead.") } return nil