Alon Bar-Lev has uploaded a new change for review.

Change subject: packaging: setup: domain validation changes
......................................................................

packaging: setup: domain validation changes

1. perform reverse lookup only if AIO plugin is installed, as it is not
   required unless libvirt is trying to perform migration.

2. allow names without suffix, warn in this case.

3. split out hostname logic from protocols.

Change-Id: I7276d3e9088fd370458846bfdcee7166331935e4
Signed-off-by: Alon Bar-Lev <[email protected]>
---
M packaging/setup/ovirt_engine_setup/constants.py
M packaging/setup/plugins/ovirt-engine-setup/all-in-one/core.py
M packaging/setup/plugins/ovirt-engine-setup/config/__init__.py
A packaging/setup/plugins/ovirt-engine-setup/config/hostname.py
M packaging/setup/plugins/ovirt-engine-setup/config/protocols.py
5 files changed, 362 insertions(+), 294 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/89/14889/1

diff --git a/packaging/setup/ovirt_engine_setup/constants.py 
b/packaging/setup/ovirt_engine_setup/constants.py
index ae03d67..9416f84 100644
--- a/packaging/setup/ovirt_engine_setup/constants.py
+++ b/packaging/setup/ovirt_engine_setup/constants.py
@@ -723,6 +723,7 @@
         return 'OVESETUP_CONFIG/firewallManager'
 
     FQDN_REVERSE_VALIDATION = 'OVESETUP_CONFIG/fqdnReverseValidation'
+    FQDN_NON_LOOPBACK_VALIDATION = 'OVESETUP_CONFIG/fqdnNonLoopback'
 
 
 @util.export
diff --git a/packaging/setup/plugins/ovirt-engine-setup/all-in-one/core.py 
b/packaging/setup/plugins/ovirt-engine-setup/all-in-one/core.py
index fa1b404..6fe201a 100644
--- a/packaging/setup/plugins/ovirt-engine-setup/all-in-one/core.py
+++ b/packaging/setup/plugins/ovirt-engine-setup/all-in-one/core.py
@@ -94,6 +94,9 @@
             self.environment[
                 osetupcons.ConfigEnv.FQDN_REVERSE_VALIDATION
             ] = True
+            self.environment[
+                osetupcons.ConfigEnv.FQDN_NON_LOOPBACK_VALIDATION
+            ] = True
 
     @plugin.event(
         stage=plugin.Stages.STAGE_MISC,
diff --git a/packaging/setup/plugins/ovirt-engine-setup/config/__init__.py 
b/packaging/setup/plugins/ovirt-engine-setup/config/__init__.py
index 6d7404b..9c28248 100644
--- a/packaging/setup/plugins/ovirt-engine-setup/config/__init__.py
+++ b/packaging/setup/plugins/ovirt-engine-setup/config/__init__.py
@@ -26,6 +26,7 @@
 from . import jboss
 from . import database
 from . import protocols
+from . import hostname
 from . import appmode
 from . import domain_type
 from . import ca
@@ -41,6 +42,7 @@
     jboss.Plugin(context=context)
     database.Plugin(context=context)
     protocols.Plugin(context=context)
+    hostname.Plugin(context=context)
     appmode.Plugin(context=context)
     domain_type.Plugin(context=context)
     ca.Plugin(context=context)
diff --git a/packaging/setup/plugins/ovirt-engine-setup/config/hostname.py 
b/packaging/setup/plugins/ovirt-engine-setup/config/hostname.py
new file mode 100644
index 0000000..6b48d02
--- /dev/null
+++ b/packaging/setup/plugins/ovirt-engine-setup/config/hostname.py
@@ -0,0 +1,356 @@
+#
+# ovirt-engine-setup -- ovirt engine setup
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+"""Hostname plugin."""
+
+
+import re
+import socket
+import gettext
+_ = lambda m: gettext.dgettext(message=m, domain='ovirt-engine-setup')
+
+
+from otopi import util
+from otopi import plugin
+
+
+from ovirt_engine_setup import constants as osetupcons
+
+
[email protected]
+class Plugin(plugin.PluginBase):
+    """Protocols plugin."""
+    _IPADDR_RE = re.compile(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
+
+    _DOMAIN_RE = re.compile(
+        flags=re.VERBOSE,
+        pattern=r"""
+            ^
+            [\w\.\-\_]+
+            \w+
+            $
+        """
+    )
+
+    _INTERFACE_RE = re.compile(
+        flags=re.VERBOSE,
+        pattern=r"""
+            ^
+            \d+
+            :
+            \s+
+            (?P<interface>\w+)
+            :
+            \s+
+            <(?P<options>[^>]+)
+            .*
+        """
+    )
+
+    _ADDRESS_RE = re.compile(
+        flags=re.VERBOSE,
+        pattern=r"""
+            \s+
+            inet
+            \s
+            (?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})
+            .+
+            \s+
+            (?P<interface>\w+)
+            $
+    """
+    )
+
+    _DIG_LOOKUP_RE = re.compile(
+        flags=re.VERBOSE,
+        pattern=r"""
+            ^
+            [\w.-]+
+            \s+
+            \d+
+            \s+
+            IN
+            \s+
+            (A|CNAME)
+            \s+
+            [\w.-]+
+        """
+    )
+
+    _DIG_REVLOOKUP_RE = re.compile(
+        flags=re.VERBOSE,
+        pattern=r"""
+            ^
+            (?P<query>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).in-addr.arpa.
+            \s+
+            \d+
+            \s+
+            IN
+            \s+
+            PTR
+            \s+
+            (?P<answer>[\w.-]+)
+            \.
+            $
+        """
+    )
+
+    def __init__(self, context):
+        super(Plugin, self).__init__(context=context)
+
+    def _getNonLoopbackAddresses(self):
+        interfaces = {}
+        addresses = {}
+        rc, stdout, stderr = self.execute(
+            args=(
+                self.command.get('ip'),
+                'addr',
+            ),
+        )
+        for line in stdout:
+            interfacematch = self._INTERFACE_RE.match(line)
+            addressmatch = self._ADDRESS_RE.match(line)
+            if interfacematch is not None:
+                interfaces[
+                    interfacematch.group('interface')
+                ] = 'LOOPBACK' in interfacematch.group('options')
+            elif addressmatch is not None:
+                addresses.setdefault(
+                    addressmatch.group('interface'),
+                    []
+                ).append(
+                    addressmatch.group('address')
+                )
+        iplist = []
+        for interface, loopback in interfaces.iteritems():
+            if not loopback:
+                iplist.extend(addresses.get(interface, []))
+
+        self.logger.debug('addresses: %s' % iplist)
+        return set(iplist)
+
+    def _resolvedByDNS(self, fqdn):
+        args = [
+            self.command.get('dig'),
+            fqdn
+        ]
+        rc, stdout, stderr = self.execute(
+            args=args,
+            raiseOnError=False
+        )
+        resolved = False
+        if rc == 0:
+            for line in stdout:
+                if self._DIG_LOOKUP_RE.search(line):
+                    resolved = True
+        return resolved
+
+    def _dig_reverse_lookup(self, addr):
+        names = set()
+        args = [
+            self.command.get('dig'),
+            '-x',
+            addr,
+        ]
+        rc, stdout, stderr = self.execute(
+            args=args,
+            raiseOnError=False
+        )
+        if rc == 0:
+            for line in stdout:
+                found = self._DIG_REVLOOKUP_RE.search(line)
+                if found:
+                    names.add(found.group('answer'))
+        return names
+
+    def _validateFQDNresolvability(self, fqdn):
+
+        try:
+            resolvedAddresses = set([
+                address[0] for __, __, __, __, address in
+                socket.getaddrinfo(
+                    fqdn,
+                    None
+                )
+            ])
+            self.logger.debug(
+                '{fqdn} resolves to: {addresses}'.format(
+                    fqdn=fqdn,
+                    addresses=resolvedAddresses,
+                )
+            )
+            resolvedAddressesAsString = ' '.join(resolvedAddresses)
+        except socket.error:
+            raise RuntimeError(
+                _('{fqdn} did not resolve into an IP address').format(
+                    fqdn=fqdn,
+                )
+            )
+
+        resolvedByDNS = self._resolvedByDNS(fqdn)
+        if not resolvedByDNS:
+            self.logger.warning(
+                _(
+                    'Failed to resolve {fqdn} using DNS, '
+                    'it can be resolved only locally'
+                ).format(
+                    fqdn=fqdn,
+                )
+            )
+        elif self.environment[osetupcons.ConfigEnv.FQDN_REVERSE_VALIDATION]:
+            revResolved = False
+            for address in resolvedAddresses:
+                for name in self._dig_reverse_lookup(address):
+                    revResolved = name.lower() == fqdn.lower()
+                    if revResolved:
+                        break
+                if revResolved:
+                    break
+            if not revResolved:
+                raise RuntimeError(
+                    _(
+                        'The following addresses: {addresses} did not reverse'
+                        'resolve into {fqdn}'
+                    ).format(
+                        addresses=resolvedAddressesAsString,
+                        fqdn=fqdn
+                    )
+                )
+
+        if self.environment[
+            osetupcons.ConfigEnv.FQDN_NON_LOOPBACK_VALIDATION
+        ]:
+            if not resolvedAddresses.issubset(self._getNonLoopbackAddresses()):
+                raise RuntimeError(
+                    _(
+                        'The following addreses: '
+                        "{addresses} can't be mapped "
+                        'to non loopback devices on this host'
+                    ).format(
+                        addresses=resolvedAddressesAsString
+                    )
+                )
+
+    def _validateFQDN(self, fqdn):
+        if self._IPADDR_RE.match(fqdn):
+            raise RuntimeError(
+                _(
+                    '{fqdn} is an IP address and not a FQDN. '
+                    'A FQDN is needed to be able to generate '
+                    'certificates correctly.'
+                ).format(
+                    fqdn=fqdn,
+                )
+            )
+
+        if not fqdn:
+            raise RuntimeError(
+                _('Please specify host FQDN')
+            )
+
+        if len(fqdn) > 1000:
+            raise RuntimeError(
+                _('FQDN has invalid length')
+            )
+
+        components = fqdn.split('.', 1)
+        if len(components) == 1 or not components[0]:
+            self.logger.warning(
+                _('Host name {fqdn} has no domain suffix').format(
+                    fqdn=fqdn,
+                )
+            )
+        else:
+            if not self._DOMAIN_RE.match(components[1]):
+                raise RuntimeError(
+                    _('Host name {fqdn} has invalid domain name').format(
+                        fqdn=fqdn,
+                    )
+                )
+
+    @plugin.event(
+        stage=plugin.Stages.STAGE_INIT,
+    )
+    def _init(self):
+        self.environment.setdefault(
+            osetupcons.ConfigEnv.FQDN,
+            None
+        )
+        self.environment.setdefault(
+            osetupcons.ConfigEnv.FQDN_REVERSE_VALIDATION,
+            False
+        )
+        self.environment.setdefault(
+            osetupcons.ConfigEnv.FQDN_NON_LOOPBACK_VALIDATION,
+            False
+        )
+
+    @plugin.event(
+        stage=plugin.Stages.STAGE_SETUP,
+    )
+    def _setup(self):
+        self.command.detect('ip')
+        self.command.detect('dig')
+
+    @plugin.event(
+        stage=plugin.Stages.STAGE_CUSTOMIZATION,
+        name=osetupcons.Stages.CONFIG_PROTOCOLS_CUSTOMIZATION,
+        before=[
+            osetupcons.Stages.DIALOG_TITLES_E_NETWORK,
+        ],
+        after=[
+            osetupcons.Stages.DIALOG_TITLES_S_NETWORK,
+        ],
+    )
+    def _customization(self):
+        interactive = self.environment[osetupcons.ConfigEnv.FQDN] is None
+        validFQDN = False
+        while not validFQDN:
+            if interactive:
+                fqdn = socket.getfqdn()
+                self.environment[
+                    osetupcons.ConfigEnv.FQDN
+                ] = self.dialog.queryString(
+                    name='OVESETUP_NETWORK_FQDN',
+                    note=_(
+                        'Host fully qualified DNS name of '
+                        'this server [@DEFAULT@]: '
+                    ),
+                    prompt=True,
+                    default=fqdn,
+                )
+            try:
+                self._validateFQDN(
+                    self.environment[osetupcons.ConfigEnv.FQDN]
+                )
+                self._validateFQDNresolvability(
+                    self.environment[osetupcons.ConfigEnv.FQDN]
+                )
+                validFQDN = True
+            except RuntimeError as e:
+                if interactive:
+                    self.logger.error(
+                        _('FQDN is not valid: {error}').format(
+                            error=e
+                        )
+                    )
+                else:
+                    raise
+
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/packaging/setup/plugins/ovirt-engine-setup/config/protocols.py 
b/packaging/setup/plugins/ovirt-engine-setup/config/protocols.py
index d7b0799..4a9444c 100644
--- a/packaging/setup/plugins/ovirt-engine-setup/config/protocols.py
+++ b/packaging/setup/plugins/ovirt-engine-setup/config/protocols.py
@@ -19,8 +19,6 @@
 """Protocols plugin."""
 
 
-import re
-import socket
 import gettext
 _ = lambda m: gettext.dgettext(message=m, domain='ovirt-engine-setup')
 
@@ -37,262 +35,14 @@
 @util.export
 class Plugin(plugin.PluginBase):
     """Protocols plugin."""
-    _IPADDR_RE = re.compile(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
-
-    _DOMAIN_RE = re.compile(
-        flags=re.VERBOSE,
-        pattern=r"""
-            ^
-            [\w\-\_]+
-            \.
-            [\w\.\-\_]+
-            \w+
-            $
-        """
-    )
-
-    _INTERFACE_RE = re.compile(
-        flags=re.VERBOSE,
-        pattern=r"""
-            ^
-            \d+
-            :
-            \s+
-            (?P<interface>\w+)
-            :
-            \s+
-            <(?P<options>[^>]+)
-            .*
-        """
-    )
-
-    _ADDRESS_RE = re.compile(
-        flags=re.VERBOSE,
-        pattern=r"""
-            \s+
-            inet
-            \s
-            (?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})
-            .+
-            \s+
-            (?P<interface>\w+)
-            $
-    """
-    )
-
-    _DIG_LOOKUP_RE = re.compile(
-        flags=re.VERBOSE,
-        pattern=r"""
-            ^
-            [\w.-]+
-            \s+
-            \d+
-            \s+
-            IN
-            \s+
-            (A|CNAME)
-            \s+
-            [\w.-]+
-        """
-    )
-
-    _DIG_REVLOOKUP_RE = re.compile(
-        flags=re.VERBOSE,
-        pattern=r"""
-            ^
-            (?P<query>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).in-addr.arpa.
-            \s+
-            \d+
-            \s+
-            IN
-            \s+
-            PTR
-            \s+
-            (?P<answer>[\w.-]+)
-            \.
-            $
-        """
-    )
 
     def __init__(self, context):
         super(Plugin, self).__init__(context=context)
-
-    def _getConfiguredIps(self):
-        interfaces = {}
-        addresses = {}
-        rc, stdout, stderr = self.execute(
-            args=(
-                self.command.get('ip'),
-                'addr',
-            ),
-        )
-        for line in stdout:
-            interfacematch = self._INTERFACE_RE.match(line)
-            addressmatch = self._ADDRESS_RE.match(line)
-            if interfacematch is not None:
-                interfaces[
-                    interfacematch.group('interface')
-                ] = 'LOOPBACK' in interfacematch.group('options')
-            elif addressmatch is not None:
-                addresses.setdefault(
-                    addressmatch.group('interface'),
-                    []
-                ).append(
-                    addressmatch.group('address')
-                )
-        iplist = []
-        for interface, loopback in interfaces.iteritems():
-            if not loopback:
-                iplist.extend(addresses.get(interface, []))
-        if len(iplist) < 1:
-            raise RuntimeError(
-                _('Could not find any configured IP address on the host')
-            )
-        return set(iplist)
-
-    def _resolvedByDNS(self, fqdn):
-        args = [
-            self.command.get('dig'),
-            fqdn
-        ]
-        rc, stdout, stderr = self.execute(
-            args=args,
-            raiseOnError=False
-        )
-        resolved = False
-        if rc == 0:
-            for line in stdout:
-                if self._DIG_LOOKUP_RE.search(line):
-                    resolved = True
-        return resolved
-
-    def _dig_reverse_lookup(self, addr):
-        names = set()
-        args = [
-            self.command.get('dig'),
-            '-x',
-            addr,
-        ]
-        rc, stdout, stderr = self.execute(
-            args=args,
-            raiseOnError=False
-        )
-        if rc == 0:
-            for line in stdout:
-                found = self._DIG_REVLOOKUP_RE.search(line)
-                if found:
-                    names.add(found.group('answer'))
-        return names
-
-    def _validateFQDNresolvability(self, fqdn):
-
-        #get set of IPs
-        ipAddresses = self._getConfiguredIps()
-        #resolve fqdn
-        try:
-            resolvedAddresses = set([
-                address[0] for __, __, __, __, address in
-                socket.getaddrinfo(
-                    fqdn,
-                    None
-                )
-            ])
-        except socket.error:
-            #can't be resolved
-            raise RuntimeError(
-                _('{fqdn} did not resolve into an IP address').format(
-                    fqdn=fqdn,
-                )
-            )
-
-        prettyString = ' '.join(resolvedAddresses)
-        self.logger.debug(
-            '{fqdn} resolves to: {addresses}'.format(
-                fqdn=fqdn,
-                addresses=prettyString
-            )
-        )
-        resolvedByDNS = self._resolvedByDNS(fqdn)
-        source = ('DNS' if resolvedByDNS else _('host file'))
-        #compare found IP with list of local IPs and match.
-        if not resolvedAddresses.issubset(ipAddresses):
-            raise RuntimeError(
-                _(
-                    'The following addresses resolved from {source}: '
-                    "{addresses} can't be mapped "
-                    'to non loopback devices on this host'
-                ).format(
-                    source=source,
-                    addresses=prettyString
-                )
-            )
-
-        if not resolvedByDNS:
-            self.logger.warning(
-                _(
-                    'Failed to resolve {fqdn} using DNS, '
-                    'it can be resolved only locally'
-                ).format(
-                    fqdn=fqdn,
-                )
-            )
-        elif self.environment[osetupcons.ConfigEnv.FQDN_REVERSE_VALIDATION]:
-            #reverse resolved IP and compare with given fqdn
-            revResolved = False
-            for address in resolvedAddresses:
-                reverseNames = self._dig_reverse_lookup(address)
-                for name in reverseNames:
-                    revResolved = name.lower() == fqdn.lower()
-                    if revResolved:
-                        break
-                if revResolved:
-                    break
-            if not revResolved:
-                raise RuntimeError(
-                    _(
-                        'The following addresses: {addresses} did not reverse'
-                        'resolve into {fqdn}'
-                    ).format(
-                        addresses=prettyString,
-                        fqdn=fqdn
-                    )
-                )
-
-    def _validateFQDN(self, fqdn):
-        if self._IPADDR_RE.match(fqdn):
-            raise RuntimeError(
-                _(
-                    '{fqdn} is an IP address and not a FQDN. '
-                    'A FQDN is needed to be able to generate '
-                    'certificates correctly.'
-                ).format(
-                    fqdn=fqdn,
-                )
-            )
-        if (
-            len(fqdn) < 1 or
-            len(fqdn) > 1000 or
-            not self._DOMAIN_RE.match(fqdn)
-        ):
-            raise RuntimeError(
-                _('{fqdn} has not a valid domain name').format(
-                    fqdn=fqdn,
-                )
-            )
-        self._validateFQDNresolvability(fqdn)
 
     @plugin.event(
         stage=plugin.Stages.STAGE_INIT,
     )
     def _init(self):
-        self.environment.setdefault(
-            osetupcons.ConfigEnv.FQDN,
-            None
-        )
-        self.environment.setdefault(
-            osetupcons.ConfigEnv.FQDN_REVERSE_VALIDATION,
-            False
-        )
         self.environment.setdefault(
             osetupcons.ConfigEnv.HTTP_PORT,
             osetupcons.Defaults.DEFAULT_NETWORK_HTTP_PORT
@@ -322,8 +72,6 @@
         stage=plugin.Stages.STAGE_SETUP,
     )
     def _setup(self):
-        self.command.detect('ip')
-        self.command.detect('dig')
         if self.environment[osetupcons.CoreEnv.DEVELOPER_MODE]:
             self.environment[
                 osetupcons.ConfigEnv.HTTP_PORT
@@ -335,48 +83,6 @@
             ] = self.environment[
                 osetupcons.ConfigEnv.JBOSS_HTTPS_PORT
             ]
-
-    @plugin.event(
-        stage=plugin.Stages.STAGE_CUSTOMIZATION,
-        name=osetupcons.Stages.CONFIG_PROTOCOLS_CUSTOMIZATION,
-        before=[
-            osetupcons.Stages.DIALOG_TITLES_E_NETWORK,
-        ],
-        after=[
-            osetupcons.Stages.DIALOG_TITLES_S_NETWORK,
-        ],
-    )
-    def _customization(self):
-        interactive = self.environment[osetupcons.ConfigEnv.FQDN] is None
-        validFQDN = False
-        while not validFQDN:
-            if interactive:
-                fqdn = socket.getfqdn()
-                self.environment[
-                    osetupcons.ConfigEnv.FQDN
-                ] = self.dialog.queryString(
-                    name='OVESETUP_NETWORK_FQDN',
-                    note=_(
-                        'Host fully qualified DNS name of '
-                        'this server [@DEFAULT@]: '
-                    ),
-                    prompt=True,
-                    default=fqdn,
-                )
-            try:
-                self._validateFQDN(
-                    self.environment[osetupcons.ConfigEnv.FQDN]
-                )
-                validFQDN = True
-            except RuntimeError as e:
-                if interactive:
-                    self.logger.error(
-                        _('FQDN is not valid: {error}').format(
-                            error=e
-                        )
-                    )
-                else:
-                    raise
 
     @plugin.event(
         stage=plugin.Stages.STAGE_MISC,


--
To view, visit http://gerrit.ovirt.org/14889
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I7276d3e9088fd370458846bfdcee7166331935e4
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Alon Bar-Lev <[email protected]>
_______________________________________________
Engine-patches mailing list
[email protected]
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to