[virt-tools-list] [virt-manager PATCH v3] Introduction of cloud-init configuration in virt-install
Athina Plaskasoviti
athina.plaskasoviti at gmail.com
Fri Jun 28 16:05:18 UTC 2019
Usage:
--cloud-init
Signed-off-by: Athina Plaskasoviti <athina.plaskasoviti at gmail.com>
---
virt-install | 5 +++
virtinst/cli.py | 25 +++++++++++++
virtinst/install/cloudinit.py | 57 +++++++++++++++++++++++++++++
virtinst/install/installer.py | 17 +++++++++
virtinst/install/installerinject.py | 20 +++++-----
5 files changed, 115 insertions(+), 9 deletions(-)
create mode 100644 virtinst/install/cloudinit.py
diff --git a/virt-install b/virt-install
index ee2b9006..1ccf7a31 100755
--- a/virt-install
+++ b/virt-install
@@ -444,6 +444,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
@@ -821,6 +824,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)
diff --git a/virtinst/cli.py b/virtinst/cli.py
index 9a1fe2f6..9a6badcd 100644
--- a/virtinst/cli.py
+++ b/virtinst/cli.py
@@ -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
##########################
@@ -1602,6 +1603,30 @@ def parse_install(optstr):
return installdata
+########################
+# --cloud-init parsing #
+########################
+
+class ParserCloudInit(VirtCLIParser):
+ cli_arg_name = "cloud-init"
+
+ @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 #
######################
diff --git a/virtinst/install/cloudinit.py b/virtinst/install/cloudinit.py
new file mode 100644
index 00000000..41667f4b
--- /dev/null
+++ b/virtinst/install/cloudinit.py
@@ -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
diff --git a/virtinst/install/installer.py b/virtinst/install/installer.py
index a5c7a708..7b8ba9bd 100644
--- a/virtinst/install/installer.py
+++ b/virtinst/install/installer.py
@@ -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
@@ -266,6 +268,9 @@ class Installer(object):
elif unattended_script:
self._prepare_unattended_data(guest, unattended_script)
+ elif self._cloudinit_data:
+ self._install_cloudinit(guest)
+
def _cleanup(self, guest):
if self._treemedia:
self._treemedia.cleanup(guest)
@@ -401,6 +406,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 #
diff --git a/virtinst/install/installerinject.py b/virtinst/install/installerinject.py
index d7cfcfb4..7ad4833f 100644
--- a/virtinst/install/installerinject.py
+++ b/virtinst/install/installerinject.py
@@ -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
--
2.20.1
More information about the virt-tools-list
mailing list