refine strategy to spot ip address, keep on calling guest_ipv4
[build.git] / lbuild-initvm.sh
index 34c9115..58030cc 100755 (executable)
@@ -10,18 +10,30 @@ COMMAND=$(basename $0)
 DIRNAME=$(dirname $0)
 BUILD_DIR=$(pwd)
 
 DIRNAME=$(dirname $0)
 BUILD_DIR=$(pwd)
 
-# pkgs parsing utilities
+# pkgs parsing utilities + lbuild-bridge.sh
 export PATH=$(dirname $0):$PATH
 
 export PATH=$(dirname $0):$PATH
 
-# old guests have e.g. mount in /bin but this is no longer part of 
+# old guests have e.g. mount in /bin but this is no longer part of
 # the standard PATH in recent hosts after usrmove, so let's keep it simple
 export PATH=$PATH:/bin:/sbin
 
 . build.common
 
 # the standard PATH in recent hosts after usrmove, so let's keep it simple
 export PATH=$PATH:/bin:/sbin
 
 . build.common
 
-DEFAULT_FCDISTRO=f20
+# xxx fixme : we pass $lxc around in functions,
+# but in fact then we use lxc_root which is a global..
+# it works, but this really is poor practice
+# we should have an lxc_root function instead
+function lxcroot () {
+    local lxc=$1; shift
+    echo /vservers/$lxc
+}
+
+# XXX fixme : when creating a 32bits VM we need to call linux32 as appropriate...s
+
+DEFAULT_FCDISTRO=f39
 DEFAULT_PLDISTRO=lxc
 DEFAULT_PERSONALITY=linux64
 DEFAULT_PLDISTRO=lxc
 DEFAULT_PERSONALITY=linux64
+DEFAULT_MEMORY=3072
 
 ##########
 # constant
 
 ##########
 # constant
@@ -31,191 +43,105 @@ PUBLIC_BRIDGE=br0
 VIF_GUEST=eth0
 
 ##########
 VIF_GUEST=eth0
 
 ##########
-FEDORA_PREINSTALLED="yum initscripts passwd rsyslog vim-minimal dhclient chkconfig rootfiles policycoreutils openssh-server openssh-clients"
+FEDORA_MIRROR="http://mirror.onelab.eu/"
+FEDORA_MIRROR_KEYS="http://mirror.onelab.eu/keys/"
+FEDORA_PREINSTALLED="dnf dnf-yum passwd rsyslog vim-minimal dhclient chkconfig rootfiles policycoreutils openssh-server openssh-clients"
 DEBIAN_PREINSTALLED="openssh-server openssh-client"
 
 DEBIAN_PREINSTALLED="openssh-server openssh-client"
 
-##############################
-## stolen from tests/system/template-qemu/qemu-bridge-init
-# use /proc/net/dev instead of a hard-wired list
-function gather_interfaces () {
-    python <<EOF
-for line in file("/proc/net/dev"):
-    if ':' not in line: continue
-    ifname=line.replace(" ","").split(":")[0]
-    if ifname.find("lo")==0: continue
-    if ifname.find("br")==0: continue
-    if ifname.find("virbr")==0: continue
-    if ifname.find("veth")==0: continue
-    if ifname.find("tap")==0: continue
-    print ifname
-EOF
-}
-
-function discover_interface () {
-    for ifname in $(gather_interfaces); do
-       ip link show $ifname | grep -qi 'state UP' && { echo $ifname; return; }
-    done
-    # still not found ? that's bad
-    echo unknown
-}
-
-########## networking -- ctd
+########## networking utilities
 function gethostbyname () {
 function gethostbyname () {
-    hostname=$1
-    python -c "import socket; print socket.gethostbyname('"$hostname"')" 2> /dev/null
+    local hostname=$1
+    python3 -c "import socket; print(socket.gethostbyname('"$hostname"'))" 2> /dev/null
 }
 
 # e.g. 21 -> 255.255.248.0
 function masklen_to_netmask () {
 }
 
 # e.g. 21 -> 255.255.248.0
 function masklen_to_netmask () {
-    masklen=$1; shift
-    python <<EOF
+    local masklen=$1; shift
+    python3 <<EOF
 import sys
 import sys
-masklen=$masklen
-if not (masklen>=1 and masklen<=32): 
-  print "Wrong masklen",masklen
+masklen = $masklen
+if not (1 <= masklen <= 32):
+  print("Wrong masklen", masklen)
   exit(1)
   exit(1)
-result=[]
+result = []
 for i in range(4):
 for i in range(4):
-    if masklen>=8:
+    if masklen >= 8:
        result.append(8)
        result.append(8)
-       masklen-=8
+       masklen -= 8
     else:
        result.append(masklen)
     else:
        result.append(masklen)
-       masklen=0
-print ".".join([ str(256-2**(8-i)) for i in result ])
-  
+       masklen = 0
+print(".".join([ str(256-2**(8-i)) for i in result ]))
 EOF
 }
 
 EOF
 }
 
-#################### bridge initialization
-function create_bridge_if_needed() {
-   
-    # turn on verbosity
-    set -x
-
-    # already created ? - we're done
-    ip addr show $PUBLIC_BRIDGE >& /dev/null && {
-       echo "Bridge already set up - skipping create_bridge_if_needed"
-       return 0
-    }
-
-    # find out the physical interface to bridge onto
-    if_lan=$(discover_interface)
-
-    ip addr show $if_lan &>/dev/null || {
-        echo "Cannot use interface $if_lan - exiting"
-        exit 1
-    }
-
-    #################### bride initialization
-    check_yum_installed bridge-utils
-
-    echo "========== $COMMAND: entering create_bridge - beg"
-    hostname
-    uname -a
-    ip addr show
-    ip route
-    echo "========== $COMMAND: entering create_bridge - end"
-
-    # disable netfilter calls for bridge interface (they cause panick on 2.6.35 anyway)
-    #
-    # another option would be to accept the all forward packages for
-    # bridged interface like: -A FORWARD -m physdev --physdev-is-bridged -j ACCEPT
-    sysctl net.bridge.bridge-nf-call-iptables=0
-    sysctl net.bridge.bridge-nf-call-ip6tables=0
-    sysctl net.bridge.bridge-nf-call-arptables=0
-
-    
-    #Getting host IP/masklen
-    address=$(ip addr show $if_lan | grep -v inet6 | grep inet | head --lines=1 | awk '{print $2;}')
-    [ -z "$address" ] && { echo "ERROR: Could not determine IP address for $if_lan" ; exit 1 ; }
-
-    broadcast=$(ip addr show $if_lan | grep -v inet6 | grep inet | head --lines=1 | awk '{print $4;}')
-    [ -z "$broadcast" ] && echo "WARNING: Could not determine broadcast address for $if_lan"
-
-    gateway=$(ip route show | grep default | awk '{print $3;}')
-    [ -z "$gateway" ] && echo "WARNING: Could not determine gateway IP"
-
-
-    # creating the bridge
-    echo "Creating bridge PUBLIC_BRIDGE=$PUBLIC_BRIDGE"
-    brctl addbr $PUBLIC_BRIDGE
-    brctl addif $PUBLIC_BRIDGE $if_lan
-    echo "Activating promiscuous mode if_lan=$if_lan"
-    ip link set $if_lan up promisc on
-    sleep 2
-    # rely on dhcp to re assign IP.. 
-    echo "Starting dhclient on $PUBLIC_BRIDGE"
-    dhclient $PUBLIC_BRIDGE
-    sleep 1
-
-    #Reconfigure the routing table
-    echo "Configuring gateway=$gateway"
-    ip route add default via $gateway dev $PUBLIC_BRIDGE
-    ip route del default via $gateway dev $if_lan
-    # at this point we have an extra route like e.g.
-    ## ip route show
-    #default via 138.96.112.250 dev br0
-    #138.96.112.0/21 dev em1  proto kernel  scope link  src 138.96.112.57
-    #138.96.112.0/21 dev br0  proto kernel  scope link  src 138.96.112.57
-    #192.168.122.0/24 dev virbr0  proto kernel  scope link  src 192.168.122.1
-    route_dest=$(ip route show | grep -v default | grep "dev $PUBLIC_BRIDGE" | awk '{print $1;}')
-    ip route del $route_dest dev $if_lan
-
-    echo "========== $COMMAND: exiting create_bridge - beg"
-    ip addr show
-    ip route show
-    echo "========== $COMMAND: exiting create_bridge - end"
-
-    # for safety
-    sleep 3
-    return 0
-
-}
-
 ##############################
 ##############################
-# return yum or debootstrap
+# return dnf or debootstrap
 function package_method () {
 function package_method () {
-    fcdistro=$1; shift
+    local fcdistro=$1; shift
+    case $fcdistro in
+        f[0-9]*|centos[0-9]*|sl[0-9]*)
+            echo dnf ;;
+        wheezy|jessie|trusty|xenial|bionic|focal|jammy)
+            echo debootstrap ;;
+        *)
+            echo "Unknown package_method for distro $fcdistro" ;;
+    esac
+}
+
+### return
+# ifcfg          for fedora up to 36
+# networkmanager for fedora starting with f37 (probably works with older as well...)
+# interfaces     for older debian/uuntu
+# systemd        for more recent debian/ubuntu
+function network_config_method () {
+    local fcdistro=$1; shift
     case $fcdistro in
     case $fcdistro in
-       f[0-9]*|centos[0-9]*|sl[0-9]*) echo yum ;;
-       squeeze|wheezy|jessie|oneiric|precise|quantal|raring|saucy) echo debootstrap ;;
-       *) echo Unknown distro $fcdistro ;;
-    esac 
+        # have not used centos or scientific linux for a very long time
+        #f[0-9]*|centos[0-9]*|sl[0-9]*)
+        f2*|f3[0-6])
+            echo ifcfg ;;
+        f3[7-9]|f[4-9]*)
+            echo networkmanager ;;
+        wheezy|jessie|trusty|xenial|bionic)
+            echo interfaces ;;
+        focal|jammy)
+            echo systemd ;;
+        *)
+            echo "Unknown network_config_method for distro $fcdistro" ;;
+    esac
 }
 
 # return arch from debian distro and personality
 function canonical_arch () {
 }
 
 # return arch from debian distro and personality
 function canonical_arch () {
-    personality=$1; shift
-    fcdistro=$1; shift
+    local personality=$1; shift
+    local fcdistro=$1; shift
     case $(package_method $fcdistro) in
     case $(package_method $fcdistro) in
-       yum)
-           case $personality in *32) echo i386 ;; *64) echo x86_64 ;; *) echo Unknown-arch-1 ;; esac ;;
-       debootstrap)
-           case $personality in *32) echo i386 ;; *64) echo amd64 ;; *) echo Unknown-arch-2 ;; esac ;;
-       *)
-           echo Unknown-arch-3 ;;
+        dnf)
+            case $personality in
+                *32) echo i386 ;;
+                *64) echo x86_64 ;;
+                *) echo Unknown-arch-1 ;;
+            esac ;;
+        debootstrap)
+            case $personality in
+                *32) echo i386 ;;
+                *64) echo amd64 ;;
+                *) echo Unknown-arch-2 ;;
+            esac ;;
+        *)
+            echo Unknown-arch-3 ;;
     esac
 }
 
 # the new test framework creates /timestamp in /vservers/<name> *before* populating it
     esac
 }
 
 # the new test framework creates /timestamp in /vservers/<name> *before* populating it
-function almost_empty () { 
-    dir="$1"; shift ; 
+function almost_empty () {
+    local dir="$1"; shift ;
     # non existing is fine
     # non existing is fine
-    [ ! -d $dir ] && return 0; 
+    [ ! -d $dir ] && return 0;
     # need to have at most one file
     # need to have at most one file
-    count=$(cd $dir; ls | wc -l); [ $count -le 1 ]; 
-}
-
-##############################
-function check_yum_installed () {
-    package=$1; shift
-    rpm -q $package >& /dev/null || yum -y install $package
-}
-
-function check_yumgroup_installed () {
-    group="$1"; shift
-    yum grouplist "$group" | grep -q Installed || { yum -y groupinstall "$group" ; }
+    local count=$(cd $dir; ls | wc -l)
+    [ $count -le 1 ]
 }
 
 ##############################
 }
 
 ##############################
@@ -223,36 +149,42 @@ function fedora_install() {
     set -x
     set -e
 
     set -x
     set -e
 
-    cache=/var/cache/lxc/fedora/$arch/$release
-    
-    mkdir -p /var/lock/subsys/
+    local lxc=$1; shift
+    local lxc_root=$(lxcroot $lxc)
+
+    local cache=/var/cache/lxc/fedora/$arch/${fedora_release}
+    mkdir -p $cache
+
     (
     (
-        flock -n -x 200 || { echo "Cache repository is busy." ; return 1 ; }
+        flock --exclusive --timeout 60 200 || { echo "Cache repository is busy." ; return 1 ; }
 
         if [ ! -e "$cache/rootfs" ]; then
             echo "Getting cache download in $cache/rootfs ... "
 
         if [ ! -e "$cache/rootfs" ]; then
             echo "Getting cache download in $cache/rootfs ... "
-            fedora_download || { echo "Failed to download 'fedora base'"; return 1; }
+            fedora_download $cache || { echo "Failed to download 'fedora base'"; return 1; }
         else
             echo "Updating cache $cache/rootfs ..."
         else
             echo "Updating cache $cache/rootfs ..."
-           if ! yum --installroot $cache/rootfs -y --nogpgcheck update ; then
+            if ! dnf --installroot $cache/rootfs --releasever=${fedora_release} -y --nogpgcheck update ; then
                 echo "Failed to update 'fedora base', continuing with last known good cache"
             else
                 echo "Update finished"
             fi
         fi
 
                 echo "Failed to update 'fedora base', continuing with last known good cache"
             else
                 echo "Update finished"
             fi
         fi
 
-        echo "Copy $cache/rootfs to $lxc_root ... "
-       rsync -a $cache/rootfs/ $lxc_root/
-       
+        echo "Filling $lxc_root from $cache/rootfs ... "
+        rsync -a $cache/rootfs/ $lxc_root/
+
         return 0
 
         return 0
 
-        ) 200>/var/lock/subsys/lxc
+        ) 200> $cache/lock
 
     return $?
 }
 
 function fedora_download() {
     set -x
 
     return $?
 }
 
 function fedora_download() {
     set -x
+
+    local cache=$1; shift
+
     # check the mini fedora was not already downloaded
     INSTALL_ROOT=$cache/partial
     echo $INSTALL_ROOT
     # check the mini fedora was not already downloaded
     INSTALL_ROOT=$cache/partial
     echo $INSTALL_ROOT
@@ -262,54 +194,64 @@ function fedora_download() {
 
     mkdir -p $INSTALL_ROOT || { echo "Failed to create '$INSTALL_ROOT' directory" ; return 1; }
 
 
     mkdir -p $INSTALL_ROOT || { echo "Failed to create '$INSTALL_ROOT' directory" ; return 1; }
 
-    mkdir -p $INSTALL_ROOT/etc/yum.repos.d   
+    mkdir -p $INSTALL_ROOT/etc/yum.repos.d
     mkdir -p $INSTALL_ROOT/dev
     mknod -m 0444 $INSTALL_ROOT/dev/random c 1 8
     mknod -m 0444 $INSTALL_ROOT/dev/urandom c 1 9
 
     # copy yum config and repo files
     cp /etc/yum.conf $INSTALL_ROOT/etc/
     mkdir -p $INSTALL_ROOT/dev
     mknod -m 0444 $INSTALL_ROOT/dev/random c 1 8
     mknod -m 0444 $INSTALL_ROOT/dev/urandom c 1 9
 
     # copy yum config and repo files
     cp /etc/yum.conf $INSTALL_ROOT/etc/
-    cp /etc/yum.repos.d/fedora* $INSTALL_ROOT/etc/yum.repos.d/
-
-    # append fedora repo files with desired $release and $basearch
-    for f in $INSTALL_ROOT/etc/yum.repos.d/* ; do
-      sed -i "s/\$basearch/$arch/g; s/\$releasever/$release/g;" $f
-    done 
-
-    MIRROR_URL=http://mirror.onelab.eu/fedora/releases/$release/Everything/$arch/os
-    RELEASE_URL1="$MIRROR_URL/Packages/fedora-release-$release-1.noarch.rpm"
-    # with fedora18 the rpms are scattered by first name
-    RELEASE_URL2="$MIRROR_URL/Packages/f/fedora-release-$release-1.noarch.rpm"
-    RELEASE_TARGET=$INSTALL_ROOT/fedora-release-$release.noarch.rpm
-    found=""
-    for attempt in $RELEASE_URL1 $RELEASE_URL2; do
-       if curl -f $attempt -o $RELEASE_TARGET ; then
-           echo "Retrieved $attempt"
-           found=true
-           break
-       else
-           echo "Failed attempt $attempt"
-       fi
-    done
-    [ -n "$found" ] || { echo "Could not retrieve fedora-release rpm - exiting" ; exit 1; }
-    
+    cp /etc/yum.repos.d/fedora{,-updates}.repo $INSTALL_ROOT/etc/yum.repos.d/
+
+    # append fedora repo files with hardwired releasever and basearch
+    if [ -z "$USE_UPSTREAM_REPOS" ]; then
+        for f in $INSTALL_ROOT/etc/yum.repos.d/* ; do
+            sed -i "s/\$basearch/$arch/g; s/\$releasever/${fedora_release}/g;" $f
+        done
+    fi
+
+# looks like all this business about fetching fedora-release is not needed
+# it does
+#    MIRROR_URL=$FEDORA_MIRROR/fedora/releases/${fedora_release}/Everything/$arch/os
+#    # since fedora18 the rpms are scattered by first name
+#    # first try the second version of fedora-release first
+#    RELEASE_URLS=""
+#    local subindex
+#    for subindex in 3 2 1; do
+#        RELEASE_URLS="$RELEASE_URLS $MIRROR_URL/Packages/f/fedora-release-${fedora_release}-${subindex}.noarch.rpm"
+#    done
+#
+#    RELEASE_TARGET=$INSTALL_ROOT/fedora-release-${fedora_release}.noarch.rpm
+#    local found=""
+#    local attempt
+#    for attempt in $RELEASE_URLS; do
+#        if curl --silent --fail $attempt -o $RELEASE_TARGET; then
+#            echo "Successfully Retrieved $attempt"
+#            found=true
+#            break
+#        else
+#            echo "Failed (not to worry about) with attempt $attempt"
+#        fi
+#    done
+#    [ -n "$found" ] || { echo "Could not retrieve fedora-release rpm - exiting" ; exit 1; }
+
     mkdir -p $INSTALL_ROOT/var/lib/rpm
     rpm --root $INSTALL_ROOT  --initdb
     # when installing f12 this apparently is already present, so ignore result
     mkdir -p $INSTALL_ROOT/var/lib/rpm
     rpm --root $INSTALL_ROOT  --initdb
     # when installing f12 this apparently is already present, so ignore result
-    rpm --root $INSTALL_ROOT -ivh $INSTALL_ROOT/fedora-release-$release.noarch.rpm || :
+#    rpm --root $INSTALL_ROOT -ivh $INSTALL_ROOT/fedora-release-${fedora_release}.noarch.rpm || :
     # however f12 root images won't get created on a f18 host
     # (the issue here is the same as the one we ran into when dealing with a vs-box)
     # in a nutshell, in f12 the glibc-common and filesystem rpms have an apparent conflict
     # however f12 root images won't get created on a f18 host
     # (the issue here is the same as the one we ran into when dealing with a vs-box)
     # in a nutshell, in f12 the glibc-common and filesystem rpms have an apparent conflict
-    # >>> file /usr/lib/locale from install of glibc-common-2.11.2-3.x86_64 conflicts 
+    # >>> file /usr/lib/locale from install of glibc-common-2.11.2-3.x86_64 conflicts
     #          with file from package filesystem-2.4.30-2.fc12.x86_64
     #          with file from package filesystem-2.4.30-2.fc12.x86_64
-    # in fact this was - of course - allowed by f12's rpm but later on a fix was made 
+    # in fact this was - of course - allowed by f12's rpm but later on a fix was made
     #   http://rpm.org/gitweb?p=rpm.git;a=commitdiff;h=cf1095648194104a81a58abead05974a5bfa3b9a
     # So ideally if we want to be able to build f12 images from f18 we need an rpm that has
     # this patch undone, like we have in place on our f14 boxes (our f14 boxes need a f18-like rpm)
 
     #   http://rpm.org/gitweb?p=rpm.git;a=commitdiff;h=cf1095648194104a81a58abead05974a5bfa3b9a
     # So ideally if we want to be able to build f12 images from f18 we need an rpm that has
     # this patch undone, like we have in place on our f14 boxes (our f14 boxes need a f18-like rpm)
 
-    YUM="yum --installroot=$INSTALL_ROOT --nogpgcheck -y"
-    echo "$YUM install $FEDORA_PREINSTALLED"
-    $YUM install $FEDORA_PREINSTALLED || { echo "Failed to download rootfs, aborting." ; return 1; }
+    DNF="dnf --installroot=$INSTALL_ROOT --nogpgcheck -y --releasever=${fedora_release}"
+    echo "$DNF install $FEDORA_PREINSTALLED"
+    $DNF install $FEDORA_PREINSTALLED || { echo "Failed to download rootfs, aborting." ; return 1; }
 
     mv "$INSTALL_ROOT" "$cache/rootfs"
     echo "Download complete."
 
     mv "$INSTALL_ROOT" "$cache/rootfs"
     echo "Download complete."
@@ -323,30 +265,23 @@ function fedora_configure() {
     set -x
     set -e
 
     set -x
     set -e
 
+    local lxc=$1; shift
+    local fcdistro=$1; shift
+    local lxc_root=$(lxcroot $lxc)
+
     # disable selinux in fedora
     mkdir -p $lxc_root/selinux
     echo 0 > $lxc_root/selinux/enforce
 
     # disable selinux in fedora
     mkdir -p $lxc_root/selinux
     echo 0 > $lxc_root/selinux/enforce
 
-    # set the hostname
-    case "$fcdistro" in 
-       f18|f2?)
-           cat <<EOF > ${lxc_root}/etc/hostname
-$GUEST_HOSTNAME
-EOF
-           echo ;;
-       *)
-            cat <<EOF > ${lxc_root}/etc/sysconfig/network
+    # enable networking and set hostname
+    cat <<EOF > ${lxc_root}/etc/sysconfig/network
 NETWORKING=yes
 NETWORKING=yes
-HOSTNAME=$GUEST_HOSTNAME
 EOF
 EOF
-            # set minimal hosts
-           cat <<EOF > $lxc_root/etc/hosts
-127.0.0.1 localhost $GUEST_HOSTNAME
+    cat <<EOF > ${lxc_root}/etc/hostname
+$GUEST_HOSTNAME
 EOF
 EOF
-           echo ;;
-    esac
 
 
-    dev_path="${lxc_root}/dev"
+    local dev_path="${lxc_root}/dev"
     rm -rf $dev_path
     mkdir -p $dev_path
     mknod -m 666 ${dev_path}/null c 1 3
     rm -rf $dev_path
     mkdir -p $dev_path
     mknod -m 666 ${dev_path}/null c 1 3
@@ -366,159 +301,178 @@ EOF
     mknod -m 600 ${dev_path}/initctl p
     mknod -m 666 ${dev_path}/ptmx c 5 2
 
     mknod -m 600 ${dev_path}/initctl p
     mknod -m 666 ${dev_path}/ptmx c 5 2
 
-    if [ "$(echo $fcdistro | cut -d"f" -f2)" -le "14" ]; then
-       fedora_configure_init
-    else
-       fedora_configure_systemd
-    fi
+    fedora_configure_systemd $lxc
 
 
-    guest_ifcfg=${lxc_root}/etc/sysconfig/network-scripts/ifcfg-$VIF_GUEST
-    ( [ -n "$BUILD_MODE" ] && write_guest_ifcfg_build || write_guest_ifcfg_test ) > $guest_ifcfg
+    fedora_configure_network $lxc
 
 
-    fedora_configure_yum $lxc $fcdistro $pldistro
+    [ -z "$IMAGE" ] && fedora_configure_yum $lxc $fcdistro $pldistro
 
     return 0
 }
 
 
     return 0
 }
 
-function fedora_configure_init() {
-    set -e
-    set -x
-    sed -i 's|.sbin.start_udev||' ${lxc_root}/etc/rc.sysinit
-    sed -i 's|.sbin.start_udev||' ${lxc_root}/etc/rc.d/rc.sysinit
-    # don't mount devpts, for pete's sake
-    sed -i 's/^.*dev.pts.*$/#\0/' ${lxc_root}/etc/rc.sysinit
-    sed -i 's/^.*dev.pts.*$/#\0/' ${lxc_root}/etc/rc.d/rc.sysinit
-    chroot ${lxc_root} chkconfig udev-post off
-    chroot ${lxc_root} chkconfig network on
+function fedora_configure_network() {
+    local lxc="$1"
+    case $(network_config_method $fcdistro) in
+        ifcfg)
+            # probably no longer useful
+            local guest_ifcfg=${lxc_root}/etc/sysconfig/network-scripts/ifcfg-$VIF_GUEST
+            mkdir -p $(dirname ${guest_ifcfg})
+            if [ -n "$NAT_MODE" ]; then
+                write_guest_ifcfg_natip
+            else
+                write_guest_ifcfg_publicip
+            fi > $guest_ifcfg
+            ;;
+        networkmanager)
+            local guest_keyfile=${lxc_root}/etc/NetworkManager/system-connections/'Wired connection 1.nmconnection'
+            mkdir -p $(dirname "${guest_keyfile}")
+            if [ -n "$NAT_MODE" ]; then
+                write_guest_networkmanager_natip
+            else
+                write_guest_networkmanager_publicip
+            fi > "$guest_keyfile"
+            chmod 600 "$guest_keyfile"
+            ;;
+    esac
 }
 
 # this code of course is for guests that do run on systemd
 function fedora_configure_systemd() {
     set -e
     set -x
 }
 
 # this code of course is for guests that do run on systemd
 function fedora_configure_systemd() {
     set -e
     set -x
-    # so ignore if we can't find /etc/systemd at all 
+    local lxc=$1; shift
+    local lxc_root=$(lxcroot $lxc)
+
+    # so ignore if we can't find /etc/systemd at all
     [ -d ${lxc_root}/etc/systemd ] || return 0
     # otherwise let's proceed
     ln -sf /lib/systemd/system/multi-user.target ${lxc_root}/etc/systemd/system/default.target
     touch ${lxc_root}/etc/fstab
     ln -sf /dev/null ${lxc_root}/etc/systemd/system/udev.service
     [ -d ${lxc_root}/etc/systemd ] || return 0
     # otherwise let's proceed
     ln -sf /lib/systemd/system/multi-user.target ${lxc_root}/etc/systemd/system/default.target
     touch ${lxc_root}/etc/fstab
     ln -sf /dev/null ${lxc_root}/etc/systemd/system/udev.service
-# Thierry - Feb 2013
-# this was intended for f16 initially, in order to enable getty that otherwise would not start
-# having a getty running is helpful only if ssh won't start though, and we see a correlation between
-# VM's that refuse to lxc-stop and VM's that run crazy getty's
+# Thierry - Feb 2013 relying on getty is looking for trouble
 # so, turning getty off for now instead
 # so, turning getty off for now instead
-#   #dependency on a device unit fails it specially that we disabled udev
 #    sed -i 's/After=dev-%i.device/After=/' ${lxc_root}/lib/systemd/system/getty\@.service
     ln -sf /dev/null ${lxc_root}/etc/systemd/system/"getty@.service"
     rm -f ${lxc_root}/etc/systemd/system/getty.target.wants/*service || :
 #    sed -i 's/After=dev-%i.device/After=/' ${lxc_root}/lib/systemd/system/getty\@.service
     ln -sf /dev/null ${lxc_root}/etc/systemd/system/"getty@.service"
     rm -f ${lxc_root}/etc/systemd/system/getty.target.wants/*service || :
-# can't seem to handle this one with systemctl
-    chroot ${lxc_root} chkconfig network on
+    chroot ${lxc_root} $personality systemctl enable NetworkManager
 }
 
 # overwrite container yum config
 function fedora_configure_yum () {
 }
 
 # overwrite container yum config
 function fedora_configure_yum () {
-    set -x 
-    set -e 
+    set -x
+    set -e
     trap failure ERR INT
 
     trap failure ERR INT
 
-    lxc=$1; shift
-    fcdistro=$1; shift
-    pldistro=$1; shift
+    local lxc=$1; shift
+    local fcdistro=$1; shift
+    local pldistro=$1; shift
 
 
+    local lxc_root=$(lxcroot $lxc)
     # rpm --rebuilddb
     # rpm --rebuilddb
-    chroot $lxc_root rpm --rebuilddb
+    chroot ${lxc_root} $personality rpm --rebuilddb
 
 
-    echo "Initializing yum.repos.d in $lxc"
-    rm -f $lxc_root/etc/yum.repos.d/*
+    if [ -z "$USE_UPSTREAM_REPOS" ]; then
+        echo "Initializing yum.repos.d in $lxc"
+        rm -f $lxc_root/etc/yum.repos.d/*
 
 
-    cat > $lxc_root/etc/yum.repos.d/building.repo <<EOF
-[fedora]
-name=Fedora $release - $arch
-baseurl=http://mirror.onelab.eu/fedora/releases/$release/Everything/$arch/os/
-enabled=1
-metadata_expire=7d
-gpgcheck=1
-gpgkey=http://mirror.onelab.eu/keys/RPM-GPG-KEY-fedora-$release-primary
+        # use mirroring/ stuff instead of a hard-wired config
+        local repofile=$lxc_root/etc/yum.repos.d/building.repo
+        yumconf_mirrors $repofile ${DIRNAME} $fcdistro "" $FEDORA_MIRROR
+        # the keys stuff requires adjustment though
+        sed -i $repofile -e s,'gpgkey=.*',"gpgkey=${FEDORA_MIRROR_KEYS}/RPM-GPG-KEY-fedora-${fedora_release}-primary,"
+    fi
+    # import fedora key so that gpgckeck does not whine or require stdin
+    # required since fedora24
+    rpm --root $lxc_root --import $FEDORA_MIRROR_KEYS/RPM-GPG-KEY-fedora-${fedora_release}-primary
 
 
-[updates]
-name=Fedora $release - $arch - Updates
-baseurl=http://mirror.onelab.eu/fedora/updates/$release/$arch/
-enabled=1
-metadata_expire=7d
-gpgcheck=1
-gpgkey=http://mirror.onelab.eu/keys/RPM-GPG-KEY-fedora-$release-primary
-EOF
-    
-    # for using vtest-init-lxc.sh as a general-purpose lxc creation wrapper
+    # for using this script as a general-purpose lxc creation wrapper
     # just mention 'none' as the repo url
     # just mention 'none' as the repo url
-    if [ -n "$REPO_URL" ] ; then
-       if [ ! -d $lxc_root/etc/yum.repos.d ] ; then
-           echo "WARNING : cannot create myplc repo"
-       else
-            # exclude kernel from fedora repos 
-           yumexclude=$(pl_plcyumexclude $fcdistro $pldistro $DIRNAME)
-           for repo in $lxc_root/etc/yum.repos.d/* ; do
-               [ -f $repo ] && yumconf_exclude $repo "exclude=$yumexclude" 
-           done
-           # the build repo is not signed at this stage
-           cat > $lxc_root/etc/yum.repos.d/myplc.repo <<EOF
+    if [ -n "$MYPLC_REPO_URL" ] ; then
+        if [ ! -d $lxc_root/etc/yum.repos.d ] ; then
+            echo "WARNING : cannot create myplc repo"
+        else
+            # exclude kernel from fedora repos
+            yumexclude=$(pl_plcyumexclude $fcdistro $pldistro $DIRNAME)
+            for repo in $lxc_root/etc/yum.repos.d/* ; do
+                [ -f $repo ] && yumconf_exclude $repo "exclude=$yumexclude"
+            done
+            # the build repo is not signed at this stage
+            cat > $lxc_root/etc/yum.repos.d/myplc.repo <<EOF
 [myplc]
 name= MyPLC
 [myplc]
 name= MyPLC
-baseurl=$REPO_URL
+baseurl=$MYPLC_REPO_URL
 enabled=1
 gpgcheck=0
 EOF
 enabled=1
 gpgcheck=0
 EOF
-       fi
+        fi
     fi
     fi
-}    
+}
 
 ##############################
 
 ##############################
+# apparently ubuntu exposes a mirrors list by country at
+# http://mirrors.ubuntu.com/mirrors.txt
 # need to specify the right mirror for debian variants like ubuntu and the like
 function debian_mirror () {
 # need to specify the right mirror for debian variants like ubuntu and the like
 function debian_mirror () {
-    fcdistro=$1; shift
+    local fcdistro=$1; shift
     case $fcdistro in
     case $fcdistro in
-       squeeze|wheezy|jessie) 
-           echo http://ftp2.fr.debian.org/debian/ ;;
-       oneiric|precise|quantal|raring|saucy) 
-           echo http://mir1.ovh.net/ubuntu/ubuntu/ ;;
-       *) echo unknown distro $fcdistro; exit 1;;
+        wheezy|jessie)
+            echo http://ftp2.fr.debian.org/debian/ ;;
+        trusty|xenial|bionic|focal|jammy)
+            echo http://www-ftp.lip6.fr/pub/linux/distributions/Ubuntu/archive/ ;;
+        *) echo unknown distro $fcdistro; exit 1;;
     esac
 }
 
 function debian_install () {
     set -e
     set -x
     esac
 }
 
 function debian_install () {
     set -e
     set -x
-    lxc=$1; shift
+    local lxc=$1; shift
+    local lxc_root=$(lxcroot $lxc)
     mkdir -p $lxc_root
     mkdir -p $lxc_root
-    arch=$(canonical_arch $personality $fcdistro)
-    mirror=$(debian_mirror $fcdistro)
-    debootstrap --arch $arch $fcdistro $lxc_root $mirror
+    local arch=$(canonical_arch $personality $fcdistro)
+    local mirror=$(debian_mirror $fcdistro)
+    debootstrap --no-check-gpg --arch $arch $fcdistro $lxc_root $mirror
     # just like with fedora we ensure a few packages get installed as well
     # not started yet
     # just like with fedora we ensure a few packages get installed as well
     # not started yet
-    #virsh -c lxc:/// lxc-enter-namespace $lxc /bin/bash -c "apt-get update"
-    #virsh -c lxc:/// lxc-enter-namespace $lxc /bin/bash -c "apt-get -y install $DEBIAN_PREINSTALLED"
-    chroot $lxc_root apt-get update
-    chroot $lxc_root apt-get -y install $DEBIAN_PREINSTALLED
+    #virsh -c lxc:/// lxc-enter-namespace $lxc /usr/bin/$personality /bin/bash -c "apt-get update"
+    #virsh -c lxc:/// lxc-enter-namespace $lxc /usr/bin/$personality /bin/bash -c "apt-get -y install $DEBIAN_PREINSTALLED"
+    chroot ${lxc_root} $personality apt-get update
+    chroot ${lxc_root} $personality apt-get -y install $DEBIAN_PREINSTALLED
     # configure hostname
     cat <<EOF > ${lxc_root}/etc/hostname
 $GUEST_HOSTNAME
 EOF
     # configure hostname
     cat <<EOF > ${lxc_root}/etc/hostname
 $GUEST_HOSTNAME
 EOF
-    
+
 }
 
 function debian_configure () {
 }
 
 function debian_configure () {
-    guest_interfaces=${lxc_root}/etc/network/interfaces
-    ( [ -n "$BUILD_MODE" ] && write_guest_interfaces_build || write_guest_interfaces_test ) > $guest_interfaces
+    local lxc=$1; shift
+    local fcdistro=$1; shift
+    case $(network_config_method $fcdistro) in
+        interfaces)
+            local guest_interfaces=${lxc_root}/etc/network/interfaces
+            ( [ -n "$NAT_MODE" ] \
+                && write_guest_interfaces_natip \
+                || write_guest_interfaces_publicip ) > $guest_interfaces
+            ;;
+        systemd)
+            local systemd_config="${lxc_root}/etc/systemd/network/wired.network"
+            ( [ -n "$NAT_MODE" ] \
+                && write_guest_systemd_natip \
+                || write_guest_systemd_publicip ) > $systemd_config
+            chroot "${lxc_root}" systemctl enable systemd-networkd
+            ;;
+    esac
 }
 
 }
 
-function write_guest_interfaces_build () {
+function write_guest_interfaces_natip () {
     cat <<EOF
 auto $VIF_GUEST
 iface $VIF_GUEST inet dhcp
 EOF
 }
 
     cat <<EOF
 auto $VIF_GUEST
 iface $VIF_GUEST inet dhcp
 EOF
 }
 
-function write_guest_interfaces_test () {
+function write_guest_interfaces_publicip () {
     cat <<EOF
 auto $VIF_GUEST
 iface $VIF_GUEST inet static
     cat <<EOF
 auto $VIF_GUEST
 iface $VIF_GUEST inet static
@@ -527,6 +481,56 @@ netmask $NETMASK
 gateway $GATEWAY
 EOF
 }
 gateway $GATEWAY
 EOF
 }
+
+# systemd-networkd
+#   https://wiki.archlinux.org/title/systemd-networkd
+#   https://www.linuxtricks.fr/wiki/systemd-le-reseau-avec-systemd-networkd
+function write_guest_systemd_natip () {
+    cat << EOF
+[Match]
+Name=eth0
+
+[Network]
+DHCP=ipv4
+EOF
+}
+
+function write_guest_systemd_publicip () {
+    cat << EOF
+[Match]
+Name=eth0
+
+[Network]
+Address=${GUEST_IP}/${MASKLEN}
+Gateway=$GATEWAY
+EOF
+}
+
+function uuid() {
+    python -c "import uuid; print(uuid.uuid1())"
+}
+# xxx this seems to be no longer needed ?
+function write_guest_networkmanager_natip() {
+    cat << EOF
+EOF
+}
+function write_guest_networkmanager_publicip() {
+    cat << EOF
+[connection]
+id=Wired connection 1
+uuid=$(uuid)
+type=ethernet
+autoconnect-priority=-999
+# setting this seems to be counter-productive
+# interface-name=${VIF_GUEST}
+
+[ipv4]
+address1=${GUEST_IP}/${MASKLEN},${GATEWAY}
+#dhcp-hostname=${GUEST_HOSTNAME}
+method=manual
+EOF
+}
+
 ##############################
 function setup_lxc() {
 
 ##############################
 function setup_lxc() {
 
@@ -534,59 +538,89 @@ function setup_lxc() {
     set -e
     #trap failure ERR INT
 
     set -e
     #trap failure ERR INT
 
-    lxc=$1; shift
-    fcdistro=$1; shift
-    pldistro=$1; shift
-    personality=$1; shift
+    local lxc=$1; shift
+    local fcdistro=$1; shift
+    local pldistro=$1; shift
+    local personality=$1; shift
+
+    local lxc_root=$(lxcroot $lxc)
+
+    # create lxc container
 
 
-    # create lxc container 
-    
     pkg_method=$(package_method $fcdistro)
     case $pkg_method in
     pkg_method=$(package_method $fcdistro)
     case $pkg_method in
-       yum)
-           fedora_install || { echo "failed to install fedora root image"; exit 1 ; }
-           fedora_configure || { echo "failed to configure fedora for a container"; exit 1 ; }
-           ;;
-       debootstrap)
-           debian_install $lxc || { echo "failed to install debian/ubuntu root image"; exit 1 ; }
-           debian_configure || { echo "failed to configure debian/ubuntu for a container"; exit 1 ; }
-           ;;
-       *)
-           echo "$COMMAND:: unknown package_method - exiting"
-           exit 1
-           ;;
+        dnf)
+            if [ -z "$IMAGE" ]; then
+                fedora_install $lxc ||  { echo "failed to install fedora root image"; exit 1 ; }
+                # this appears to be safer; observed in Jan. 2016 on a f23 host and a f14 cached image
+                # we were getting this message when attempting the first chroot dnf install
+                # rpmdb: Program version 4.8 doesn't match environment version 5.3
+                chroot $(lxcroot $lxc) $personality rm -rf /var/lib/rpm/__db.00{0,1,2,3,4,5,6,7,8,9}
+                chroot $(lxcroot $lxc) $personality rpm --rebuilddb
+            fi
+            fedora_configure $lxc $fcdistro || { echo "failed to configure fedora for a container"; exit 1 ; }
+            ;;
+        debootstrap)
+            if [ -z "$IMAGE" ]; then
+                debian_install $lxc || { echo "failed to install debian/ubuntu root image"; exit 1 ; }
+            fi
+            debian_configure $lxc $fcdistro || { echo "failed to configure debian/ubuntu for a container"; exit 1 ; }
+            ;;
+        *)
+            echo "$COMMAND:: unknown package_method - exiting"
+            exit 1
+            ;;
     esac
 
     # Enable cgroup -- xxx -- is this really useful ?
     esac
 
     # Enable cgroup -- xxx -- is this really useful ?
-    mkdir $lxc_root/cgroup
-    
-    # set up resolv.conf
-    cp /etc/resolv.conf $lxc_root/etc/resolv.conf
-    # and /etc/hosts for at least localhost
+    [ -d $lxc_root/cgroup ] || mkdir $lxc_root/cgroup
+
+    ### set up resolv.conf from host
+    # ubuntu precise and on, /etc/resolv.conf is a symlink to ../run/resolvconf/resolv.conf
+    [ -h $lxc_root/etc/resolv.conf ] && rm -f $lxc_root/etc/resolv.conf
+    ### since fedora36, our hosts use systemd-resolved, but the guests can't use that
+    # so the administrator has the option to create /etc/resolv.conf.containers
+    # and if that file exists it will be copied in the containers instead of /etc/resolv.conf
+    if [ -f /etc/resolv.conf.containers ]; then
+        cp /etc/resolv.conf.containers $lxc_root/etc/resolv.conf
+    else
+        cp /etc/resolv.conf $lxc_root/etc/resolv.conf
+    fi
+    ### and /etc/hosts for at least localhost
     [ -f $lxc_root/etc/hosts ] || echo "127.0.0.1 localhost localhost.localdomain" > $lxc_root/etc/hosts
     [ -f $lxc_root/etc/hosts ] || echo "127.0.0.1 localhost localhost.localdomain" > $lxc_root/etc/hosts
-    
+
     # grant ssh access from host to guest
     # grant ssh access from host to guest
-    mkdir $lxc_root/root/.ssh
+    mkdir -p $lxc_root/root/.ssh
     cat /root/.ssh/id_rsa.pub >> $lxc_root/root/.ssh/authorized_keys
     chmod 700 $lxc_root/root/.ssh
     chmod 600 $lxc_root/root/.ssh/authorized_keys
 
     # don't keep the input xml, this can be retrieved at all times with virsh dumpxml
     cat /root/.ssh/id_rsa.pub >> $lxc_root/root/.ssh/authorized_keys
     chmod 700 $lxc_root/root/.ssh
     chmod 600 $lxc_root/root/.ssh/authorized_keys
 
     # don't keep the input xml, this can be retrieved at all times with virsh dumpxml
-    config_xml=/tmp/$lxc.xml
-    ( [ -n "$BUILD_MODE" ] && write_lxc_xml_build $lxc || write_lxc_xml_test $lxc ) > $config_xml
-    
+    local config_xml=/tmp/$lxc.xml
+    ( [ -n "$NAT_MODE" ] && write_lxc_xml_natip $lxc || write_lxc_xml_publicip $lxc ) > $config_xml
+
     # define lxc container for libvirt
     virsh -c lxc:/// define $config_xml
 
     return 0
 }
 
     # define lxc container for libvirt
     virsh -c lxc:/// define $config_xml
 
     return 0
 }
 
-function write_lxc_xml_test () {
-    lxc=$1; shift
+# this part does not belong in a domain any more
+# but goes in a network object of its own existence
+#      <network>
+#        <name>host-bridge</name>
+#        <forward mode="bridge"/>
+#        <bridge name="br0"/>
+#      </network>
+#
+
+function write_lxc_xml_publicip () {
+    local lxc=$1; shift
+    local lxc_root=$(lxcroot $lxc)
     cat <<EOF
 <domain type='lxc'>
   <name>$lxc</name>
     cat <<EOF
 <domain type='lxc'>
   <name>$lxc</name>
-  <memory>524288</memory>
+  <memory>$MEMORY</memory>
   <os>
     <type arch='$arch2'>exe</type>
     <init>/sbin/init</init>
   <os>
     <type arch='$arch2'>exe</type>
     <init>/sbin/init</init>
@@ -611,27 +645,27 @@ function write_lxc_xml_test () {
     </interface>
     <console type='pty' />
   </devices>
     </interface>
     <console type='pty' />
   </devices>
-  <network>
-    <name>host-bridge</name>
-    <forward mode="bridge"/>
-    <bridge name="$PUBLIC_BRIDGE"/>
-  </network>
 </domain>
 EOF
 }
 
 </domain>
 EOF
 }
 
-function write_lxc_xml_build () { 
-    lxc=$1; shift
+# grant build guests the ability to do mknods
+function write_lxc_xml_natip () {
+    local lxc=$1; shift
+    local lxc_root=$(lxcroot $lxc)
     cat <<EOF
 <domain type='lxc'>
   <name>$lxc</name>
     cat <<EOF
 <domain type='lxc'>
   <name>$lxc</name>
-  <memory>524288</memory>
+  <memory>$MEMORY</memory>
   <os>
     <type arch='$arch2'>exe</type>
     <init>/sbin/init</init>
   </os>
   <features>
     <acpi/>
   <os>
     <type arch='$arch2'>exe</type>
     <init>/sbin/init</init>
   </os>
   <features>
     <acpi/>
+    <capabilities policy='default'>
+      <mknod state='on'/>
+    </capabilities>
   </features>
   <vcpu>1</vcpu>
   <clock offset='utc'/>
   </features>
   <vcpu>1</vcpu>
   <clock offset='utc'/>
@@ -654,19 +688,18 @@ EOF
 }
 
 # this one is dhcp-based
 }
 
 # this one is dhcp-based
-function write_guest_ifcfg_build () {
+function write_guest_ifcfg_natip () {
     cat <<EOF
 DEVICE=$VIF_GUEST
 BOOTPROTO=dhcp
 ONBOOT=yes
     cat <<EOF
 DEVICE=$VIF_GUEST
 BOOTPROTO=dhcp
 ONBOOT=yes
-NM_CONTROLLED=no
 TYPE=Ethernet
 MTU=1500
 EOF
 }
 
 # use fixed GUEST_IP as specified by GUEST_HOSTNAME
 TYPE=Ethernet
 MTU=1500
 EOF
 }
 
 # use fixed GUEST_IP as specified by GUEST_HOSTNAME
-function write_guest_ifcfg_test () {
+function write_guest_ifcfg_publicip () {
     cat <<EOF
 DEVICE=$VIF_GUEST
 BOOTPROTO=static
     cat <<EOF
 DEVICE=$VIF_GUEST
 BOOTPROTO=static
@@ -675,129 +708,156 @@ HOSTNAME=$GUEST_HOSTNAME
 IPADDR=$GUEST_IP
 NETMASK=$NETMASK
 GATEWAY=$GATEWAY
 IPADDR=$GUEST_IP
 NETMASK=$NETMASK
 GATEWAY=$GATEWAY
-NM_CONTROLLED=no
 TYPE=Ethernet
 MTU=1500
 EOF
 }
 
 TYPE=Ethernet
 MTU=1500
 EOF
 }
 
-function devel_or_vtest_tools () {
+function devel_or_test_tools () {
 
 
-    set -x 
-    set -e 
+    set -x
+    set -e
     trap failure ERR INT
 
     trap failure ERR INT
 
-    lxc=$1; shift
-    fcdistro=$1; shift
-    pldistro=$1; shift
-    personality=$1; shift
+    local lxc=$1; shift
+    local fcdistro=$1; shift
+    local pldistro=$1; shift
+    local personality=$1; shift
 
 
-    pkg_method=$(package_method $fcdistro)
+    local lxc_root=$(lxcroot $lxc)
+
+    local pkg_method=$(package_method $fcdistro)
 
 
-    pkgsfile=$(pl_locateDistroFile $DIRNAME $pldistro $PREINSTALLED)
+    local pkgsfile=$(pl_locateDistroFile $DIRNAME $pldistro $PREINSTALLED)
 
     ### install individual packages, then groups
 
     ### install individual packages, then groups
-    # get target arch - use uname -i here (we want either x86_64 or i386)
-   
-    lxc_arch=$(chroot $lxc_root uname -i)
+    # get target arch - (we want either x86_64 or i386)
+
+    local lxc_arch=$(chroot ${lxc_root} $personality arch)
     # on debian systems we get arch through the 'arch' command
     # on debian systems we get arch through the 'arch' command
-    [ "$lxc_arch" = "unknown" ] && lxc_arch=$(chroot $lxc_root arch)
+    [ "$lxc_arch" = "unknown" ] && lxc_arch=$(chroot ${lxc_root} $personality arch)
 
 
-    packages=$(pl_getPackages -a $lxc_arch $fcdistro $pldistro $pkgsfile)
-    groups=$(pl_getGroups -a $lxc_arch $fcdistro $pldistro $pkgsfile)
+    local packages=$(pl_getPackages -a $lxc_arch $fcdistro $pldistro $pkgsfile)
+    local groups=$(pl_getGroups -a $lxc_arch $fcdistro $pldistro $pkgsfile)
 
     case "$pkg_method" in
 
     case "$pkg_method" in
-       yum)
-           [ -n "$packages" ] && chroot $lxc_root yum -y install $packages
-           for group_plus in $groups; do
-               group=$(echo $group_plus | sed -e "s,+++, ,g")
-               chroot $lxc_root yum -y groupinstall "$group"
-           done
-           # store current rpm list in /init-lxc.rpms in case we need to check the contents
-           chroot $lxc_root rpm -aq > $lxc_root/init-lxc.rpms
-           ;;
-       debootstrap)
-           # for ubuntu
-           if grep -iq ubuntu /vservers/$lxc/etc/lsb-release 2> /dev/null; then
-               # on ubuntu, at this point we end up with a single feed in /etc/apt/sources.list
-               # we need at least to add the 'universe' feed for python-rpm
-               ( cd /vservers/$lxc/etc/apt ; head -1 sources.list | sed -e s,main,universe, > sources.list.d/universe.list )
-               # also adding a link to updates sounds about right
-               ( cd /vservers/$lxc/etc/apt ; head -1 sources.list | sed -e 's, main,-updates main,' > sources.list.d/updates.list )
-           fi
-           for package in $packages ; do
-               # container not started yet
-               #virsh -c lxc:/// lxc-enter-namespace $lxc /bin/bash -c "apt-get install -y $package" || :
-               chroot $lxc_root apt-get install -y $package || :
-           done
-           ### xxx todo install groups with apt..
-           ;;
-       *)
-           echo "unknown pkg_method $pkg_method"
-           ;;
+        dnf)
+            # --allowerasing required starting with fedora24
+            #
+            local has_dnf=""
+            chroot ${lxc_root} $personality dnf --version && has_dnf=true
+            if [ -n "$has_dnf" ]; then
+                echo "container has dnf - invoking with --allowerasing"
+                local pkg_installer="dnf -y install --allowerasing"
+                local grp_installer="dnf -y groupinstall --allowerasing"
+            else
+                echo "container has only dnf"
+                local pkg_installer="dnf -y install"
+                local grp_installer="dnf -y groupinstall"
+            fi
+            [ -n "$packages" ] && chroot ${lxc_root} $personality $pkg_installer $packages
+            for group_plus in $groups; do
+                local group=$(echo $group_plus | sed -e "s,+++, ,g")
+                chroot ${lxc_root} $personality $grp_installer "$group"
+            done
+            # store current rpm list in /init-lxc.rpms in case we need to check the contents
+            chroot ${lxc_root} $personality rpm -aq > $lxc_root/init-lxc.rpms
+            ;;
+        debootstrap)
+            # for ubuntu
+            if grep -iq ubuntu /vservers/$lxc/etc/lsb-release 2> /dev/null; then
+                # on ubuntu, at this point we end up with a single feed in /etc/apt/sources.list
+                # we need at least to add the 'universe' feed for python-rpm
+                ( cd /vservers/$lxc/etc/apt ; head -1 sources.list | sed -e s,main,universe, > sources.list.d/universe.list )
+                # also adding a link to updates sounds about right
+                ( cd /vservers/$lxc/etc/apt ; head -1 sources.list | sed -e 's, main,-updates main,' > sources.list.d/updates.list )
+                # tell apt about the changes
+                chroot /vservers/$lxc apt-get update
+            fi
+            for package in $packages ; do
+                # container not started yet
+                #virsh -c lxc:/// lxc-enter-namespace $lxc /usr/bin/$personality /bin/bash -c "apt-get install -y $package" || :
+                chroot ${lxc_root} $personality apt-get install -y $package || :
+            done
+            ### xxx todo install groups with apt..
+            ;;
+        *)
+            echo "unknown pkg_method $pkg_method"
+            ;;
     esac
 
     return 0
 }
 
 function post_install () {
     esac
 
     return 0
 }
 
 function post_install () {
-    lxc=$1; shift 
-    personality=$1; shift
-    if [ -n "$BUILD_MODE" ] ; then
-       post_install_build $lxc $personality
-       virsh -c lxc:/// start $lxc
-       # manually run dhclient in guest - somehow this network won't start on its own
-       virsh -c lxc:/// lxc-enter-namespace $lxc /bin/bash -c "dhclient $VIF_GUEST"
-    else
-       post_install_myplc $lxc $personality
-       virsh -c lxc:/// start $lxc
-# it sounds like we don't need ssh per se any more
-# it still makes sense to wait for network readiness though
-# some day maybe...
-       wait_for_ssh $lxc
-    fi
+    local lxc=$1; shift
+    local personality=$1; shift
+    local lxc_root=$(lxcroot $lxc)
     # setup localtime from the host
     cp /etc/localtime $lxc_root/etc/localtime
     # setup localtime from the host
     cp /etc/localtime $lxc_root/etc/localtime
+    sshd_disable_password_auth $lxc
+    # post install hook
+    [ -n "$NAT_MODE" ] && post_install_natip $lxc $personality || post_install_myplc $lxc $personality
+    # start the VM unless specified otherwise
+    if [ -n "$START_VM" ] ; then
+        echo Starting guest $lxc
+        virsh -c lxc:/// start $lxc
+        if [ -n "$NAT_MODE" ] ; then
+            wait_for_ssh $lxc
+        else
+            wait_for_ssh $lxc $GUEST_IP
+        fi
+    fi
 }
 
 }
 
-function post_install_build () {
+# just in case, let's stay on the safe side
+function sshd_disable_password_auth () {
+    local lxc=$1; shift
+    local lxc_root=$(lxcroot $lxc)
+    sed --in-place=.password -e 's,^#\?PasswordAuthentication.*,PasswordAuthentication no,' \
+        $lxc_root/etc/ssh/sshd_config
+}
+
+function post_install_natip () {
 
 
-    set -x 
-    set -e 
+    set -x
+    set -e
     trap failure ERR INT
 
     trap failure ERR INT
 
-    lxc=$1; shift
-    personality=$1; shift
+    local lxc=$1; shift
+    local personality=$1; shift
+    local lxc_root=$(lxcroot $lxc)
 
 ### From myplc-devel-native.spec
 # be careful to backslash $ in this, otherwise it's the root context that's going to do the evaluation
 
 ### From myplc-devel-native.spec
 # be careful to backslash $ in this, otherwise it's the root context that's going to do the evaluation
-    cat << EOF | chroot $lxc_root bash -x
-    
+    cat << EOF | chroot ${lxc_root} $personality bash -x
+
     # customize root's prompt
     /bin/cat << PROFILE > /root/.profile
 export PS1="[$lxc] \\w # "
 PROFILE
 
 EOF
     # customize root's prompt
     /bin/cat << PROFILE > /root/.profile
 export PS1="[$lxc] \\w # "
 PROFILE
 
 EOF
-       
+
 }
 
 function post_install_myplc  () {
 }
 
 function post_install_myplc  () {
-    set -x 
-    set -e 
+    set -x
+    set -e
     trap failure ERR INT
 
     trap failure ERR INT
 
-    lxc=$1; shift
-    personality=$1; shift
+    local lxc=$1; shift
+    local personality=$1; shift
+    local lxc_root=$(lxcroot $lxc)
 
 # be careful to backslash $ in this, otherwise it's the root context that's going to do the evaluation
 
 # be careful to backslash $ in this, otherwise it's the root context that's going to do the evaluation
-    cat << EOF | chroot $lxc_root bash -x
+    cat << EOF | chroot ${lxc_root} $personality bash -x
 
     # create /etc/sysconfig/network if missing
     [ -f /etc/sysconfig/network ] || /bin/echo NETWORKING=yes > /etc/sysconfig/network
 
     # turn off regular crond, as plc invokes plc_crond
 
     # create /etc/sysconfig/network if missing
     [ -f /etc/sysconfig/network ] || /bin/echo NETWORKING=yes > /etc/sysconfig/network
 
     # turn off regular crond, as plc invokes plc_crond
-    /sbin/chkconfig crond off
+    /sbin/chkconfig crond off >& /dev/null
 
     # customize root's prompt
     /bin/cat << PROFILE > /root/.profile
 
     # customize root's prompt
     /bin/cat << PROFILE > /root/.profile
@@ -807,31 +867,82 @@ PROFILE
 EOF
 }
 
 EOF
 }
 
+########################################
+# workaround for broken lxc-enter-namespace
+# 1st version was relying on virsh net-dhcp-leases
+# however this was too fragile, would not work for fedora14 containers
+# WARNING: this code is duplicated in lbuild-nightly.sh
+function guest_ipv4_old() {
+    lxc=$1; shift
+
+    mac=$(virsh -c lxc:/// domiflist $lxc | grep -E 'network|bridge' | awk '{print $5;}')
+    [ -z "$mac" ] && { echo 1>&2 guest_ipv4_old cannot find mac; return 1; }
+    ip=$(arp -en | grep "$mac" | awk '{print $1;}')
+    # if not known: run a ping and try again
+    if [ -z $ip ]; then
+           ping -c1 -w1 -W1 $lxc >& /dev/null
+           ping -c1 -w1 -W1 $lxc.pl.sophia.inria.fr >& /dev/null
+           ip=$(arp -en | grep "$mac" | awk '{print $1;}')
+    fi
+    [ -z "$ip" ] && { echo 1>&2 guest_ipv4_old cannot find ip; return 1; }
+    echo $ip
+}
+
+function guest_ipv4() {
+    lxc=$1; shift
+
+    # this gives us the libvirt_lxc pid for the container
+    local lxc_pid=$(virsh -c lxc:/// dominfo $lxc | grep '^Id:' | awk '{print $2;}' | sed -e "s|-||g")
+    [[ -z "$lxc_pid" ]] && { echo 1>&2 guest_ipv4 cannot find lxc pid; return 1; }
+    # but we need the systemd (pid=1) instance for the container
+    local systemd_pid=$(pgrep -P $lxc_pid systemd)
+    [[ -z "$systemd_pid" ]] && { echo 1>&2 guest_ipv4 cannot systemd pid; return 1; }
+    # from there we can inspect the network interfaces
+    local domip=$(nsenter -t $systemd_pid -n ip -br addr show eth0 \
+                 | awk '{print $3}' \
+                 | cut -d/ -f1 \
+                 )
+    [ -z "$domip" ] && { echo 1>&2 guest_ipv4 cannot find ip; return 1; }
+    echo $domip
+}
+
 function wait_for_ssh () {
     set -x
     set -e
 function wait_for_ssh () {
     set -x
     set -e
-    #trap failure ERR INT
 
 
-    lxc=$1; shift
-  
-    echo network in guest is up, waiting for ssh...
-
-    #wait max 2 min for sshd to start 
-    ssh_up=""
-    current_time=$(date +%s)
-    stop_time=$(($current_time + 120))
-    
-    counter=1
+    local lxc=$1; shift
+
+    # if run in public_ip mode, we know the IP of the guest and it is specified here
+    local specified_ip
+    [ -n "$1" ] && { specified_ip=$1; shift; }
+
+    #wait max 2 min for sshd to start
+    local success=""
+    local current_time=$(date +%s)
+    local stop_time=$(($current_time + 120))
+
+    local counter=1
     while [ "$current_time" -lt "$stop_time" ] ; do
     while [ "$current_time" -lt "$stop_time" ] ; do
-         echo "$counter-th attempt to reach sshd in container $lxc ..."
-         ssh -o "StrictHostKeyChecking no" $GUEST_IP 'uname -i' && { ssh_up=true; echo "SSHD in container $lxc is UP"; break ; } || :
-         sleep 10
-         current_time=$(($current_time + 10))
-         counter=$(($counter+1))
+        if [ -n "$specified_ip" ]; then
+            guest_ip="${specified_ip}"
+        else
+            guest_ip=$(guest_ipv4 $lxc) || :
+        fi
+        echo "$counter-th attempt to reach sshd in container $lxc on address $guest_ip ..."
+        [ -n "$guest_ip" ] && ssh -o "StrictHostKeyChecking no" $guest_ip arch && {
+            success=true; echo "SSHD in container $lxc is UP on IP $guest_ip"; break ; } || :
+        # some of our boxes have gone through a long upgrade historically, and
+        # so they don't end up with the same gid mapping for the ssh_keys
+        # group as the ones in the guest that result from a fresh install
+        # 2024 : lxc-enter-namespace is broken anyways
+        # virsh -c lxc:/// lxc-enter-namespace $lxc /bin/bash -c "chown root:ssh_keys /etc/ssh/*_key" || :
+        counter=$(($counter+1))
+        sleep 10
+        current_time=$(date +%s)
     done
 
     # Thierry: this is fatal, let's just exit with a failure here
     done
 
     # Thierry: this is fatal, let's just exit with a failure here
-    [ -z $ssh_up ] && { echo "SSHD in container $lxc is not running" ; exit 1 ; } 
+    [ -z $success ] && { echo "SSHD in container $lxc could not be reached (guest_ip=$guest_ip)" ; exit 1 ; }
     return 0
 }
 
     return 0
 }
 
@@ -842,7 +953,7 @@ function failure () {
 }
 
 function usage () {
 }
 
 function usage () {
-    set +x 
+    set +x
     echo "Usage: $COMMAND [options] lxc-name             (aka build mode)"
     echo "Usage: $COMMAND -n hostname [options] lxc-name (aka test mode)"
     echo "Description:"
     echo "Usage: $COMMAND [options] lxc-name             (aka build mode)"
     echo "Usage: $COMMAND -n hostname [options] lxc-name (aka test mode)"
     echo "Description:"
@@ -858,11 +969,14 @@ function usage () {
     echo " -r repo-url - used to populate yum.repos.d - required in test mode"
     echo " -P pkgs_file - defines a set of extra packages to install in guest"
     echo "    by default we use devel.pkgs (build mode) or runtime.pkgs (test mode)"
     echo " -r repo-url - used to populate yum.repos.d - required in test mode"
     echo " -P pkgs_file - defines a set of extra packages to install in guest"
     echo "    by default we use devel.pkgs (build mode) or runtime.pkgs (test mode)"
+    echo " -i image - the location of the rootfs"
+    echo " -m memory - the amount of allocated memory in MB - defaults to $DEFAULT_MEMORY MB"
+    echo " -s do not start VM"
     echo " -v be verbose"
     exit 1
 }
 
     echo " -v be verbose"
     exit 1
 }
 
-### parse args and 
+### parse args and
 function main () {
 
     #set -e
 function main () {
 
     #set -e
@@ -873,68 +987,91 @@ function main () {
           exit 1
     fi
 
           exit 1
     fi
 
-    while getopts "n:f:d:p:r:P:v" opt ; do
-       case $opt in
-           n) GUEST_HOSTNAME=$OPTARG;;
-           f) fcdistro=$OPTARG;;
-           d) pldistro=$OPTARG;;
-           p) personality=$OPTARG;;
-           r) REPO_URL=$OPTARG;;
-           P) PREINSTALLED=$OPTARG;;
-           v) VERBOSE=true; set -x;;
-           *) usage ;;
-       esac
+    START_VM=true
+    while getopts "n:f:d:p:r:uP:i:m:sv" opt ; do
+        case $opt in
+            n) GUEST_HOSTNAME=$OPTARG;;
+            f) fcdistro=$OPTARG;;
+            d) pldistro=$OPTARG;;
+            p) personality=$OPTARG;;
+            r) MYPLC_REPO_URL=$OPTARG;;
+            u) USE_UPSTREAM_REPOS=true;;
+            P) PREINSTALLED=$OPTARG;;
+            i) IMAGE=$OPTARG;;
+            m) MEMORY=$OPTARG;;
+            s) START_VM= ;;
+            v) VERBOSE=true; set -x;;
+            *) usage ;;
+        esac
     done
     done
-       
+
     shift $(($OPTIND - 1))
 
     # parse fixed arguments
     [[ -z "$@" ]] && usage
     shift $(($OPTIND - 1))
 
     # parse fixed arguments
     [[ -z "$@" ]] && usage
-    lxc=$1 ; shift
-    lxc_root=/vservers/$lxc
+    local lxc=$1 ; shift
+    local lxc_root=$(lxcroot $lxc)
+
     # rainchecks
     # rainchecks
+    # when using with the -i option, checking that $lxc_root is void
+    # is a little too much stress..
     almost_empty $lxc_root || \
     almost_empty $lxc_root || \
-       { echo "container $lxc already exists in $lxc_root - exiting" ; exit 1 ; }
+        { echo "container $lxc already exists in $lxc_root - exiting" ; exit 1 ; }
     virsh -c lxc:/// domuuid $lxc >& /dev/null && \
     virsh -c lxc:/// domuuid $lxc >& /dev/null && \
-       { echo "container $lxc already exists in libvirt - exiting" ; exit 1 ; }
+        { echo "container $lxc already exists in libvirt - exiting" ; exit 1 ; }
     mkdir -p $lxc_root
 
     mkdir -p $lxc_root
 
+    # if IMAGE, copy the provided rootfs to lxc_root
+    if [ -n "$IMAGE" ] ; then
+        if [ ! -d "$IMAGE" ]; then
+            echo "$IMAGE rootfs folder does not exist - exiting"
+            exit 1
+        else
+            echo "Copying $IMAGE into $lxc_root with rsync --archive --delete"
+            rsync --archive --delete $IMAGE/ $lxc_root/
+        fi
+    fi
+
     # check we've exhausted the arguments
     [[ -n "$@" ]] && usage
 
     # check we've exhausted the arguments
     [[ -n "$@" ]] && usage
 
-    # BUILD_MODE is true unless we specified a hostname
-    [ -n "$GUEST_HOSTNAME" ] || BUILD_MODE=true
+    # NAT_MODE is true unless we specified a hostname
+    [ -n "$GUEST_HOSTNAME" ] || NAT_MODE=true
 
     # set default values
     [ -z "$fcdistro" ] && fcdistro=$DEFAULT_FCDISTRO
     [ -z "$pldistro" ] && pldistro=$DEFAULT_PLDISTRO
     [ -z "$personality" ] && personality=$DEFAULT_PERSONALITY
 
     # set default values
     [ -z "$fcdistro" ] && fcdistro=$DEFAULT_FCDISTRO
     [ -z "$pldistro" ] && pldistro=$DEFAULT_PLDISTRO
     [ -z "$personality" ] && personality=$DEFAULT_PERSONALITY
-    
+    [ -z "$MEMORY" ] && MEMORY=$DEFAULT_MEMORY
+
+    # set memory in KB
+    MEMORY=$(($MEMORY * 1024))
+
     # the set of preinstalled packages - depends on mode
     # the set of preinstalled packages - depends on mode
-    if [ -z "$PREINSTALLED"] ; then
-       if [ -n "$BUILD_MODE" ] ; then
-           PREINSTALLED=devel.pkgs
-       else
-           PREINSTALLED=runtime.pkgs
-       fi
+    if [ -z "$PREINSTALLED" ] ; then
+        if [ -n "$NAT_MODE" ] ; then
+            PREINSTALLED=devel.pkgs
+        else
+            PREINSTALLED=runtime.pkgs
+        fi
     fi
 
     fi
 
-    if [ -n "$BUILD_MODE" ] ; then
-       # we can now set GUEST_HOSTNAME safely
+    if [ -n "$NAT_MODE" ] ; then
+        # we can now set GUEST_HOSTNAME safely
         [ -z "$GUEST_HOSTNAME" ] && GUEST_HOSTNAME=$(echo $lxc | sed -e 's,\.,-,g')
     else
         [ -z "$GUEST_HOSTNAME" ] && GUEST_HOSTNAME=$(echo $lxc | sed -e 's,\.,-,g')
     else
-       # as this command can be used in other contexts, not specifying
-       # a repo is considered a warning
-       # use -r none to get rid of this warning
-       if [ "$REPO_URL" == "none" ] ; then
-           REPO_URL=""
-       elif [ -z "$REPO_URL" ] ; then
-           echo "WARNING -- setting up a yum repo is recommended" 
-       fi
+        # as this command can be used in other contexts, not specifying
+        # a repo is considered a warning
+        # use -r none to get rid of this warning
+        if [ "$MYPLC_REPO_URL" == "none" ] ; then
+            MYPLC_REPO_URL=""
+        elif [ -z "$MYPLC_REPO_URL" ] ; then
+            echo "WARNING -- setting up a yum repo is recommended"
+        fi
     fi
 
     ##########
     fi
 
     ##########
-    release=$(echo $fcdistro | cut -df -f2)
+    fedora_release=$(echo $fcdistro | cut -df -f2)
 
     if [ "$personality" == "linux32" ]; then
         arch=i386
 
     if [ "$personality" == "linux32" ]; then
         arch=i386
@@ -948,26 +1085,31 @@ function main () {
 
     # compute networking details for the test mode
     # (build mode relies entirely on dhcp on the private subnet)
 
     # compute networking details for the test mode
     # (build mode relies entirely on dhcp on the private subnet)
-    if [ -z "$BUILD_MODE" ] ; then
+    if [ -z "$NAT_MODE" ] ; then
 
 
-       create_bridge_if_needed
+        #create_bridge_if_needed $PUBLIC_BRIDGE
+        lbuild-bridge.sh $PUBLIC_BRIDGE
 
 
-       GUEST_IP=$(gethostbyname $GUEST_HOSTNAME)
-       # use same NETMASK as bridge interface br0
-       MASKLEN=$(ip addr show $PUBLIC_BRIDGE | grep -v inet6 | grep inet | awk '{print $2;}' | cut -d/ -f2)
+        GUEST_IP=$(gethostbyname $GUEST_HOSTNAME)
+        # use same NETMASK as bridge interface br0
+        MASKLEN=$(ip addr show $PUBLIC_BRIDGE | grep -v inet6 | grep inet | awk '{print $2;}' | cut -d/ -f2)
         NETMASK=$(masklen_to_netmask $MASKLEN)
         GATEWAY=$(ip route show | grep default | awk '{print $3}' | head -1)
         NETMASK=$(masklen_to_netmask $MASKLEN)
         GATEWAY=$(ip route show | grep default | awk '{print $3}' | head -1)
-        VIF_HOST="i$(echo $GUEST_HOSTNAME | cut -d. -f1)"
+        VIF_HOST="vif$(echo $GUEST_HOSTNAME | cut -d. -f1)"
     fi
 
     fi
 
-    setup_lxc $lxc $fcdistro $pldistro $personality 
+    setup_lxc $lxc $fcdistro $pldistro $personality
 
 
-    devel_or_vtest_tools $lxc $fcdistro $pldistro $personality
+    # historically this command is for setting up a build or a test VM
+    # kind of patchy right now though
+    devel_or_test_tools $lxc $fcdistro $pldistro $personality
 
     # container gets started here
     post_install $lxc $personality
 
     # container gets started here
     post_install $lxc $personality
-    
+
     echo $COMMAND Done
     echo $COMMAND Done
+
+    exit 0
 }
 
 main "$@"
 }
 
 main "$@"