Simone Tiraboschi has uploaded a new change for review.

Change subject: WIP: packaging: setup: let the user configure engine VM 
networking
......................................................................

WIP: packaging: setup: let the user configure engine VM networking

Let the user specify a static configuration for the engine VM
network configuration.
The configuration will be applyed via cloud-init.

Change-Id: Ic09f4d92e44f1bf810f06ada703e533b8c5e2061
Bug-Url: https://bugzilla.redhat.com/1223801
Signed-off-by: Simone Tiraboschi <stira...@redhat.com>
---
M ovirt-hosted-engine-setup.spec.in
M src/ovirt_hosted_engine_setup/constants.py
M src/plugins/ovirt-hosted-engine-setup/network/gateway.py
M src/plugins/ovirt-hosted-engine-setup/vm/cloud_init.py
4 files changed, 187 insertions(+), 3 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-hosted-engine-setup 
refs/changes/57/41357/1

diff --git a/ovirt-hosted-engine-setup.spec.in 
b/ovirt-hosted-engine-setup.spec.in
index 318757a..9e6c78e 100644
--- a/ovirt-hosted-engine-setup.spec.in
+++ b/ovirt-hosted-engine-setup.spec.in
@@ -46,6 +46,7 @@
 Requires:       python
 Requires:       python-ethtool >= 0.6-3
 Requires:       python-paramiko
+Requires:       python-netaddr
 Requires:       sanlock >= 2.8
 Requires:       sanlock-python >= 2.8
 Requires:       sudo
diff --git a/src/ovirt_hosted_engine_setup/constants.py 
b/src/ovirt_hosted_engine_setup/constants.py
index c6195d6..f4a775b 100644
--- a/src/ovirt_hosted_engine_setup/constants.py
+++ b/src/ovirt_hosted_engine_setup/constants.py
@@ -721,6 +721,24 @@
     @ohostedattrs(
         answerfile=True,
     )
+    def CLOUD_INIT_VM_STATIC_CIDR(self):
+        return 'OVEHOSTED_VM/cloudinitVMStaticCIDR'
+
+    @ohostedattrs(
+        answerfile=True,
+    )
+    def CLOUD_INIT_VM_DNS1(self):
+        return 'OVEHOSTED_VM/cloudinitVMDNS1'
+
+    @ohostedattrs(
+        answerfile=True,
+    )
+    def CLOUD_INIT_VM_DNS2(self):
+        return 'OVEHOSTED_VM/cloudinitVMDNS2'
+
+    @ohostedattrs(
+        answerfile=True,
+    )
     def CDROM_UUID(self):
         return 'OVEHOSTED_VM/cdromUUID'
 
@@ -853,6 +871,7 @@
     CONFIG_STORAGE_LATE = 'ohosted.storage.configuration.late'
     CONFIG_STORAGE_BLOCKD = 'ohosted.storage.blockd.configuration.available'
     CONFIG_STORAGE_NFS = 'ohosted.storage.nfs.configuration.available'
+    CONFIG_GATEWAY = 'ohosted.networking.gateway.configuration.available'
     GLUSTER_PROVISIONING = 'ohosted.storage.gluster.provisioned'
     CONFIG_ADDITIONAL_HOST = 'ohosted.core.additional.host'
     CONFIG_CLOUD_INIT_OPTIONS = 'ohosted.boot.configuration.cloud_init_options'
diff --git a/src/plugins/ovirt-hosted-engine-setup/network/gateway.py 
b/src/plugins/ovirt-hosted-engine-setup/network/gateway.py
index e687d72..25024f4 100644
--- a/src/plugins/ovirt-hosted-engine-setup/network/gateway.py
+++ b/src/plugins/ovirt-hosted-engine-setup/network/gateway.py
@@ -106,6 +106,7 @@
         before=(
             ohostedcons.Stages.DIALOG_TITLES_E_NETWORK,
         ),
+        name=ohostedcons.Stages.CONFIG_GATEWAY,
     )
     def _customization(self):
 
diff --git a/src/plugins/ovirt-hosted-engine-setup/vm/cloud_init.py 
b/src/plugins/ovirt-hosted-engine-setup/vm/cloud_init.py
index 8f84468..2170c78 100644
--- a/src/plugins/ovirt-hosted-engine-setup/vm/cloud_init.py
+++ b/src/plugins/ovirt-hosted-engine-setup/vm/cloud_init.py
@@ -29,6 +29,8 @@
 import shutil
 import tempfile
 
+import netaddr
+
 from otopi import constants as otopicons
 from otopi import plugin
 from otopi import util
@@ -52,6 +54,30 @@
         self._enable = False
         self._directory_name = None
 
+    def _validate_ip_cidr(self, ipcidr):
+        try:
+            ip = netaddr.IPNetwork(ipcidr)
+            if not (
+                ip.ip and
+                ip.ip.is_unicast() and
+                not ip.ip.is_loopback()
+            ):
+                return None
+            if len(list(ip)) > 1:
+                if not (
+                    ip.network and
+                    ip.ip != ip.network and
+                    ip.broadcast and
+                    ip.ip != ip.broadcast
+                ):
+                    return None
+        except netaddr.AddrFormatError:
+            return None
+        return ip
+
+    def _validate_ip(self, ip):
+        return self._validate_ip_cidr(ip + '/32')
+
     @plugin.event(
         stage=plugin.Stages.STAGE_INIT,
     )
@@ -69,6 +95,10 @@
         )
         self.environment.setdefault(
             ohostedcons.VMEnv.CLOUD_INIT_INSTANCE_HOSTNAME,
+            None
+        )
+        self.environment.setdefault(
+            ohostedcons.VMEnv.CLOUD_INIT_EXECUTE_ESETUP,
             None
         )
         self.environment.setdefault(
@@ -109,6 +139,9 @@
             self.environment[ohostedcons.VMEnv.CLOUD_INIT_ROOTPWD],
             self.environment[ohostedcons.VMEnv.CLOUD_INIT_INSTANCE_HOSTNAME],
             self.environment[ohostedcons.VMEnv.CLOUD_INIT_EXECUTE_ESETUP],
+            self.environment[ohostedcons.VMEnv.CLOUD_INIT_VM_STATIC_CIDR],
+            self.environment[ohostedcons.VMEnv.CLOUD_INIT_VM_DNS1],
+            self.environment[ohostedcons.VMEnv.CLOUD_INIT_VM_DNS1],
         ]) == set([None])
 
         if interactive:
@@ -208,6 +241,8 @@
                             ohostedcons.VMEnv.CLOUD_INIT_ROOTPWD
                         ] = False
 
+                self._configure_vm_networking()
+
                 self.environment[
                     ohostedcons.VMEnv.CLOUD_INIT_EXECUTE_ESETUP
                 ] = self.dialog.queryString(
@@ -252,12 +287,116 @@
             ] or
             self.environment[
                 ohostedcons.VMEnv.CLOUD_INIT_EXECUTE_ESETUP
+            ] or
+            self.environment[
+                ohostedcons.VMEnv.CLOUD_INIT_VM_STATIC_CIDR
+            ] or
+            self.environment[
+                ohostedcons.VMEnv.CLOUD_INIT_VM_DNS1
+            ] or
+            self.environment[
+                ohostedcons.VMEnv.CLOUD_INIT_VM_DNS2
             ]
         ):
             self.environment[
                 ohostedcons.VMEnv.GENERATE_CLOUD_INIT_ISO
             ] = ohostedcons.Const.CLOUD_INIT_GENERATE
             self._enable = True
+
+    @plugin.event(
+        stage=plugin.Stages.STAGE_CUSTOMIZATION,
+        after=(
+                ohostedcons.Stages.DIALOG_TITLES_S_NETWORK,
+                ohostedcons.Stages.CONFIG_GATEWAY,
+        ),
+        before=(
+                ohostedcons.Stages.DIALOG_TITLES_E_NETWORK,
+        ),
+        condition=lambda self: self._enable,
+        name=ohostedcons.Stages.CONFIG_CLOUD_INIT_VM_NETWORKING,
+    )
+    def _customize_vm_networking(self):
+        interactive = self.environment[
+            ohostedcons.VMEnv.CLOUD_INIT_VM_STATIC_CIDR
+        ] is None
+
+        if interactive:
+            while self.environment[
+                ohostedcons.VMEnv.CLOUD_INIT_VM_STATIC_CIDR
+            ] is None:
+                static = self.dialog.queryString(
+                    name='CI_VM_STATIC_NETWORKING',
+                    note=_(
+                        'Rely on DHCP to configure the network for the '
+                        'engine VM?\n'
+                        '(@VALUES@)[@DEFAULT@]? '
+                    ),
+                    prompt=True,
+                    validValues=(_('DHCP'), _('Static')),
+                    caseSensitive=False,
+                    default=_('DHCP')
+                ) == _('Static').lower()
+                if static:  # Static
+                    # TODO: construct a proper one based on the host one!
+                    default_cidr = '192.168.1.100/24'
+                    ipcidr = self.dialog.queryString(
+                        name='CI_CIDR',
+                        note=_(
+                            'Please enter the IP address/prefix len '
+                            'to be used for the engine VM\n'
+                            '[@DEFAULT@] '
+                        ),
+                        prompt=True,
+                        caseSensitive=False,
+                        default=default_cidr,
+                    )
+                    valid_ip = self._validate_ip_cidr(ipcidr)
+                    if not valid_ip:
+                        self.logger.error(
+                            _(
+                                "'{ipcidr}' is not a valid CIDR "
+                                "IP address"
+                            ).format(
+                                ipcidr=ipcidr
+                            )
+                        )
+                        break
+
+                    self.environment[
+                        ohostedcons.VMEnv.CLOUD_INIT_VM_CIDR
+                    ] = ipcidr
+
+                # TODO: validate that the gateway will be in the same subnet
+                # TODO: ask for DNS
+                # TODO: ask if the user want to push host hostname under
+                # /etc/hosts on the VM
+
+                else:  # DHCP
+                    self.environment[
+                        ohostedcons.VMEnv.CLOUD_INIT_VM_CIDR
+                    ] = False
+        else:
+            if self.environment[
+                ohostedcons.VMEnv.CLOUD_INIT_VM_CIDR
+            ]:
+                valid_ip = self._validate_ip_cidr(
+                    self.environment[
+                        ohostedcons.VMEnv.CLOUD_INIT_VM_CIDR
+                    ]
+                )
+                if not valid_ip:
+                    msg = _(
+                        "'{ipcidr}' is not a valid CIDR "
+                        "IP address"
+                    ).format(
+                        ipcidr=self.environment[
+                            ohostedcons.VMEnv.CLOUD_INIT_VM_CIDR
+                        ]
+                    )
+                    self.logger.error(
+                        msg
+                    )
+                    raise RuntimeError(msg)
 
     @plugin.event(
         stage=plugin.Stages.STAGE_MISC,
@@ -336,11 +475,12 @@
         f.write(user_data)
         f.close()
 
-        meta_data = ''
+        meta_data = 'instance-id: {instance}\n'.format(
+            instance=self.environment[ohostedcons.VMEnv.VM_UUID],
+        )
         f_meta_data = os.path.join(self._directory_name, 'meta-data')
         if self.environment[ohostedcons.VMEnv.CLOUD_INIT_INSTANCE_HOSTNAME]:
-            meta_data = (
-                'instance-id: {instance}\n'
+            meta_data += (
                 'local-hostname: {hostname}\n'
             ).format(
                 instance=self.environment[
@@ -350,6 +490,29 @@
                     ohostedcons.VMEnv.CLOUD_INIT_INSTANCE_HOSTNAME
                 ],
             )
+
+        if self.environment[ohostedcons.VMEnv.CLOUD_INIT_VM_STATIC_CIDR]:
+            ip = netaddr.IPNetwork(
+                self.environment[ohostedcons.VMEnv.CLOUD_INIT_VM_STATIC_CIDR]
+            )
+            meta_data += (
+                'network-interfaces: |\n'
+                '  iface eth0 inet static\n'
+                '  address {ip_addr}\n'
+                '  network {network}\n'
+                '  netmask {netmask}\n'
+                '  broadcast {broadcast}\n'
+                '  gateway {gateway}\n'
+            ).format(
+                ip_addr=ip.ip,
+                network=ip.network,
+                netmask=ip.netmask,
+                broadcast=ip.broadcast,
+                gateway=self.environment[
+                    ohostedcons.NetworkEnv.GATEWAY
+                ],
+            )
+
         f = open(f_meta_data, 'w')
         f.write(meta_data)
         f.close()


-- 
To view, visit https://gerrit.ovirt.org/41357
To unsubscribe, visit https://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic09f4d92e44f1bf810f06ada703e533b8c5e2061
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-hosted-engine-setup
Gerrit-Branch: master
Gerrit-Owner: Simone Tiraboschi <stira...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to