Sandro Bonazzola has uploaded a new change for review.

Change subject: HC: provisioning replica 3 volume
......................................................................

HC: provisioning replica 3 volume

Provision a Replica 3 Gluster volume instead
of a single brick volume.

Change-Id: I5fcde43354268477e4c8a92ee29470750da7337a
Bug-Url: https://bugzilla.redhat.com/1217448
Signed-off-by: Sandro Bonazzola <sbona...@redhat.com>
---
M src/ovirt_hosted_engine_setup/constants.py
M src/plugins/ovirt-hosted-engine-setup/storage/glusterfs.py
2 files changed, 137 insertions(+), 36 deletions(-)


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

diff --git a/src/ovirt_hosted_engine_setup/constants.py 
b/src/ovirt_hosted_engine_setup/constants.py
index c6195d6..4245e36 100644
--- a/src/ovirt_hosted_engine_setup/constants.py
+++ b/src/ovirt_hosted_engine_setup/constants.py
@@ -550,10 +550,10 @@
     @ohostedattrs(
         answerfile=True,
         summary=True,
-        description=_('GlusterFS Brick'),
+        description=_('GlusterFS Bricks'),
     )
-    def GLUSTER_BRICK(self):
-        return 'OVEHOSTED_STORAGE/glusterBrick'
+    def GLUSTER_BRICKS(self):
+        return 'OVEHOSTED_STORAGE/glusterBricks'
 
     @ohostedattrs(
         answerfile=True,
diff --git a/src/plugins/ovirt-hosted-engine-setup/storage/glusterfs.py 
b/src/plugins/ovirt-hosted-engine-setup/storage/glusterfs.py
index 745993f..f9da948 100644
--- a/src/plugins/ovirt-hosted-engine-setup/storage/glusterfs.py
+++ b/src/plugins/ovirt-hosted-engine-setup/storage/glusterfs.py
@@ -23,7 +23,7 @@
 """
 
 import gettext
-import socket
+import time
 
 
 from otopi import constants as otopicons
@@ -31,6 +31,7 @@
 from otopi import plugin
 from otopi import transaction
 from otopi import util
+from vdsm import netinfo
 from vdsm import vdscli
 
 
@@ -48,9 +49,13 @@
     GlusterFS storage provisioning plugin.
     """
 
+    CONNECTION_MAX_COUNT = 10
+    CONNECTION_WAIT = 2
+
     def __init__(self, context):
         super(Plugin, self).__init__(context=context)
         self._checker = ohosteddomains.DomainChecker()
+        self._local_ip_addresses = None
 
     def _configure_glusterfs_service(self):
         """
@@ -66,6 +71,8 @@
                 elif line.find('base-port') != -1:
                     continue
                 elif line.find('end-volume') == 0:
+                    # TODO: remove rpc-auth-allow-insecure config after
+                    # gluster3.7.1 is out and required.
                     content.append('    option rpc-auth-allow-insecure on')
                     content.append('    option base-port 49217')
                 content.append(line)
@@ -88,7 +95,9 @@
         """
         cli = vdscli.connect()
         share = self.environment[ohostedcons.StorageEnv.GLUSTER_SHARE_NAME]
-        brick = self.environment[ohostedcons.StorageEnv.GLUSTER_BRICK]
+        bricks = self.environment[
+            ohostedcons.StorageEnv.GLUSTER_BRICKS
+        ].split(',')
         self.logger.debug('glusterVolumesList')
         response = cli.glusterVolumesList()
         self.logger.debug(response)
@@ -98,8 +107,8 @@
         volumes = response['volumes']
         if share in volumes:
             self.logger.info(_('GlusterFS Volume already exists'))
-            if set([brick]) != set(volumes[share]['bricks']):
-                self.logger.debug(set([brick]))
+            if set([bricks]) != set(volumes[share]['bricks']):
+                self.logger.debug(set([bricks]))
                 self.logger.debug(set(volumes[share]['bricks']))
                 raise RuntimeError(
                     _(
@@ -108,15 +117,62 @@
                     )
                 )
         else:
+            self.logger.info(_('Adding hosts to the Gluster Pool'))
+            self.logger.debug(cli.glusterHostsList())
+            for brick in bricks:
+                host = brick.split(':')[0]
+                if host not in self._local_ip_addresses:
+                    self.logger.debug('glusterHostAdd("%s")' % host)
+                    response = cli.glusterHostAdd(host)
+                    self.logger.debug(response)
+                    if response['status']['code'] != 0:
+                        self.logger.error(
+                            _(
+                                'Failed to add {host} to the Gluster Pool'
+                            ).format(host=host)
+                        )
+                        raise RuntimeError(response['status']['message'])
+            ready = False
+            count = 0
+            while not ready:
+                count += 1
+                response = cli.glusterHostsList()
+                self.logger.debug(response)
+                if response['status']['code'] != 0:
+                    self.logger.error(
+                        _('Failed to get the Gluster Pool status')
+                    )
+                    raise RuntimeError(response['status']['message'])
+                wait_list = []
+                for host in response['hosts']:
+                    if host['status'] != 'CONNECTED':
+                        wait_list.append(host['hostname'])
+                if wait_list:
+                    self.logger.info(
+                        _('Still waiting for {hosts} to be connected').format(
+                            hosts=', '.join(wait_list)
+                        )
+                    )
+                    if count < self.CONNECTION_MAX_COUNT:
+                        time.sleep(self.CONNECTION_WAIT)
+                    else:
+                        raise RuntimeError(
+                            _(
+                                'Timeout while waiting for hosts to be '
+                                'connected'
+                            )
+                        )
+                else:
+                    ready = True
             self.logger.info(_('Creating GlusterFS Volume'))
-            replica_count = ''
+            replica_count = '3'
             stripe_count = ''
             transport_list = ['tcp']
-            force = True
+            force = True  # Handle allowed but unsupported cases
             self.logger.debug('glusterVolumeCreate')
             response = cli.glusterVolumeCreate(
                 share,
-                [brick],
+                bricks,
                 replica_count,
                 stripe_count,
                 transport_list,
@@ -204,7 +260,7 @@
             ohostedcons.Defaults.DEFAULT_GLUSTER_SHARE_NAME
         )
         self.environment.setdefault(
-            ohostedcons.StorageEnv.GLUSTER_BRICK,
+            ohostedcons.StorageEnv.GLUSTER_BRICKS,
             None
         )
 
@@ -234,9 +290,8 @@
             ] = self.dialog.queryString(
                 name='OVEHOSTED_GLUSTER_PROVISIONING',
                 note=_(
-                    'Do you want to configure this host for '
-                    'providing GlusterFS storage (will start with no replica '
-                    'requires to grow to replica 3 later)? '
+                    'Do you want to configure the system for '
+                    'providing Replica 3 GlusterFS storage? '
                     '(@VALUES@)[@DEFAULT@]: '
                 ),
                 prompt=True,
@@ -259,33 +314,79 @@
     )
     def _brick_customization(self):
         interactive = self.environment[
-            ohostedcons.StorageEnv.GLUSTER_BRICK
+            ohostedcons.StorageEnv.GLUSTER_BRICKS
         ] is None
-        if interactive:
-            path = self.dialog.queryString(
-                name='OVEHOSTED_GLUSTER_BRICKS',
-                note=_(
-                    'Please provide a path to be used for the brick '
-                    'on this host:'
-                ),
-                prompt=True,
-                caseSensitive=True,
-            )
-            self.environment[
-                ohostedcons.StorageEnv.GLUSTER_BRICK
-            ] = '%s:%s' % (socket.gethostname(), path)
-        else:
-            host = self.environment[
-                ohostedcons.StorageEnv.GLUSTER_BRICK
-            ].split(':')[0]
-            if host != socket.gethostname():
-                raise ValueError(
-                    _('The specified brick must be on this host')
+        user_input = self.environment[ohostedcons.StorageEnv.GLUSTER_BRICKS]
+        valid = False
+        hosts = set()
+        while not valid:
+            # Check that 3 bricks have been specified
+            # Check that each brick is host:/path
+            # Check that at least one brick is on local host
+            # Check that 3 different hosts are used
+            if interactive:
+                user_input = self.dialog.queryString(
+                    name='OVEHOSTED_GLUSTER_BRICKS',
+                    note=_(
+                        'Please provide a comma separated list of 3 bricks '
+                        'using the IP address of the NIC to be used for '
+                        'the storage. Ensure that Gluster Server daemon is '
+                        'installed and running on the specified hosts before '
+                        'continuing. '
+                        '(ip_addr1:/path1,ip_addr2:/path2,ip_addr3:/path3): '
+                    ),
+                    prompt=True,
+                    caseSensitive=True,
                 )
+            bricks = user_input.split(',')
+            try:
+                if len(bricks) != 3:
+                    raise ValueError(
+                        _('Only {bricks} have been specified').format(
+                            bricks=bricks
+                        )
+                    )
+                for brick in bricks:
+                    try:
+                        host, _path = brick.split(':')
+                    except ValueError:
+                        raise ValueError(
+                            _(
+                                'Invalid brick: {brick} has been specified'
+                            ).format(
+                                brick=brick
+                            )
+                        )
+                    hosts.add(host)
+                self._local_ip_addresses = set([
+                    addr.split('/')[0] for addr in netinfo.getIpAddresses()
+                ])
+                if not self._local_ip_addresses.intersection(hosts):
+                    raise ValueError(
+                        _('No local brick has been specified')
+                    )
+                if len(hosts) != 3:
+                    self.logger.warning(
+                        _(
+                            'Only {hosts} hosts have been specified. '
+                            'Using less than three hosts for Replica 3 '
+                            'deployment is allowed but not supported.'
+                        )
+                    )
+                valid = True
+                self.environment[
+                    ohostedcons.StorageEnv.GLUSTER_BRICKS
+                ] = user_input
+            except ValueError as e:
+                if interactive:
+                    self.logger.error(str(e))
+                    continue
+                else:
+                    raise e
         self.environment[
             ohostedcons.StorageEnv.STORAGE_DOMAIN_CONNECTION
         ] = '{host}:/{share}'.format(
-            host=socket.gethostname(),
+            host=list(self._local_ip_addresses.intersection(hosts))[0],
             share=self.environment[ohostedcons.StorageEnv.GLUSTER_SHARE_NAME],
         )
 


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

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

Reply via email to