Introduction of cloud-init configuration in virt-install
Usage: --cloud-init Signed-off-by: Athina Plaskasoviti <athina.plaskasoviti@gmail.com>
This commit is contained in:
parent
c311b28979
commit
19317024cc
|
@ -456,6 +456,9 @@ def build_installer(options, guest, installdata):
|
||||||
installer.set_initrd_injections(options.initrd_inject)
|
installer.set_initrd_injections(options.initrd_inject)
|
||||||
if options.autostart:
|
if options.autostart:
|
||||||
installer.autostart = True
|
installer.autostart = True
|
||||||
|
if options.cloud_init:
|
||||||
|
cloudinit_data = cli.parse_cloud_init(options.cloud_init)
|
||||||
|
installer.set_cloudinit_data(cloudinit_data)
|
||||||
|
|
||||||
return installer
|
return installer
|
||||||
|
|
||||||
|
@ -842,6 +845,8 @@ def parse_args():
|
||||||
help=_("Perform an unattended installation"))
|
help=_("Perform an unattended installation"))
|
||||||
insg.add_argument("--install",
|
insg.add_argument("--install",
|
||||||
help=_("Specify fine grained install options"))
|
help=_("Specify fine grained install options"))
|
||||||
|
insg.add_argument("--cloud-init", nargs="?", const=1,
|
||||||
|
help=_("Perform a cloud image installation, configuring cloud-init"))
|
||||||
|
|
||||||
# Takes a URL and just prints to stdout the detected distro name
|
# Takes a URL and just prints to stdout the detected distro name
|
||||||
insg.add_argument("--test-media-detection", help=argparse.SUPPRESS)
|
insg.add_argument("--test-media-detection", help=argparse.SUPPRESS)
|
||||||
|
|
|
@ -28,6 +28,7 @@ from .nodedev import NodeDevice
|
||||||
from .osdict import OSDB
|
from .osdict import OSDB
|
||||||
from .storage import StoragePool, StorageVolume
|
from .storage import StoragePool, StorageVolume
|
||||||
from .install.unattended import UnattendedData
|
from .install.unattended import UnattendedData
|
||||||
|
from .install.cloudinit import CloudInitData
|
||||||
|
|
||||||
|
|
||||||
HAS_VIRTVIEWER = shutil.which("virt-viewer")
|
HAS_VIRTVIEWER = shutil.which("virt-viewer")
|
||||||
|
@ -467,7 +468,7 @@ def get_meter():
|
||||||
|
|
||||||
def _get_completer_parsers():
|
def _get_completer_parsers():
|
||||||
return VIRT_PARSERS + [ParserCheck, ParserLocation,
|
return VIRT_PARSERS + [ParserCheck, ParserLocation,
|
||||||
ParserUnattended, ParserInstall]
|
ParserUnattended, ParserInstall, ParserCloudInit]
|
||||||
|
|
||||||
|
|
||||||
def _virtparser_completer(prefix, **kwargs):
|
def _virtparser_completer(prefix, **kwargs):
|
||||||
|
@ -1614,6 +1615,31 @@ def parse_install(optstr):
|
||||||
return installdata
|
return installdata
|
||||||
|
|
||||||
|
|
||||||
|
########################
|
||||||
|
# --cloud-init parsing #
|
||||||
|
########################
|
||||||
|
|
||||||
|
class ParserCloudInit(VirtCLIParser):
|
||||||
|
cli_arg_name = "cloud_init"
|
||||||
|
supports_clearxml = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _init_class(cls, **kwargs):
|
||||||
|
VirtCLIParser._init_class(**kwargs)
|
||||||
|
cls.add_arg("root-password", "root_password")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_cloud_init(optstr):
|
||||||
|
ret = CloudInitData()
|
||||||
|
if optstr == 1:
|
||||||
|
# This means bare --cloud-init, so there's nothing to parse
|
||||||
|
return ret
|
||||||
|
|
||||||
|
parser = ParserCloudInit(optstr)
|
||||||
|
parser.parse(ret)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# --location parsing #
|
# --location parsing #
|
||||||
######################
|
######################
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import tempfile
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import time
|
||||||
|
from ..logger import log
|
||||||
|
|
||||||
|
|
||||||
|
class CloudInitData():
|
||||||
|
root_password = None
|
||||||
|
|
||||||
|
|
||||||
|
def create_metadata(scratchdir, hostname=None):
|
||||||
|
if hostname:
|
||||||
|
instance = hostname
|
||||||
|
else:
|
||||||
|
hostname = instance = "localhost"
|
||||||
|
content = 'instance-id: %s\n' % instance
|
||||||
|
content += 'hostname: %s\n' % hostname
|
||||||
|
log.debug("Generated cloud-init metadata:\n%s", content)
|
||||||
|
|
||||||
|
fileobj = tempfile.NamedTemporaryFile(
|
||||||
|
prefix="virtinst-", suffix="-metadata",
|
||||||
|
dir=scratchdir, delete=False)
|
||||||
|
filename = fileobj.name
|
||||||
|
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def create_userdata(scratchdir, cloudinit_data, username=None, password=None):
|
||||||
|
if not password:
|
||||||
|
password = ""
|
||||||
|
for dummy in range(16):
|
||||||
|
password += random.choice(string.ascii_letters + string.digits)
|
||||||
|
content = "#cloud-config\n"
|
||||||
|
if username:
|
||||||
|
content += "name: %s\n" % username
|
||||||
|
if cloudinit_data.root_password == "generate":
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
content += "password: %s\n" % password
|
||||||
|
log.debug("Generated password for first boot: \n%s", password)
|
||||||
|
time.sleep(20)
|
||||||
|
content += "runcmd:\n"
|
||||||
|
content += "- [ sudo, touch, /etc/cloud/cloud-init.disabled ]\n"
|
||||||
|
log.debug("Generated cloud-init userdata:\n%s", content)
|
||||||
|
|
||||||
|
|
||||||
|
fileobj = tempfile.NamedTemporaryFile(
|
||||||
|
prefix="virtinst-", suffix="-userdata",
|
||||||
|
dir=scratchdir, delete=False)
|
||||||
|
filename = fileobj.name
|
||||||
|
|
||||||
|
with open(filename, "w+") as f:
|
||||||
|
f.write(content)
|
||||||
|
return filename
|
|
@ -16,6 +16,7 @@ from ..devices import DeviceDisk
|
||||||
from ..osdict import OSDB
|
from ..osdict import OSDB
|
||||||
from ..logger import log
|
from ..logger import log
|
||||||
from .. import progress
|
from .. import progress
|
||||||
|
from .cloudinit import create_metadata, create_userdata
|
||||||
|
|
||||||
|
|
||||||
def _make_testsuite_path(path):
|
def _make_testsuite_path(path):
|
||||||
|
@ -60,6 +61,7 @@ class Installer(object):
|
||||||
self._tmpfiles = []
|
self._tmpfiles = []
|
||||||
self._defaults_are_set = False
|
self._defaults_are_set = False
|
||||||
self._unattended_data = None
|
self._unattended_data = None
|
||||||
|
self._cloudinit_data = None
|
||||||
|
|
||||||
self._install_bootdev = install_bootdev
|
self._install_bootdev = install_bootdev
|
||||||
self._no_install = no_install
|
self._no_install = no_install
|
||||||
|
@ -279,6 +281,9 @@ class Installer(object):
|
||||||
elif unattended_scripts:
|
elif unattended_scripts:
|
||||||
self._prepare_unattended_data(guest, meter, unattended_scripts)
|
self._prepare_unattended_data(guest, meter, unattended_scripts)
|
||||||
|
|
||||||
|
elif self._cloudinit_data:
|
||||||
|
self._install_cloudinit(guest)
|
||||||
|
|
||||||
def _cleanup(self, guest):
|
def _cleanup(self, guest):
|
||||||
if self._treemedia:
|
if self._treemedia:
|
||||||
self._treemedia.cleanup(guest)
|
self._treemedia.cleanup(guest)
|
||||||
|
@ -414,6 +419,18 @@ class Installer(object):
|
||||||
def set_unattended_data(self, unattended_data):
|
def set_unattended_data(self, unattended_data):
|
||||||
self._unattended_data = unattended_data
|
self._unattended_data = unattended_data
|
||||||
|
|
||||||
|
def set_cloudinit_data(self, cloudinit_data):
|
||||||
|
self._cloudinit_data = cloudinit_data
|
||||||
|
|
||||||
|
def _install_cloudinit(self, guest):
|
||||||
|
metadata = create_metadata(guest.conn.get_app_cache_dir())
|
||||||
|
userdata = create_userdata(guest.conn.get_app_cache_dir(), self._cloudinit_data)
|
||||||
|
|
||||||
|
iso = perform_cdrom_injections([(metadata, "meta-data"), (userdata, "user-data")],
|
||||||
|
guest.conn.get_app_cache_dir(), cloudinit=True)
|
||||||
|
self._tmpfiles.append(iso)
|
||||||
|
self._add_unattended_install_cdrom_device(guest, iso)
|
||||||
|
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# guest install handling #
|
# guest install handling #
|
||||||
|
|
|
@ -44,19 +44,21 @@ def _run_initrd_commands(initrd, tempdir):
|
||||||
log.debug("gzip stderr=%s", gziperr)
|
log.debug("gzip stderr=%s", gziperr)
|
||||||
|
|
||||||
|
|
||||||
def _run_iso_commands(iso, tempdir):
|
def _run_iso_commands(iso, tempdir, cloudinit=False):
|
||||||
cmd = ["genisoimage",
|
cmd = ["genisoimage",
|
||||||
"-o", iso,
|
"-o", iso,
|
||||||
"-J",
|
"-J",
|
||||||
"-input-charset", "utf8",
|
"-input-charset", "utf8",
|
||||||
"-rational-rock",
|
"-rational-rock"]
|
||||||
tempdir]
|
if cloudinit:
|
||||||
|
cmd.extend(["-V", "cidata"])
|
||||||
|
cmd.append(tempdir)
|
||||||
log.debug("Running iso build command: %s", cmd)
|
log.debug("Running iso build command: %s", cmd)
|
||||||
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||||
log.debug("cmd output: %s", output)
|
log.debug("cmd output: %s", output)
|
||||||
|
|
||||||
|
|
||||||
def _perform_generic_injections(injections, scratchdir, media, cb):
|
def _perform_generic_injections(injections, scratchdir, media, cb, cloudinit=False):
|
||||||
if not injections:
|
if not injections:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -74,20 +76,20 @@ def _perform_generic_injections(injections, scratchdir, media, cb):
|
||||||
filename, dst, media)
|
filename, dst, media)
|
||||||
shutil.copy(filename, os.path.join(tempdir, dst))
|
shutil.copy(filename, os.path.join(tempdir, dst))
|
||||||
|
|
||||||
return cb(media, tempdir)
|
return cb(media, tempdir, cloudinit)
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(tempdir)
|
shutil.rmtree(tempdir)
|
||||||
|
|
||||||
|
|
||||||
def perform_initrd_injections(initrd, injections, scratchdir):
|
def perform_initrd_injections(initrd, injections, scratchdir, cloudinit=False):
|
||||||
"""
|
"""
|
||||||
Insert files into the root directory of the initial ram disk
|
Insert files into the root directory of the initial ram disk
|
||||||
"""
|
"""
|
||||||
_perform_generic_injections(injections, scratchdir, initrd,
|
_perform_generic_injections(injections, scratchdir, initrd,
|
||||||
_run_initrd_commands)
|
_run_initrd_commands, cloudinit)
|
||||||
|
|
||||||
|
|
||||||
def perform_cdrom_injections(injections, scratchdir):
|
def perform_cdrom_injections(injections, scratchdir, cloudinit=False):
|
||||||
"""
|
"""
|
||||||
Insert files into the root directory of a generated cdrom
|
Insert files into the root directory of a generated cdrom
|
||||||
"""
|
"""
|
||||||
|
@ -98,7 +100,7 @@ def perform_cdrom_injections(injections, scratchdir):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_perform_generic_injections(injections, scratchdir, iso,
|
_perform_generic_injections(injections, scratchdir, iso,
|
||||||
_run_iso_commands)
|
_run_iso_commands, cloudinit)
|
||||||
except Exception: # pragma: no cover
|
except Exception: # pragma: no cover
|
||||||
os.unlink(iso)
|
os.unlink(iso)
|
||||||
raise
|
raise
|
||||||
|
|
Loading…
Reference in New Issue