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)
|
||||
if options.autostart:
|
||||
installer.autostart = True
|
||||
if options.cloud_init:
|
||||
cloudinit_data = cli.parse_cloud_init(options.cloud_init)
|
||||
installer.set_cloudinit_data(cloudinit_data)
|
||||
|
||||
return installer
|
||||
|
||||
|
@ -842,6 +845,8 @@ def parse_args():
|
|||
help=_("Perform an unattended installation"))
|
||||
insg.add_argument("--install",
|
||||
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
|
||||
insg.add_argument("--test-media-detection", help=argparse.SUPPRESS)
|
||||
|
|
|
@ -28,6 +28,7 @@ from .nodedev import NodeDevice
|
|||
from .osdict import OSDB
|
||||
from .storage import StoragePool, StorageVolume
|
||||
from .install.unattended import UnattendedData
|
||||
from .install.cloudinit import CloudInitData
|
||||
|
||||
|
||||
HAS_VIRTVIEWER = shutil.which("virt-viewer")
|
||||
|
@ -467,7 +468,7 @@ def get_meter():
|
|||
|
||||
def _get_completer_parsers():
|
||||
return VIRT_PARSERS + [ParserCheck, ParserLocation,
|
||||
ParserUnattended, ParserInstall]
|
||||
ParserUnattended, ParserInstall, ParserCloudInit]
|
||||
|
||||
|
||||
def _virtparser_completer(prefix, **kwargs):
|
||||
|
@ -1614,6 +1615,31 @@ def parse_install(optstr):
|
|||
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 #
|
||||
######################
|
||||
|
|
|
@ -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 ..logger import log
|
||||
from .. import progress
|
||||
from .cloudinit import create_metadata, create_userdata
|
||||
|
||||
|
||||
def _make_testsuite_path(path):
|
||||
|
@ -60,6 +61,7 @@ class Installer(object):
|
|||
self._tmpfiles = []
|
||||
self._defaults_are_set = False
|
||||
self._unattended_data = None
|
||||
self._cloudinit_data = None
|
||||
|
||||
self._install_bootdev = install_bootdev
|
||||
self._no_install = no_install
|
||||
|
@ -279,6 +281,9 @@ class Installer(object):
|
|||
elif unattended_scripts:
|
||||
self._prepare_unattended_data(guest, meter, unattended_scripts)
|
||||
|
||||
elif self._cloudinit_data:
|
||||
self._install_cloudinit(guest)
|
||||
|
||||
def _cleanup(self, guest):
|
||||
if self._treemedia:
|
||||
self._treemedia.cleanup(guest)
|
||||
|
@ -414,6 +419,18 @@ class Installer(object):
|
|||
def set_unattended_data(self, 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 #
|
||||
|
|
|
@ -44,19 +44,21 @@ def _run_initrd_commands(initrd, tempdir):
|
|||
log.debug("gzip stderr=%s", gziperr)
|
||||
|
||||
|
||||
def _run_iso_commands(iso, tempdir):
|
||||
def _run_iso_commands(iso, tempdir, cloudinit=False):
|
||||
cmd = ["genisoimage",
|
||||
"-o", iso,
|
||||
"-J",
|
||||
"-input-charset", "utf8",
|
||||
"-rational-rock",
|
||||
tempdir]
|
||||
"-rational-rock"]
|
||||
if cloudinit:
|
||||
cmd.extend(["-V", "cidata"])
|
||||
cmd.append(tempdir)
|
||||
log.debug("Running iso build command: %s", cmd)
|
||||
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
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:
|
||||
return
|
||||
|
||||
|
@ -74,20 +76,20 @@ def _perform_generic_injections(injections, scratchdir, media, cb):
|
|||
filename, dst, media)
|
||||
shutil.copy(filename, os.path.join(tempdir, dst))
|
||||
|
||||
return cb(media, tempdir)
|
||||
return cb(media, tempdir, cloudinit)
|
||||
finally:
|
||||
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
|
||||
"""
|
||||
_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
|
||||
"""
|
||||
|
@ -98,7 +100,7 @@ def perform_cdrom_injections(injections, scratchdir):
|
|||
|
||||
try:
|
||||
_perform_generic_injections(injections, scratchdir, iso,
|
||||
_run_iso_commands)
|
||||
_run_iso_commands, cloudinit)
|
||||
except Exception: # pragma: no cover
|
||||
os.unlink(iso)
|
||||
raise
|
||||
|
|
Loading…
Reference in New Issue