Ejemplo n.º 1
0
 def _setup(self):
     self.environment.setdefault(
         ohostedcons.CoreEnv.SKIP_TTY_CHECK,
         False
     )
     self.environment.setdefault(
         ohostedcons.CoreEnv.SCREEN_PROCEED,
         None
     )
     ssh_connected = not os.getenv('SSH_CLIENT') is None
     skip_tty_check = self.environment[
         ohostedcons.CoreEnv.SKIP_TTY_CHECK
     ]
     if not skip_tty_check:
         if ssh_connected and os.getenv('TERM') is None:
             self.logger.error(
                 _(
                     'It has been detected that this program is executed '
                     'through an SSH connection without pseudo-tty '
                     'allocation.\n'
                     'Please run again ssh adding -t option\n'
                 )
             )
             raise context.Abort('Aborted due to missing requirement')
     screen_used = os.getenv('TERM') == 'screen'
     if ssh_connected and not screen_used:
         interactive = self.environment[
             ohostedcons.CoreEnv.SCREEN_PROCEED
         ] is None
         if interactive:
             self.environment[
                 ohostedcons.CoreEnv.SCREEN_PROCEED
             ] = self.dialog.queryString(
                 name=ohostedcons.Confirms.SCREEN_PROCEED,
                 note=_(
                     'It has been detected that this program is executed '
                     'through an SSH connection without using screen.\n'
                     'Continuing with the installation may lead to broken '
                     'installation if the network connection fails.\n'
                     'It is highly recommended to abort the installation '
                     'and run it inside a screen session using command '
                     '"screen".\n'
                     'Do you want to continue anyway? '
                     '(@VALUES@)[@DEFAULT@]: '
                 ),
                 prompt=True,
                 validValues=(_('Yes'), _('No')),
                 caseSensitive=False,
                 default=_('No')
             ) == _('Yes').lower()
             if not self.environment[ohostedcons.CoreEnv.SCREEN_PROCEED]:
                 raise context.Abort('Aborted by user')
Ejemplo n.º 2
0
    def queryMultiString(self, name, note=None):
        if note is None:
            note = _("\nPlease specify multiple strings for '{name}':").format(
                name=name)
        self._writeQueryStart(name)
        self.dialog.note(text=note)
        self.dialog.note(text=_("type '{boundary}' in own line to mark end, "
                                "'{abortboundary}' aborts").format(
                                    boundary=self.BOUNDARY,
                                    abortboundary=self.ABORT_BOUNDARY,
                                ))
        self._write(text='%s%s %s %s %s\n' % (
            dialogcons.DialogMachineConst.REQUEST_PREFIX,
            dialogcons.DialogMachineConst.QUERY_MULTI_STRING,
            name,
            self.BOUNDARY,
            self.ABORT_BOUNDARY,
        ))
        self._writeQueryEnd(name)
        value = []
        while True:
            v = self._readline()
            if v == self.BOUNDARY:
                break
            elif v == self.ABORT_BOUNDARY:
                raise context.Abort(_('Aborted by dialog'))
            value.append(v)

        return value
Ejemplo n.º 3
0
    def _setup(self):
        self.dialog.note(_('During customization use CTRL-D to abort.'))
        interactive = self.environment[
            ohostedcons.CoreEnv.DEPLOY_PROCEED] is None
        if interactive:
            self.environment[
                ohostedcons.CoreEnv.DEPLOY_PROCEED] = self.dialog.queryString(
                    name=ohostedcons.Confirms.DEPLOY_PROCEED,
                    note=
                    _('Continuing will configure this host for serving as '
                      'hypervisor and create a local VM with a running engine.\n'
                      'The locally running engine will be used to configure '
                      'a storage domain and create a VM there.\n'
                      'At the end the disk of the local VM will be moved to the '
                      'shared storage.\n'
                      'Are you sure you want to continue? '
                      '(@VALUES@)[@DEFAULT@]: '),
                    prompt=True,
                    validValues=(_('Yes'), _('No')),
                    caseSensitive=False,
                    default=_('Yes')) == _('Yes').lower()
        if not self.environment[ohostedcons.CoreEnv.DEPLOY_PROCEED]:
            raise otopicontext.Abort('Aborted by user')

        self.environment.setdefault(
            ohostedcons.CoreEnv.REQUIREMENTS_CHECK_ENABLED, True)

        self.environment[ohostedcons.CoreEnv.ANSIBLE_DEPLOYMENT] = True
        self.environment[ohostedcons.VMEnv.CDROM] = None
Ejemplo n.º 4
0
    def _setup(self):
        self.dialog.note(_('During customization use CTRL-D to abort.'))
        interactive = self.environment[
            ohostedcons.CoreEnv.DEPLOY_PROCEED] is None
        if interactive:
            self.environment[
                ohostedcons.CoreEnv.DEPLOY_PROCEED] = self.dialog.queryString(
                    name=ohostedcons.Confirms.DEPLOY_PROCEED,
                    note=_(
                        'Continuing will configure this host for serving as '
                        'hypervisor and create a VM where you have to install '
                        'the engine afterwards.\n'
                        'Are you sure you want to continue? '
                        '(@VALUES@)[@DEFAULT@]: '),
                    prompt=True,
                    validValues=(_('Yes'), _('No')),
                    caseSensitive=False,
                    default=_('Yes')) == _('Yes').lower()
        if not self.environment[ohostedcons.CoreEnv.DEPLOY_PROCEED]:
            raise otopicontext.Abort('Aborted by user')

        self.environment.setdefault(
            ohostedcons.CoreEnv.REQUIREMENTS_CHECK_ENABLED, True)
        try:
            # avoid: pyflakes 'Config' imported but unused error
            import ovirt.node.utils.fs
            if hasattr(ovirt.node.utils.fs, 'Config'):
                self.environment[ohostedcons.CoreEnv.NODE_SETUP] = True
        except ImportError:
            self.logger.debug('Disabling persisting file configuration')
Ejemplo n.º 5
0
    def _setup(self):
        self.dialog.note(
            _(
                'During customization use CTRL-D to abort.'
            )
        )
        interactive = self.environment[
            ohostedcons.CoreEnv.DEPLOY_PROCEED
        ] is None
        if interactive:
            restore_addition_1 = ''
            restore_addition_2 = ''
            if self.environment[
                ohostedcons.CoreEnv.RESTORE_FROM_FILE
            ] is not None:
                restore_addition_1 = _(
                    'The provided engine backup file will be restored there,\n'
                    'it\'s strongly recommended to run this tool on an host '
                    'that wasn\'t part of the environment going to be '
                    'restored.\nIf a reference to this host is already '
                    'contained in the backup file, it will be filtered out '
                    'at restore time.\n'
                )
                restore_addition_2 = _(
                    'The old hosted-engine storage domain will be renamed, '
                    'after checking that everything is correctly working '
                    'you can manually remove it.\n'
                    'Other hosted-engine hosts have to be reinstalled from '
                    'the engine to update their hosted-engine configuration.\n'
                )

            self.environment[
                ohostedcons.CoreEnv.DEPLOY_PROCEED
            ] = self.dialog.queryString(
                name=ohostedcons.Confirms.DEPLOY_PROCEED,
                note=_(
                    'Continuing will configure this host for serving as '
                    'hypervisor and will create a local VM with a '
                    'running engine.\n'
                    '{restore_addition_1}'
                    'The locally running engine will be used to configure '
                    'a new storage domain and create a VM there.\n'
                    'At the end the disk of the local VM will be moved to the '
                    'shared storage.\n'
                    '{restore_addition_2}'
                    'Are you sure you want to continue? '
                    '(@VALUES@)[@DEFAULT@]: '
                ).format(
                    restore_addition_1=restore_addition_1,
                    restore_addition_2=restore_addition_2,
                ),
                prompt=True,
                validValues=(_('Yes'), _('No')),
                caseSensitive=False,
                default=_('Yes')
            ) == _('Yes').lower()
        if not self.environment[ohostedcons.CoreEnv.DEPLOY_PROCEED]:
            raise otopicontext.Abort('Aborted by user')

        self.environment[ohostedcons.VMEnv.CDROM] = None
Ejemplo n.º 6
0
    def _wait_datacenter_up(self):
        engine_api = engineapi.get_engine_api(self)

        my_host_id = None
        my_host_uuid = self._get_host_uuid()
        for h in engine_api.hosts.list():
            if h.get_hardware_information().get_uuid() == my_host_uuid:
                my_host_id = h.get_id()
        if not my_host_id:
            raise(_(
                'Unable to find this host in the engine, '
                'please check the backup recovery'
            ))
        host_broker = engine_api.hosts.get(id=my_host_id)

        cluster_broker = engine_api.clusters.get(
            id=host_broker.get_cluster().get_id()
        )
        dc_broker = engine_api.datacenters.get(
            id=cluster_broker.get_data_center().get_id()
        )

        ready = False
        interactive = self.environment[
            ohostedcons.Upgrade.CONFIRM_UPGRADE_SUCCESS
        ] is None
        while not ready:
            dc_broker = dc_broker.update()
            host_broker = host_broker.update()
            dc_status = dc_broker.get_status().state
            host_status = host_broker.get_status().state
            if not (dc_status == 'up' and host_status == 'up'):
                if interactive:
                    rcontinue = self.dialog.queryString(
                        name=ohostedcons.Confirms.UPGRADE_PROCEED,
                        note=_(
                            'The datacenter or this host is still marked as '
                            'down.\nPlease check engine logs to ensure that '
                            'everything is fine.\n '
                            'Are you sure you want to continue? '
                            '(@VALUES@)[@DEFAULT@]: '
                        ),
                        prompt=True,
                        validValues=(_('Yes'), _('No')),
                        caseSensitive=False,
                        default=_('Yes')
                    ) == _('Yes').lower()
                    if not rcontinue:
                        raise otopicontext.Abort('Aborted by user')
                else:
                    raise RuntimeError(
                        _(
                            'This host is not active in the engine '
                            'after the restore'
                        )
                    )
            else:
                ready = True
        engine_api.disconnect()
Ejemplo n.º 7
0
    def _setup(self):
        self.dialog.note(
            _(
                'During customization use CTRL-D to abort.'
            )
        )
        interactive = self.environment[
            ohostedcons.CoreEnv.ROLLBACK_PROCEED
        ] is None
        if interactive:
            self.environment[
                ohostedcons.CoreEnv.ROLLBACK_PROCEED
            ] = self.dialog.queryString(
                name=ohostedcons.Confirms.ROLLBACK_PROCEED,
                note=ohostedutil.readmeFileContent(
                    ohostedcons.FileLocations.README_ROLLBACK
                ) + _(
                    'Continuing will rollback the engine VM from a previous '
                    'upgrade attempt.\n'
                    'This procedure will restore an engine VM image '
                    'from a backup taken during an upgrade attempt.\n'
                    'The result of any action occurred after the backup '
                    'creation instant could be definitively lost.\n'
                    'Are you sure you want to continue? '
                    '(@VALUES@)[@DEFAULT@]: '
                ),
                # TODO: point to our site for troubleshooting info...
                prompt=True,
                validValues=(_('Yes'), _('No')),
                caseSensitive=False,
                default=_('Yes')
            ) == _('Yes').lower()
        if not self.environment[ohostedcons.CoreEnv.ROLLBACK_PROCEED]:
            raise otopicontext.Abort('Aborted by user')

        self.environment[
            ohostedcons.CoreEnv.ROLLBACK_UPGRADE
        ] = True

        self.environment[
            ohostedcons.VDSMEnv.VDS_CLI
        ] = ohautil.connect_vdsm_json_rpc(
            logger=self.logger,
            timeout=ohostedcons.Const.VDSCLI_SSL_TIMEOUT,
        )

        self.environment.setdefault(
            ohostedcons.CoreEnv.REQUIREMENTS_CHECK_ENABLED,
            True
        )
        try:
            # avoid: pyflakes 'Config' imported but unused error
            import ovirt.node.utils.fs
            if hasattr(ovirt.node.utils.fs, 'Config'):
                self.environment[ohostedcons.CoreEnv.NODE_SETUP] = True
        except ImportError:
            self.logger.debug('Disabling persisting file configuration')
Ejemplo n.º 8
0
 def _cmd_abort(self, cmd):
     parser = self._MyOptionParser(cmd[0], logger=self.logger)
     (options, args) = parser.parse_args(args=cmd[1:])
     if options.help:
         self.dialog.note(text=parser.format_help())
     elif args:
         self.logger.error(_("Syntax error"))
     else:
         raise context.Abort(_('Aborted by user'))
     return True
Ejemplo n.º 9
0
 def _confirm(self):
     if not self.dialog.confirm(
             name=odeploycons.Confirms.DEPLOY_PROCEED,
             description='Proceed with ovirt-host-deploy',
             note=_(
                 'Continuing will configure this host for serving '
                 'as hypervisor. Are you sure you want to continue? (yes/no) '
             ),
             prompt=True,
     ):
         raise context.Abort('Aborted by user')
Ejemplo n.º 10
0
    def _setup(self):
        self.dialog.note(_('During customization use CTRL-D to abort.'))
        interactive = self.environment[
            ohostedcons.CoreEnv.UPGRADE_PROCEED] is None
        if interactive:
            self.environment[
                ohostedcons.CoreEnv.UPGRADE_PROCEED] = self.dialog.queryString(
                    name=ohostedcons.Confirms.UPGRADE_PROCEED,
                    note=ohostedutil.readmeFileContent(
                        ohostedcons.FileLocations.README_APPLIANCE) +
                    _('Continuing will upgrade the engine VM running on this '
                      'hosts deploying and configuring '
                      'a new appliance.\n'
                      'If your engine VM is already based on el7 you can also '
                      'simply upgrade the engine there.\n'
                      'This procedure will create a new disk on the '
                      'hosted-engine storage domain and it will backup '
                      'there the content of your current engine VM disk.\n'
                      'The new el7 based appliance will be deployed over the '
                      'existing disk destroying its content; '
                      'at any time you will be able to rollback using the '
                      'content of the backup disk.\n'
                      'You will be asked to take a backup of the running engine '
                      'and copy it to this host.\n'
                      'The engine backup will be automatically injected '
                      'and recovered on the new appliance.\n'
                      'Are you sure you want to continue? '
                      '(@VALUES@)[@DEFAULT@]: '),
                    # TODO: point to our site for troubleshooting info...
                    prompt=True,
                    validValues=(_('Yes'), _('No')),
                    caseSensitive=False,
                    default=_('Yes')) == _('Yes').lower()
        if not self.environment[ohostedcons.CoreEnv.UPGRADE_PROCEED]:
            raise otopicontext.Abort('Aborted by user')

        self.environment[ohostedcons.CoreEnv.UPGRADING_APPLIANCE] = True

        self.environment[
            ohostedcons.VDSMEnv.VDS_CLI] = ohautil.connect_vdsm_json_rpc(
                logger=self.logger,
                timeout=ohostedcons.Const.VDSCLI_SSL_TIMEOUT,
            )

        self.environment.setdefault(
            ohostedcons.CoreEnv.REQUIREMENTS_CHECK_ENABLED, True)
        try:
            # avoid: pyflakes 'Config' imported but unused error
            import ovirt.node.utils.fs
            if hasattr(ovirt.node.utils.fs, 'Config'):
                self.environment[ohostedcons.CoreEnv.NODE_SETUP] = True
        except ImportError:
            self.logger.debug('Disabling persisting file configuration')
Ejemplo n.º 11
0
    def queryValue(self, name, note=None):
        if note is None:
            note = _("\nPlease specify value for '{name}':").format(
                name=name
            )
        self._write(
            text='%s%s %s\n' % (
                dialogcons.DialogMachineConst.REQUEST_PREFIX,
                dialogcons.DialogMachineConst.QUERY_VALUE,
                name,
            )
        )
        self.dialog.note(text=note)
        self.dialog.note(
            text=_(
                "Response is VALUE {name}=type:value or "
                "ABORT {name}"
            ).format(
                name=name,
            ),
        )

        opcode, variable = self._readline().split(' ', 1)
        variable = variable.split('=', 1)

        if variable[0] != name:
            raise RuntimeError(
                _(
                    "Expected response for {name}, "
                    "received '{received}'"
                ).format(
                    name=name,
                    received=variable[0],
                )
            )

        if opcode == dialogcons.DialogMachineConst.QUERY_VALUE_RESPONSE_ABORT:
            raise context.Abort(_('Aborted by dialog'))
        elif opcode == \
                dialogcons.DialogMachineConst.QUERY_VALUE_RESPONSE_VALUE:
            if len(variable) != 2:
                raise RuntimeError(_('Value ot provided'))
            return common.parseTypedValue(variable[1])
        else:
            raise RuntimeError(
                _("Invalid response opcode '{code}'").format(
                    code=opcode,
                )
            )
Ejemplo n.º 12
0
    def confirm(
        self,
        name,
        description,
        note=None,
        prompt=False,
    ):
        if note is None:
            note = _("\nPlease confirm '{name}' {description}\n").format(
                name=name,
                description=description,
            )
        self._write(text='%s%s %s %s\n' %
                    (dialogcons.DialogMachineConst.REQUEST_PREFIX,
                     dialogcons.DialogMachineConst.CONFIRM, name, description))
        self.dialog.note(
            text=note,
            prompt=prompt,
        )
        self.dialog.note(text=_("Response is CONFIRM {name}=yes|no or "
                                "ABORT {name}").format(name=name, ), )

        opcode, variable = self._readline().split(' ', 1)
        variable = variable.split('=', 1)

        if variable[0] != name:
            raise RuntimeError(
                _("Expected response for {name}, "
                  "received '{received}'").format(
                      name=name,
                      received=variable[0],
                  ))

        if opcode == dialogcons.DialogMachineConst.CONFIRM_RESPONSE_ABORT:
            raise context.Abort(_('Aborted by dialog'))
        elif opcode == \
                dialogcons.DialogMachineConst.CONFIRM_RESPONSE_VALUE:
            if len(variable) != 2:
                raise RuntimeError(_('Value ot provided'))
            return variable[1] in ('yes', 'YES', 'y', 'Y')
        else:
            raise RuntimeError(
                _("Invalid response opcode '{code}'").format(code=opcode, ))
Ejemplo n.º 13
0
    def _validate_lm_volumes(self):
        """
        This method, if the relevant uuids aren't in the initial answerfile,
        will look for lockspace and metadata volumes on the shared
        storage identifying them by their description.
        We need to re-scan each time we run the upgrade flow since they
        could have been created in a previous upgrade attempt.
        If the volumes are not on disk, it triggers volume creation as for
        fresh deployments; volume creation code will also remove the previous
        file and create a new symlink to the volume using the same file name.
        """
        self.logger.info(_('Scanning for lockspace and metadata volumes'))
        cli = self.environment[ohostedcons.VDSMEnv.VDS_CLI]
        img = image.Image(
            self.environment[ohostedcons.StorageEnv.DOMAIN_TYPE],
            self.environment[ohostedcons.StorageEnv.SD_UUID],
        )
        img_list = img.get_images_list(cli)
        self.logger.debug('img list: {il}'.format(il=img_list))
        sdUUID = self.environment[ohostedcons.StorageEnv.SD_UUID]
        spUUID = ohostedcons.Const.BLANK_UUID
        for img in img_list:
            try:
                volumeslist = cli.StorageDomain.getVolumes(
                    imageID=img,
                    storagepoolID=spUUID,
                    storagedomainID=sdUUID,
                )
                self.logger.debug('volumeslist: {vl}'.format(vl=volumeslist))
            except ServerError as e:
                # avoid raising here, simply skip the unknown image
                self.logger.debug(
                    'Error fetching volumes for {image}: {message}'.format(
                        image=img,
                        message=str(e),
                    ))
                continue

            for vol_uuid in volumeslist['items']:
                try:
                    volumeinfo = cli.Volume.getInfo(
                        volumeID=vol_uuid,
                        imageID=img,
                        storagepoolID=spUUID,
                        storagedomainID=sdUUID,
                    )
                    self.logger.debug(volumeinfo)
                except ServerError as e:
                    # avoid raising here, simply skip the unknown volume
                    self.logger.debug(('Error fetching volume info '
                                       'for {volume}: {message}').format(
                                           volume=vol_uuid,
                                           message=str(e),
                                       ))
                    continue

                disk_description = volumeinfo['description']
                if disk_description == self.environment[
                        ohostedcons.SanlockEnv.LOCKSPACE_NAME] + '.lockspace':
                    self.environment[ohostedcons.StorageEnv.
                                     LOCKSPACE_VOLUME_UUID] = vol_uuid
                    self.environment[
                        ohostedcons.StorageEnv.LOCKSPACE_IMAGE_UUID] = img
                elif disk_description == self.environment[
                        ohostedcons.SanlockEnv.LOCKSPACE_NAME] + '.metadata':
                    self.environment[
                        ohostedcons.StorageEnv.METADATA_VOLUME_UUID] = vol_uuid
                    self.environment[
                        ohostedcons.StorageEnv.METADATA_IMAGE_UUID] = img

        if (self.environment[ohostedcons.StorageEnv.LOCKSPACE_VOLUME_UUID] and
                self.environment[ohostedcons.StorageEnv.METADATA_VOLUME_UUID]):
            self.logger.info(
                _('Lockspace and metadata volumes are already on the '
                  'HE storage domain'))
            return

        interactive = self.environment[
            ohostedcons.Upgrade.LM_VOLUMES_UPGRADE_PROCEED] is None
        if interactive:
            self.environment[
                ohostedcons.Upgrade.
                LM_VOLUMES_UPGRADE_PROCEED] = self.dialog.queryString(
                    name=ohostedcons.Confirms.LM_VOLUMES_UPGRADE_PROCEED,
                    note=_(
                        'This system was initially deployed with oVirt 3.4 '
                        'using file based metadata and lockspace area.\n'
                        'Now you have to upgrade to up to date structure '
                        'using this tool.\n'
                        'In order to do that please manually stop ovirt-ha-agent '
                        'and ovirt-ha-broker on all the other HE hosts '
                        '(but not this one). '
                        'At the end you of this procedure you can simply '
                        'manually upgrade ovirt-hosted-engine-ha and '
                        'restart ovirt-ha-agent and ovirt-ha-broker on all '
                        'the hosted-engine hosts.\n'
                        'Are you sure you want to continue? '
                        '(@VALUES@)[@DEFAULT@]: '),
                    prompt=True,
                    validValues=(_('Yes'), _('No')),
                    caseSensitive=False,
                    default=_('Yes')) == _('Yes').lower()
        if not self.environment[
                ohostedcons.Upgrade.LM_VOLUMES_UPGRADE_PROCEED]:
            raise otopicontext.Abort('Aborted by user')

        self.logger.info(
            _('Waiting for HA agents on other hosts to be stopped'))
        vmstatus = vm_status.VmStatus()
        ready = False
        while not ready:
            ready = True
            status = vmstatus.get_status()
            self.logger.debug('hosted-engine-status: {s}'.format(s=status))
            for h in status['all_host_stats']:
                host_id = status['all_host_stats'][h]['host-id']
                hostname = status['all_host_stats'][h]['hostname']
                if 'stopped' in status['all_host_stats'][h]:
                    stopped = status['all_host_stats'][h]['stopped']
                    if host_id == self.environment[
                            ohostedcons.StorageEnv.HOST_ID]:
                        if stopped:
                            self.logger.warning(
                                _('Please keep ovirt-ha-agent running '
                                  'on this host'))
                            ready = False
                    else:
                        if not stopped:
                            self.logger.warning(
                                _('ovirt-ha-agent is still active on host {h}, '
                                  'please stop it '
                                  '(it can require a few seconds).').format(
                                      h=hostname))
                            ready = False
                else:
                    self.logger.warning(
                        _("Ignoring inconsistent info for host {h}".format(
                            h=hostname, )))
            if not ready:
                time.sleep(2)

        self.environment[ohostedcons.Upgrade.UPGRADE_CREATE_LM_VOLUMES] = True
Ejemplo n.º 14
0
    def _check_volume_properties(self, connection):
        server, volume = connection.split(':')
        if volume[0] == '/':
            volume = volume[1:]
        glustercmd = self.command.get('gluster')
        rc, stdout, stderr = self.execute(args=(
            glustercmd,
            '--mode=script',
            '--xml',
            'volume',
            'info',
            volume,
            '--remote-host=%s' % server,
        ),
                                          raiseOnError=True)
        if rc != 0:
            self.logger.error(_('Failed to retrieve Gluster Volume info'))
            raise RuntimeError('Failed to retrieve Gluster Volume info: ' +
                               str(stdout))
        self.logger.debug('Gluster Volume info XML output: ' + str(stdout))
        dom = xml.dom.minidom.parseString(''.join(stdout))
        cliOutput = dom.getElementsByTagName('cliOutput')[0]
        opRet = (
            cliOutput.getElementsByTagName('opRet')[0]).firstChild.nodeValue
        opErrno = (
            cliOutput.getElementsByTagName('opErrno')[0]).firstChild.nodeValue
        opErrstrElement = cliOutput.getElementsByTagName('opErrstr')[0]
        opErrstr = ' '.join(t.nodeValue for t in opErrstrElement.childNodes
                            if t.nodeType == t.TEXT_NODE)

        if opRet != '0':
            self.logger.error(_('Failed to retrieve Gluster Volume info'))
            raise RuntimeError('Failed to retrieve Gluster Volume info '
                               '[{code}]: {error}'.format(
                                   code=opErrno,
                                   error=opErrstr,
                               ))
        volInfo = cliOutput.getElementsByTagName('volInfo')[0]
        volumes = volInfo.getElementsByTagName('volumes')[0]
        volCount = (
            volumes.getElementsByTagName('count')[0]).firstChild.nodeValue
        if volCount != '1':
            raise RuntimeError(
                _('GlusterFS Volume {volume} does not exist!').format(
                    volume=volume, ))
        volume = volumes.getElementsByTagName('volume')[0]
        replicaCount = (volume.getElementsByTagName('replicaCount')[0]
                        ).firstChild.nodeValue
        if replicaCount not in ('3', '1'):
            raise RuntimeError(
                _('GlusterFS Volume is not using replica 1 or 3'))
        self.logger.info(
            _('GlusterFS replica {replicaCount} Volume detected').format(
                replicaCount=replicaCount, ))

        if replicaCount == '1':
            interactive = self.environment[
                ohostedcons.StorageEnv.ALLOW_GLUSTER_REPLICA_ONE] is None
            if interactive:
                self.environment[
                    ohostedcons.StorageEnv.
                    ALLOW_GLUSTER_REPLICA_ONE] = dialog.queryBoolean(
                        dialog=self.dialog,
                        name='ALLOW_GLUSTER_REPLICA_ONE',
                        note=_(
                            'GlusterFS in replica 1 does not provide full feature '
                            'set like high availability, ease of maintenance '
                            'operations.\n'
                            'Are you sure you want to continue? '
                            '(@VALUES@)[@DEFAULT@]: '),
                        prompt=True,
                        default=False,
                    )
            if not self.environment[
                    ohostedcons.StorageEnv.ALLOW_GLUSTER_REPLICA_ONE]:
                raise otopicontext.Abort('Aborted by user')

        host_list = []
        for brickE in volume.getElementsByTagName('brick'):
            host_list.append((brickE.getElementsByTagName('hostUuid')[0]
                              ).firstChild.nodeValue)
        if len(set(host_list)) < 3:
            self.logger.warning(
                _('Three distinct hosts are required for '
                  'safe and reliable operations'))
        status = (
            volume.getElementsByTagName('status')[0]).firstChild.nodeValue
        statusStr = (
            volume.getElementsByTagName('statusStr')[0]).firstChild.nodeValue
        if status != '1':
            raise RuntimeError(
                _("GlusterFS Volume is '{statusStr}', "
                  "please ensure that it's started").format(
                      statusStr=statusStr, ))