2.3. 使用 Relax-and-Recover 工具恢复 undercloud 节点和 control plane 节点
如果您的 undercloud 或 control plane 节点被破坏,或者在更新或升级过程中发生错误,您可以将 undercloud 或 overcloud control plane 节点从备份恢复到之前的状态。如果恢复过程无法自动恢复具有 colocated Ceph 监视器的 Galera 集群或节点,您可以手动恢复这些组件。
2.3.1. 恢复 undercloud 节点 复制链接链接已复制到粘贴板!
您可以使用 ReaR 创建的备份 ISO 镜像将 undercloud 节点恢复到之前的状态。您可以在备份节点中找到备份 ISO 镜像。将可引导 ISO 镜像刻录到 DVD,或通过 Integrated Lights-Out (iLO)远程访问将其下载到 undercloud 节点。
先决条件
- 您已创建了 undercloud 节点的备份。更多信息请参阅 第 2.1.8 节 “创建 undercloud 节点的备份”。
- 您可以访问备份节点。
-
如果将 OVS 网桥用于网络接口,您可以访问您在
NETWORKING_PREPARATION_COMMANDS
参数中设置的网络配置信息。如需更多信息,请参阅 第 2.1.7 节 “配置 Open vSwitch (OVS)接口以进行备份”。 如果配置了备份加密,您必须在开始恢复过程前解密备份。在备份文件所在的系统中运行以下解密步骤:
dd if=backup.tar.gz | /usr/bin/openssl des3 -d -k "<encryption key>" | tar -C <backup_location> -xzvf - '*.conf'
$ dd if=backup.tar.gz | /usr/bin/openssl des3 -d -k "<encryption key>" | tar -C <backup_location> -xzvf - '*.conf'
Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
将
<encryption key
> 替换为您的加密密钥。 -
将
<backup_location
> 替换为您要保存backup.tar.gz
文件的文件夹,如/ctl_plane_backups/undercloud-0/
。
-
将
流程
- 关闭 undercloud 节点。在继续操作前,请确保 undercloud 节点已完全关闭。
- 使用备份 ISO 镜像引导 undercloud 节点。
显示
Relax-and-Recover
引导菜单时,选择Recover <undercloud_node>
。将<undercloud_node
> 替换为 undercloud 节点的名称。注意如果您的系统使用 UEFI,请选择
Relax-and-Recover (no Secure Boot)
选项。以
root
用户身份登录并恢复节点:显示以下信息:
Welcome to Relax-and-Recover. Run "rear recover" to restore your system! RESCUE <undercloud_node>:~ # rear recover
Welcome to Relax-and-Recover. Run "rear recover" to restore your system! RESCUE <undercloud_node>:~ # rear recover
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 当 undercloud 节点恢复过程完成后,控制台会显示以下信息:
Finished recovering your system Exiting rear recover Running exit tasks
Finished recovering your system Exiting rear recover Running exit tasks
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 关闭该节点:
RESCUE <undercloud_node>:~ # poweroff
RESCUE <undercloud_node>:~ # poweroff
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在引导时,节点会恢复其之前的状态。
2.3.2. 恢复 control plane 节点 复制链接链接已复制到粘贴板!
如果在更新或升级过程中发生错误,您可以使用您使用 ReaR 创建的备份 ISO 镜像将 control plane 节点恢复到之前的状态。
要恢复 control plane,您必须恢复所有 control plane 节点以确保状态一致性。
您可以在备份节点中找到备份 ISO 镜像。将可引导 ISO 镜像刻录到 DVD,或通过 Integrated Lights-Out (iLO)远程访问将其下载到 undercloud 节点。
红帽支持对带有原生 SDN 的 Red Hat OpenStack Platform 备份,如 Open vSwitch (OVS)和默认的 Open Virtual Network (OVN)。有关第三方 SDN 的详情,请参考第三方 SDN 文档。
先决条件
- 您已创建了 control plane 节点的备份。更多信息请参阅 第 2.2.5 节 “创建 control plane 节点的备份”。
- 您可以访问备份节点。
-
如果将 OVS 网桥用于网络接口,您可以访问您在
NETWORKING_PREPARATION_COMMANDS
参数中设置的网络配置信息。如需更多信息,请参阅 第 2.2.4 节 “配置 Open vSwitch (OVS)接口以进行备份”。
流程
- 关闭每个 control plane 节点。在继续操作前,请确保 control plane 节点已完全关闭。
- 使用对应的备份 ISO 镜像引导每个 control plane 节点。
当显示
Relax-and-Recover
引导菜单时,在每个 control plane 节点上选择Recover <control_plane_node>
。将<control_plane_node
> 替换为对应 control plane 节点的名称。注意如果您的系统使用 UEFI,请选择
Relax-and-Recover (no Secure Boot)
选项。在每个 control plane 节点上,以
root
用户身份登录并恢复节点:显示以下信息:
Welcome to Relax-and-Recover. Run "rear recover" to restore your system! RESCUE <control_plane_node>:~ # rear recover
Welcome to Relax-and-Recover. Run "rear recover" to restore your system! RESCUE <control_plane_node>:~ # rear recover
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 当 control plane 节点恢复过程完成后,控制台会显示以下信息:
Finished recovering your system Exiting rear recover Running exit tasks
Finished recovering your system Exiting rear recover Running exit tasks
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 当命令行控制台可用时,恢复每个 control plane 节点的
config-drive
分区:once completed, restore the config-drive partition (which is ISO9660)
# once completed, restore the config-drive partition (which is ISO9660) RESCUE <control_plane_node>:~ $ dd if=/mnt/local/mnt/config-drive of=<config_drive_partition>
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 关闭该节点:
RESCUE <control_plane_node>:~ # poweroff
RESCUE <control_plane_node>:~ # poweroff
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 将引导序列设置为普通引导设备。在引导时,节点会恢复其之前的状态。
要确保服务正确运行,请检查 pacemaker 的状态。以
root
用户身份登录 Controller 节点,然后输入以下命令:pcs status
# pcs status
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 要查看 overcloud 的状态,请使用 OpenStack Integration Test Suite (tempest)。如需更多信息,请参阅使用 Integration Test Suite (tempest)验证您的 OpenStack 云。
故障排除
-
运行以下命令,清除
pcs status
显示的资源警报:
pcs resource clean
# pcs resource clean
-
运行以下命令,清除
pcs status
显示的 STONITH 隔离操作错误:
pcs resource clean pcs stonith history cleanup
# pcs resource clean
# pcs stonith history cleanup
2.3.3. 手动恢复 Galera 集群 复制链接链接已复制到粘贴板!
如果 Galera 集群没有作为恢复过程的一部分恢复,您必须手动恢复 Galera。
在此过程中,您必须在一个 Controller 节点上执行一些步骤。确保您在与完成流程相同的 Controller 节点上执行这些步骤。
流程
在
Controller-0
上,检索 Galera 集群虚拟 IP:sudo hiera -c /etc/puppet/hiera.yaml mysql_vip
$ sudo hiera -c /etc/puppet/hiera.yaml mysql_vip
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 通过所有 Controller 节点上的虚拟 IP 禁用数据库连接:
sudo iptables -I INPUT -p tcp --destination-port 3306 -d $MYSQL_VIP -j DROP
$ sudo iptables -I INPUT -p tcp --destination-port 3306 -d $MYSQL_VIP -j DROP
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
Controller-0
上,检索 MySQL root 密码:sudo hiera -c /etc/puppet/hiera.yaml mysql::server::root_password
$ sudo hiera -c /etc/puppet/hiera.yaml mysql::server::root_password
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
Controller-0
上,将 Galera 资源设置为非受管
模式:sudo pcs resource unmanage galera-bundle
$ sudo pcs resource unmanage galera-bundle
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 停止所有 Controller 节点上的 MySQL 容器:
sudo podman container stop $(sudo podman container ls --all --format "{{.Names}}" --filter=name=galera-bundle)
$ sudo podman container stop $(sudo podman container ls --all --format "{{.Names}}" --filter=name=galera-bundle)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 移动所有 Controller 节点上的当前目录:
sudo mv /var/lib/mysql /var/lib/mysql-save
$ sudo mv /var/lib/mysql /var/lib/mysql-save
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在所有 Controller 节点上创建新目录
/var/lib/mysq
:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 启动所有 Controller 节点上的 MySQL 容器:
sudo podman container start $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle)
$ sudo podman container start $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在所有 Controller 节点上创建 MySQL 数据库:
sudo podman exec -i $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql_install_db --datadir=/var/lib/mysql --user=mysql --log_error=/var/log/mysql/mysql_init.log"
$ sudo podman exec -i $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql_install_db --datadir=/var/lib/mysql --user=mysql --log_error=/var/log/mysql/mysql_init.log"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 启动所有 Controller 节点上的数据库:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysqld_safe --skip-networking --wsrep-on=OFF --log-error=/var/log/mysql/mysql_safe.log" &
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysqld_safe --skip-networking --wsrep-on=OFF --log-error=/var/log/mysql/mysql_safe.log" &
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在所有 Controller 节点上移动
.my.cnf
Galera 配置文件:sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mv /root/.my.cnf /root/.my.cnf.bck"
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mv /root/.my.cnf /root/.my.cnf.bck"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 重置所有 Controller 节点上的 Galera root 密码:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql -uroot -e'use mysql;set password for root@localhost = password(\"$ROOTPASSWORD\");flush privileges;'"
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql -uroot -e'use mysql;set password for root@localhost = password(\"$ROOTPASSWORD\");flush privileges;'"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在所有 Controller 节点上的 Galera 容器中恢复
.my.cnf
Galera 配置文件:sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mv /root/.my.cnf.bck /root/.my.cnf"
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mv /root/.my.cnf.bck /root/.my.cnf"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
Controller-0
上,将备份数据库文件复制到/var/lib/MySQL
:sudo cp $BACKUP_FILE /var/lib/mysql sudo cp $BACKUP_GRANT_FILE /var/lib/mysql
$ sudo cp $BACKUP_FILE /var/lib/mysql $ sudo cp $BACKUP_GRANT_FILE /var/lib/mysql
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意这些文件的路径为 /home/tripleo-admin/。
在
Controller-0
上,恢复 MySQL 数据库:sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql -u root -p$ROOT_PASSWORD < \"/var/lib/mysql/$BACKUP_FILE\" " sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql -u root -p$ROOT_PASSWORD < \"/var/lib/mysql/$BACKUP_GRANT_FILE\" "
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql -u root -p$ROOT_PASSWORD < \"/var/lib/mysql/$BACKUP_FILE\" " $ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysql -u root -p$ROOT_PASSWORD < \"/var/lib/mysql/$BACKUP_GRANT_FILE\" "
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 关闭所有 Controller 节点上的数据库:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysqladmin shutdown"
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "mysqladmin shutdown"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
Controller-0
上,启动 bootstrap 节点:sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle) \ /usr/bin/mysqld_safe --pid-file=/var/run/mysql/mysqld.pid --socket=/var/lib/mysql/mysql.sock --datadir=/var/lib/mysql \ --log-error=/var/log/mysql/mysql_cluster.log --user=mysql --open-files-limit=16384 \ --wsrep-cluster-address=gcomm:// &
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle) \ /usr/bin/mysqld_safe --pid-file=/var/run/mysql/mysqld.pid --socket=/var/lib/mysql/mysql.sock --datadir=/var/lib/mysql \ --log-error=/var/log/mysql/mysql_cluster.log --user=mysql --open-files-limit=16384 \ --wsrep-cluster-address=gcomm:// &
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 验证 :在 Controller-0 上,检查集群的状态:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "clustercheck"
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "clustercheck"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 确保显示以下信息:"Galera 集群节点同步",否则您必须重新创建该节点。
在
Controller-0
上,从配置检索集群地址:sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "grep wsrep_cluster_address /etc/my.cnf.d/galera.cnf" | awk '{print $3}'
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "grep wsrep_cluster_address /etc/my.cnf.d/galera.cnf" | awk '{print $3}'
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在每个剩余的 Controller 节点上,启动数据库并验证集群:
启动数据库:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) /usr/bin/mysqld_safe --pid-file=/var/run/mysql/mysqld.pid --socket=/var/lib/mysql/mysql.sock \ --datadir=/var/lib/mysql --log-error=/var/log/mysql/mysql_cluster.log --user=mysql --open-files-limit=16384 \ --wsrep-cluster-address=$CLUSTER_ADDRESS &
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) /usr/bin/mysqld_safe --pid-file=/var/run/mysql/mysqld.pid --socket=/var/lib/mysql/mysql.sock \ --datadir=/var/lib/mysql --log-error=/var/log/mysql/mysql_cluster.log --user=mysql --open-files-limit=16384 \ --wsrep-cluster-address=$CLUSTER_ADDRESS &
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查 MYSQL 集群的状态:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "clustercheck"
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" \ --filter=name=galera-bundle) bash -c "clustercheck"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 确保显示以下信息:"Galera 集群节点同步",否则您必须重新创建该节点。
停止所有 Controller 节点上的 MySQL 容器:
sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle) \ /usr/bin/mysqladmin -u root shutdown
$ sudo podman exec $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle) \ /usr/bin/mysqladmin -u root shutdown
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在所有 Controller 节点上,删除以下防火墙规则以允许通过虚拟 IP 地址进行数据库连接:
sudo iptables -D INPUT -p tcp --destination-port 3306 -d $MYSQL_VIP -j DROP
$ sudo iptables -D INPUT -p tcp --destination-port 3306 -d $MYSQL_VIP -j DROP
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 重启所有 Controller 节点上的 MySQL 容器:
sudo podman container restart $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle)
$ sudo podman container restart $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=galera-bundle)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 重启所有 Controller 节点上的
clustercheck
容器:sudo podman container restart $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=clustercheck)
$ sudo podman container restart $(sudo podman container ls --all --format "{{ .Names }}" --filter=name=clustercheck)
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
Controller-0
上,将 Galera 资源设置为受管
模式:sudo pcs resource manage galera-bundle
$ sudo pcs resource manage galera-bundle
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
验证
要确保服务正确运行,请检查 pacemaker 的状态:
sudo pcs status
$ sudo pcs status
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 要查看 overcloud 的状态,请使用 OpenStack Integration Test Suite (tempest)。如需更多信息,请参阅使用 Integration Test Suite (tempest)验证您的 OpenStack 云。
如果您怀疑特定节点有问题,请使用
clustercheck
检查集群的状态:sudo podman exec clustercheck /usr/bin/clustercheck
$ sudo podman exec clustercheck /usr/bin/clustercheck
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3.4. 手动恢复 undercloud 节点数据库 复制链接链接已复制到粘贴板!
如果 undercloud 数据库没有作为 undercloud 恢复过程的一部分恢复,您可以手动恢复数据库。只有之前创建了独立数据库备份时,才能恢复数据库。
先决条件
- 您已创建了 undercloud 数据库的独立备份。更多信息请参阅 第 2.1.6 节 “可选:创建 undercloud 节点的独立数据库备份”。
流程
-
以
root
用户身份登录 director undercloud 节点。 停止所有 tripleo 服务:
systemctl stop tripleo_*
[root@director ~]# systemctl stop tripleo_*
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 输入以下命令确保没有容器在服务器上运行:
podman ps
[root@director ~]# podman ps
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 如果有任何容器正在运行,请输入以下命令来停止容器:
podman stop <container_name>
[root@director ~]# podman stop <container_name>
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建当前
/var/lib/mysql
目录的备份,然后删除该目录:cp -a /var/lib/mysql /var/lib/mysql_bck rm -rf /var/lib/mysql
[root@director ~]# cp -a /var/lib/mysql /var/lib/mysql_bck [root@director ~]# rm -rf /var/lib/mysql
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 重新创建数据库目录并为新目录设置 SELinux 属性:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 为
mariadb
镜像创建一个本地标签。将<image_id
> 和 <undercloud.ctlplane.example.com
> 替换为环境中适用的值:podman images | grep mariadb
[root@director ~]# podman images | grep mariadb <undercloud.ctlplane.example.com>:8787/rh-osbs/rhosp16-openstack-mariadb 16.2_20210322.1 <image_id> 3 weeks ago 718 MB
Copy to Clipboard Copied! Toggle word wrap Toggle overflow podman tag <image_id> mariadb
[root@director ~]# podman tag <image_id> mariadb
Copy to Clipboard Copied! Toggle word wrap Toggle overflow podman images | grep maria
[root@director ~]# podman images | grep maria localhost/mariadb latest <image_id> 3 weeks ago 718 MB <undercloud.ctlplane.example.com>:8787/rh-osbs/rhosp16-openstack-mariadb 16.2_20210322.1 <image_id> 3 weeks ago 718 MB
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用容器初始化
/var/lib/mysql
目录:podman run --net=host -v /var/lib/mysql:/var/lib/mysql localhost/mariadb mysql_install_db --datadir=/var/lib/mysql --user=mysql
[root@director ~]# podman run --net=host -v /var/lib/mysql:/var/lib/mysql localhost/mariadb mysql_install_db --datadir=/var/lib/mysql --user=mysql
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 复制您要导入到数据库的数据库备份文件:
cp /root/undercloud-all-databases.sql /var/lib/mysql
[root@director ~]# cp /root/undercloud-all-databases.sql /var/lib/mysql
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 启动数据库服务来导入数据:
podman run --net=host -dt -v /var/lib/mysql:/var/lib/mysql localhost/mariadb /usr/libexec/mysqld
[root@director ~]# podman run --net=host -dt -v /var/lib/mysql:/var/lib/mysql localhost/mariadb /usr/libexec/mysqld
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 导入数据并配置
max_allowed_packet
参数:登录到容器并进行配置:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 停止容器:
podman stop <container_id>
[root@director ~]# podman stop <container_id>
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查没有容器是否正在运行:
podman ps
[root@director ~]# podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@director ~]#
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
重启所有 tripleo 服务:
systemctl start multi-user.target
[root@director ~]# systemctl start multi-user.target
Copy to Clipboard Copied! Toggle word wrap Toggle overflow