设为首页 收藏本站
查看: 989|回复: 0

[经验分享] Openstack 开发人员安装脚本解读 [stack.sh]

[复制链接]

尚未签到

发表于 2015-10-11 07:30:50 | 显示全部楼层 |阅读模式
这是转载的ju136的专栏的文章。http://devstack.org/  有一份英文版的源代码解读,不过如果对于Openstack没有过接触的人来说,可能看起来会有一些困难。
  http://devstack.org/stack.sh.html
  
stack.sh 简介

  stack.sh是一个openstack开发人员可以选择的一种安装脚本。
  

  这个脚本的主要作用是安装并配置nova, glance, horizon, 以及keystone。
  

  使用这个脚本,你可以设置一些配置选项,比如使用哪个git源,开启哪些服务,网络如何配置,以及各种密码的设置与管理。通过这个脚本,也可以灵活地在多台机器上运行,通过一个类似的资源进行一种共享式的配置。比如mysql, rabbitmq,从而可以组建一个多机环境下的云。
  

  为了保证这个脚本足够简单,我们假设你是在一个Ubuntu 11.10 Oneiric的机器上执行。当然,这个脚本也可以在VM上运行,或是在物理机上运行。此外,我们把一些apt, pip安装包放到别的文件中去了,那么开始了解这个脚本,以及这些依赖的包。
  

  如果你想进一步的学习或了解,可以去网站
  http://devstack.org
  
Sanity Check

这个脚本首先来说是针对于Oneiric来写的,如果要在Ubuntu的其他版本上执行,当然也可以通过执行命令  
  
[cpp] viewplaincopy

  • $FORCE=yes ./stack.sh  
  ,从而达到要求。但是我个人而言,不建议采用这种方式进来安装部署。在其他版本上的安装部署,后面再补上。
  

  版本检查

  
  下面这段代码就是主要是在检查一下OS的版本。
  # DISTRO变量就是拿到OS的版本。在Ubuntu上执行,会得到发行版的名字。oneiric。
  # 接下来的几句只是在进行检查是否是oneiric版本的Ubuntu

  
[cpp] viewplaincopy

  • DISTRO=$(lsb_release -c -s)  
  •   
  • if [[ ! ${DISTRO} =~ (oneiric) ]]; then  
  •     echo "WARNING: this script has only been tested on oneiric"  
  •     if [[ "$FORCE" != "yes" ]]; then  
  •         echo "If you wish to run this script anyway run with FORCE=yes"  
  •         exit 1  
  •     fi  
  • fi  

  
  目录设定
  stack.sh把很多需要安装的包与其他一些依赖都放到别的文件里面的,你可以在/devstack/files中找到这些文件。但是在脚本中,我们是通过FILES变量来引用到这些配置文件的位置。
  

  #首先是拿到顶级目录所在位置。比如/root/devstack,那么$TOP_DIR="/root/devstack"
  #生成FILES变量的值。再去检查一下这个目录是否存在。

  
[cpp] viewplaincopy

  • TOP_DIR=$(cd $(dirname "$0") && pwd)  
  •   
  • FILES=$TOP_DIR/files  
  • if [ ! -d $FILES ]; then  
  •     echo "ERROR: missing devstack/files - did you grab more than just stack.sh?"  
  •     exit 1  
  • fi   
  

  
  设定
  这个脚本的可定制性体现在,一些环境变量可以放到别的文件中,或者是通过设定环境变量来进行处理。比如
  
[cpp] viewplaincopy

  • $export MYSQL_PASSWORD=anothersecret  
  • $./stack.sh  

可以达到效果。当然,你也可以把这些语句放在同一行,比如  
  
[cpp] viewplaincopy

  • $MYSQL_PASSWORD=simple ./stack.sh  
也可以把这些设定放到$TOP_DIR/localrc文件中。  
  比如:
  
[cpp] viewplaincopy

  • $ cat $TOP_DIR/localrc  
  • .........  
  • MYSQL_PASSWORD=anothersecret  
  • MYSQL_USER=hellaroot  
一般来说,我们都使用的是一些很敏感的设定,所以为了省事,你也可以运行  
  
[cpp] viewplaincopy

  • $./stack.sh  
(一般而言,如果这样运行,脚本会在发现没有密码的时候,叫你输出密码,如果输入密码为空,那么会随机生成一个密码)。  
  
  环境变量
  对于环境变量而言,一般我们都是放到stackrc文件中。这个文件是随着devstack一起发布的,并且主要是包含了需要使用的git源。如果你想要使用别的源,或者是别的分枝,那么你可以加入你自己的设定,并把这些设定写到localrc文件中。
  

  一般而言localrc里面的设置会把stackrc文件中的变量给覆盖掉。这个是很有用的,特别是当你想使用别的源或者分枝的时候。你也可以更改掉一些已有设定,比如
  
[cpp] viewplaincopy

  • MYSQL_PASSWORD  
  • ADMIN_PASSWORD  
  
  否则devstack脚本会给你随机生成一个(这个随机生成的密码还有点长)。
  

  #首先是从stackrc中引入环境变量
  #$DEST变量是指的是安装目录,一般默认安装目录是/opt/stack
  #下面是定义了一个函数,其实也就是使用apt-get命令。

  
[cpp] viewplaincopy

  • source ./stackrc  
  •   
  • DEST=${DEST:-/opt/stack}  
  •   
  • function apt_get() {  
  •     [[ "$OFFLINE" = "True" ]] && return  
  •     local sudo="sudo"  
  •     [ "$(id -u)" = "0" ] && sudo="env"  
  •     $sudo DEBIAN_FRONTEND=noninteractive apt-get \  
  •         --option "Dpkg::Options::=--force-confold" --assume-yes "$@"  
  • }  


  
  这里检查是否已经有stack.sh程序在运行。
  
[cpp] viewplaincopy

  • if screen -ls | egrep -q "[0-9].stack"; then  
  •     echo "You are already running a stack.sh session."  
  •     echo "To rejoin this session type 'screen -x stack'."  
  •     echo "To destroy this session, kill the running screen."  
  •     exit 1  
  • fi  

因为Openstack在设计的时候,是用的一般用户来运行的,主要原因是因为dashboard在运行的时候底层使用的是apache2的服务器,而apache2不可以在root权限下执行。  
  如果在运行脚本的时候是使用的是root权限,那么会自动新建一个stack用户来执行操作。并且这个用户会给足够的权限来进行很多操作

  
[cpp] viewplaincopy

  • if [[ $EUID -eq 0 ]]; then  
  •     ROOTSLEEP=${ROOTSLEEP:-10}  
  •     echo "You are running this script as root."  
  •     echo "In $ROOTSLEEP seconds, we will create a user 'stack' and run as that user"  
  •     sleep $ROOTSLEEP  
  •   
  •     # 因为这个脚本接下来是按照一个普通用户来执行的,那么需要给这个普通用户以sudo的权限。  
  •     #   
  •     dpkg -l sudo || apt_get update && apt_get install sudo  
  •   
  •     if ! getent passwd stack >/dev/null; then  
  •         echo "Creating a user called stack"  
  •         useradd -U -G sudo -s /bin/bash -d $DEST -m stack  
  •     fi  
  •   
  •     echo "Giving stack user passwordless sudo priviledges"  
  •     # some uec images sudoers does not have a '#includedir'. add one.  
  •     grep -q "^#includedir.*/etc/sudoers.d" /etc/sudoers ||  
  •         echo "#includedir /etc/sudoers.d" >> /etc/sudoers  
  •     ( umask 226 && echo "stack ALL=(ALL) NOPASSWD:ALL" \  
  •         > /etc/sudoers.d/50_stack_sh )  
  •   
  •     echo "Copying files to stack user"  
  •     STACK_DIR="$DEST/${PWD##*/}"  
  •     cp -r -f -T "$PWD" "$STACK_DIR"  #这里在拷文件,额外的拷文件的动作也可以加在这里。  
  •     chown -R stack "$STACK_DIR"  
  •     if [[ "$SHELL_AFTER_RUN" != "no" ]]; then  
  •         exec su -c "set -e; cd $STACK_DIR; bash stack.sh; bash" stack  
  •     else  
  •         exec su -c "set -e; cd $STACK_DIR; bash stack.sh" stack  
  •     fi  
  •     exit 1  
  • else  
  •     # Our user needs passwordless priviledges for certain commands which nova  
  •     # uses internally.  
  •     # Natty uec images sudoers does not have a '#includedir'. add one.  
  •     sudo grep -q "^#includedir.*/etc/sudoers.d" /etc/sudoers ||  
  •         echo "#includedir /etc/sudoers.d" | sudo tee -a /etc/sudoers  
  •     TEMPFILE=`mktemp`  
  •     cat $FILES/sudo/nova > $TEMPFILE  
  •     sed -e "s,%USER%,$USER,g" -i $TEMPFILE  
  •     chmod 0440 $TEMPFILE  
  •     sudo chown root:root $TEMPFILE  
  •     sudo mv $TEMPFILE /etc/sudoers.d/stack_sh_nova  
  • fi  
  
  接下来这个就是设置是否是用离线的方式来进行安装,或者是用上网的方式来进行安装。
  
[cpp] viewplaincopy

  • function trueorfalse() {  
  •     local default=$1  
  •     local testval=$2  
  •   
  •     [[ -z "$testval" ]] && { echo "$default"; return; }  
  •     [[ "0 no false False FALSE" =~ "$testval" ]] && { echo "False"; return; }  
  •     [[ "1 yes true True TRUE" =~ "$testval" ]] && { echo "True"; return; }  
  •     echo "$default"  
  • }  
  • OFFLINE=`trueorfalse False $OFFLINE`   

一般来说,安装方式都是在线的方式来安装的。如果设置的值是True,那么就是使用离线的方式来进行安装。  
  配置服务

  

  接下来是设置一些环境变量:这些环境变量主要都是为了安装某个部件而设置的。
  
[cpp] viewplaincopy

  • NOVA_DIR=$DEST/nova  
  • HORIZON_DIR=$DEST/horizon  
  • GLANCE_DIR=$DEST/glance  
  • KEYSTONE_DIR=$DEST/keystone  
  • NOVACLIENT_DIR=$DEST/python-novaclient  
  • OPENSTACKX_DIR=$DEST/openstackx  
  • NOVNC_DIR=$DEST/noVNC  
  • SWIFT_DIR=$DEST/swift  
  • SWIFT_KEYSTONE_DIR=$DEST/swift-keystone2  
  • QUANTUM_DIR=$DEST/quantum  
  
  下面的设置是为了Quantum而进行设置。
  
[cpp] viewplaincopy

  • # Default Quantum Plugin  #看样子这个应该是一个网络服务  
  • Q_PLUGIN=${Q_PLUGIN:-openvswitch}  
  • # Default Quantum Port  #这里设置端口  
  • Q_PORT=${Q_PORT:-9696}  
  • # Default Quantum Host  #这里设置主机  
  • Q_HOST=${Q_HOST:-localhost}  

接下来设置哪些服务要开启。注意这里用到的都是缩写。  
  
[cpp] viewplaincopy

  • ENABLED_SERVICES=${ENABLED_SERVICES:-g-api,g-reg,key,n-api,n-cpu,n-net,n-sch,n-vnc,horizon,mysql,rabbit,openstackx}  
  
  下面是设置volume服务
  
[cpp] viewplaincopy

  • # Name of the lvm volume group to use/create for iscsi volumes  
  • VOLUME_GROUP=${VOLUME_GROUP:-nova-volumes}  
  • VOLUME_NAME_PREFIX=${VOLUME_NAME_PREFIX:-volume-}  
  • INSTANCE_NAME_PREFIX=${INSTANCE_NAME_PREFIX:-instance-}  

  接下来是设置需要使用到的底层的服务
  
[cpp] viewplaincopy

  • VIRT_DRIVER=${VIRT_DRIVER:-libvirt}  
  • LIBVIRT_TYPE=${LIBVIRT_TYPE:-kvm}  
一般而言我们都是使用kvm+libvirt。如果kvm没有驱动起来,我们会使用qemu。  
  

  设置要使用到的Scheduler
  
[cpp] viewplaincopy

  • SCHEDULER=${SCHEDULER:-nova.scheduler.simple.SimpleScheduler}  

接下来是拿到主机IP地址。  
  
[cpp] viewplaincopy

  • if [ ! -n "$HOST_IP" ]; then  
  •     HOST_IP=`LC_ALL=C /sbin/ifconfig eth0 | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'`  
  •     if [ "$HOST_IP" = "" ]; then  
  •         echo "Could not determine host ip address."  
  •         echo "If this is not your first run of stack.sh, it is "  
  •         echo "possible that nova moved your eth0 ip address to the FLAT_NETWORK_BRIDGE."  
  •         echo "Please specify your HOST_IP in your localrc."  
  •         exit 1  
  •     fi  
  • fi  

这里是设置一下是否使用系统日志:  
  
[cpp] viewplaincopy

  • SYSLOG=`trueorfalse False $SYSLOG`  
  • SYSLOG_HOST=${SYSLOG_HOST:-$HOST_IP}  
  • SYSLOG_PORT=${SYSLOG_PORT:-516}  

服务超时设定  
  
[cpp] viewplaincopy

  • SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}  

接下这个函数就是为了读取密码而写的。  
  
[cpp] viewplaincopy

  • function read_password {  
  •     set +o xtrace  
  •     var=$1; msg=$2  
  •     pw=${!var}  
  •   
  •     localrc=$TOP_DIR/localrc  
  •   
  •     # If the password is not defined yet, proceed to prompt user for a password.  
  •     if [ ! $pw ]; then  
  •         # If there is no localrc file, create one  
  •         if [ ! -e $localrc ]; then  
  •             touch $localrc  
  •         fi  
  •   
  •         # Presumably if we got this far it can only be that our localrc is missing  
  •         # the required password.  Prompt user for a password and write to localrc.  
  •         echo ''  
  •         echo '################################################################################'  
  •         echo $msg  
  •         echo '################################################################################'  
  •         echo "This value will be written to your localrc file so you don't have to enter it again."  
  •         echo "It is probably best to avoid spaces and weird characters."  
  •         echo "If you leave this blank, a random default value will be used."  
  •         echo "Enter a password now:"  
  •         read -e $var  
  •         pw=${!var}  
  •         if [ ! $pw ]; then  
  •             pw=`openssl rand -hex 10`  
  •         fi  
  •         eval "$var=$pw"  
  •         echo "$var=$pw" >> $localrc  
  •     fi  
  •     set -o xtrace  
  • }  

你如果想设置成为一个固定的密码,那么这里可以改为如下:  
  
[cpp] viewplaincopy

  • # Presumably if we got this far it can only be that our localrc is missing  
  • # the required password.  Prompt user for a password and write to localrc.  
  • echo "Set password to nova:"  
  • pw="nova"  
  • eval "$var=$pw"  
  • echo "$var=$pw" >> $localrc  

为了简单起见,后面会一直使用nova做为密码。(自己安装着玩的时候这么干比较好,如果正式用的话,还是用一个靠谱的密码吧。)  
  

  设置网络:这些设定应该最后都会写到/etc/nova/nova.conf文件中的。不过这里好像是放在安装目录中。

  
[cpp] viewplaincopy

  • PUBLIC_INTERFACE=${PUBLIC_INTERFACE:-eth0}  
  • FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24}  
  • FIXED_NETWORK_SIZE=${FIXED_NETWORK_SIZE:-256}  
  • FLOATING_RANGE=${FLOATING_RANGE:-172.24.4.224/28}  
  • NET_MAN=${NET_MAN:-FlatDHCPManager}  
  • EC2_DMZ_HOST=${EC2_DMZ_HOST:-$HOST_IP}  
  • FLAT_NETWORK_BRIDGE=${FLAT_NETWORK_BRIDGE:-br100}  
  • VLAN_INTERFACE=${VLAN_INTERFACE:-$PUBLIC_INTERFACE}  
  
  

  下面是设置是否使用多节点模式,如果使用多节点模式,那么每个计算节点都会与网络节点都是一体的,访问相应的VM的时候,直接访问相应的物理机则可。
  这种模式可以解除SPOF和带宽的制约。
  
[cpp] viewplaincopy

  • MULTI_HOST=${MULTI_HOST:-False}  

下面一段话比较长,不过也可以看一下:  
  如果你是在多台物理机上使用FlatDHCP模式,设置"FLAT_INTERFACE"变量,并且要保证这个interface并没有一个IP。否则你可能会破坏一些东西。
  

  "*DHCP Warning" 这种情况,如果你使用flat interface设备使用DHCP,发生下面这种情况的时候,可能会让你呆住,
  网络从flat interface上移到flat network bridge。这种情况完全有可能发生。结果是当你创建第一台虚拟机的时候。这种情况会导致你的虚拟机出问题,比如连接不上,或者登陆失败。

  

  如果你只是装着单机版的来玩玩,那么你可以使用FLAT_NETWORK_BRIDGE。
  
[cpp] viewplaincopy

  • FLAT_INTERFACE=${FLAT_INTERFACE:-eth0}  

quantum网络  
  你需要确保quantum是在Enabled_services中是开启的。假如network manager设置成为QuantumManager。
  假如你想在这台机器上运行Quantum,那么最好在ENABLED_SERVICES中要找到q-svc字段。
  如果你想在quantum中使用openswitch插件,那么把 Q_PLUGIN 的值设置为openvswitch。并且需要保证q-agt服务也是在开启的服务之列。
  
如果你设置了Quantum网络,那么NET_MAN变量的值就被忽略了。
  

  MYSQL 和 RabbitMQ
  在这里,我们配置的目标是让Nova, Horizon, Glance, 与Keystone都使用MySQL做为其数据库。这里他们主要是使用一个单一的服务,但是每个服务都是有他们自己的表数据库与表单。
  一般而言,这里的脚本会安装和配置MySQL。如果你想用一个已经有的服务,那么你需要传递/user/password/host参数。也同时需要传递MYSQL_PASSWORD参数到每个节点(当你使用的单台机器的时候,这一步就省了吧)。一般而言我都是采用一种最简单的方式,也就是一台服务器,也都使用这个服务器上的MySQL数据库。

  

  下面的三行代码分别是设置MySQL的机器地址与用户名与密码。在这里是需要手动输入密码。
  
[cpp] viewplaincopy

  • MYSQL_HOST=${MYSQL_HOST:-localhost}  
  • MYSQL_USER=${MYSQL_USER:-root}  
  • read_password MYSQL_PASSWORD "ENTER A PASSWORD TO USE FOR MYSQL."  
  
  下面再给出一个链接,这个链接主要是用于连接MYSQL数据库。一般不需要去加上所需要连接的DataBase,因为其他几个服务都是需要通过这个链接去连MySQL。但是每个服务连接的数据库是不一样的。所以如果是用同一样的链接+数据库名,肯定会出错。一句话,保持下面这个不变就可以了。
  
[cpp] viewplaincopy

  • BASE_SQL_CONN=${BASE_SQL_CONN:-mysql://$MYSQL_USER:$MYSQL_PASSWORD@$MYSQL_HOST}  
  
  设置RabbitMQ的密码
  
[cpp] viewplaincopy

  • RABBIT_HOST=${RABBIT_HOST:-localhost}  
  • read_password RABBIT_PASSWORD "ENTER A PASSWORD TO USE FOR RABBIT."  

接下来指出Glance的主机与端口,值得注意的是这里需要给出端口。这个端口按照原文好像是需要指定的,特定的这么个端口(总之保持不变就是了).。  
  
[cpp] viewplaincopy

  • GLANCE_HOSTPORT=${GLANCE_HOSTPORT:-$HOST_IP:9292}  
  
  Swift设置
  需要完善的功能:
  TODO:实现对于glance的支持。
  TODO:   加入对于不同存储位置的日志功能。
  一般来说,swift驱动的位置与存储对象是放在swift源码的内部文件夹中。当然也可以自已指定一个目录。
  
[cpp] viewplaincopy

  • SWIFT_DATA_LOCATION=${SWIFT_DATA_LOCATION:-${SWIFT_DIR}/data}  

同样的道里,swift的配置文件也是放在源码内部的。当然也可以根据你自己的情况做出调整。  
  
[cpp] viewplaincopy

  • SWIFT_CONFIG_LOCATION=${SWIFT_CONFIG_LOCATION:-${SWIFT_DIR}/config}  

接下来,devstack需要建立一个loop-back的磁盘,这个磁盘会被格式化为XFS格式,并且用来存储swift数据。一般来说,这个磁盘的大小是1G。变量SWIFT_LOOPBACK_DISK_SIZE指出了这个磁盘的大小。当然你也可以根据自己的情况来调整大小。  
  
[cpp] viewplaincopy

  • SWIFT_LOOPBACK_DISK_SIZE=${SWIFT_LOOPBACK_DISK_SIZE:-1000000}  
接下来是swift需要读入一个密码。这一段比较难以理解(主查涉及到swift内部的一些原理与实现)。  
  
[cpp] viewplaincopy

  • # The ring uses a configurable number of bits from a path’s MD5 hash as  
  • # a partition index that designates a device. The number of bits kept  
  • # from the hash is known as the partition power, and 2 to the partition  
  • # power indicates the partition count. Partitioning the full MD5 hash  
  • # ring allows other parts of the cluster to work in batches of items at  
  • # once which ends up either more efficient or at least less complex than  
  • # working with each item separately or the entire cluster all at once.  
  • # By default we define 9 for the partition count (which mean 512).  
  • SWIFT_PARTITION_POWER_SIZE=${SWIFT_PARTITION_POWER_SIZE:-9}  
  •   
  • # We only ask for Swift Hash if we have enabled swift service.  
  • if [[ "$ENABLED_SERVICES" =~ "swift" ]]; then  
  •     # SWIFT_HASH is a random unique string for a swift cluster that  
  •     # can never change.  
  •     read_password SWIFT_HASH "ENTER A RANDOM SWIFT HASH."  
  • fi  

Keystone  
  服务口令:Openstack的组件需要有一个管理员口令来认证用户的口令。
  下面则是读入一个密码:
  
[cpp] viewplaincopy

  • read_password SERVICE_TOKEN "ENTER A SERVICE_TOKEN TO USE FOR THE SERVICE ADMIN TOKEN."  

同样也需要为Horizon输入一个密码。  
  
[cpp] viewplaincopy

  • read_password ADMIN_PASSWORD "ENTER A PASSWORD TO USE FOR HORIZON AND KEYSTONE (20 CHARS OR LESS)."  

接下来是设置日志  
  
[cpp] viewplaincopy

  • LOGFILE=${LOGFILE:-"$PWD/stack.sh.$.log"}  
  • (  
  • # So that errors don't compound we exit on any errors so you see only the  
  • # first error that occurred.  
  • trap failed ERR  
  • failed() {  
  •     local r=$?  
  •     set +o xtrace  
  •     [ -n "$LOGFILE" ] && echo "${0##*/} failed: full log in $LOGFILE"  
  •     exit $r  
  • }  

接下来的这个命令比较有用,就是打出每个命令,以及这个命令的输出,主要是用于查看在哪里出错了。以便及时查错。  
  
[cpp] viewplaincopy

  • set -o xtrace  

创建目标目录,并且要保证stack用户对于这个目录是可写的。  
  
[cpp] viewplaincopy

  • sudo mkdir -p $DEST  
  • if [ ! -w $DEST ]; then  
  •     sudo chown `whoami` $DEST  
  • fi  
  
  前面讲了这么多,其实都是在讲怎么配置。接下来才是要进行人工操作阶段。

  
安装依赖包
  Openstack用了很多其他的工程。所以需要安装其他工程。
  

  注意的是:
  我们只安装对于我们的服务来说是必须的包。
  

  

  

  
  

  脚本学习---得到操作系统发行版的方法

  方法一

[python] viewplaincopy

  • DISTRO=$(lsb_release -c -s)  
  可以用来得到操作系统的发行版。
  缺点
  虽然这种方法可以在ubuntu上得到类似于oneiric的字样(如果你的发行版不同,那么得到的也肯定不一样)。
  至于其他版本,则得到的结果就有点奇怪了。比如我在suse上运行,是得不到相应的结果的。
[python] viewplaincopy

  • $ lsb_release -c -s  
  • n/a  

  方法二
  其实操作系统发行版的信息是写在/etc/issue 和/etc/issue.net文件中。为了方便我们用如下命令去查看吧。
[python] viewplaincopy

  • $ cat /etc/issue*  
在ubuntu下的运行结果如下:[python] viewplaincopy

  • $ cat /etc/issue*  
  • Ubuntu 11.10 \n \l  
  •   
  • Ubuntu 11.10  

在SUSE下面运行有如下结果:  

[python] viewplaincopy

  • $ cat /etc/issue*  
  • Welcome to SUSE Linux Enterprise Server 11 SP1  (x86_64) - Kernel \r (\l).  
  •   
  •   
  • Welcome to SUSE Linux Enterprise Server 11 SP1  (x86_64) - Kernel %r (%t).  
  为了简单起见,我们需要把这些字符串转化成为小写,再进行判断。因为有时候有的发行版在说明的时候会写SUSE, 而Ubuntu的写的是Ubuntu,并不是全部都是大写。
  我们用如下脚本来进行测试是否是suse系统
[python] viewplaincopy

  • <span style=&quot;color:#3333FF;&quot;>if [[ `cat $DISTRO | grep suse | wc -l` -eq 0 ]]; then  
  •   echo &quot;Not suse&quot;  
  •   exit 1  
  • fi</span>  
  
  
  脚本学习----如何得到脚本运行时所在目录。
  当在问这个问题的时候,很有可能会回答说是pwd,在C语言里用的是getcwd()函数。实际上,pwd这个命令返回&#20540;是运行命令时所在路径,而不是脚本所在目录。
  如果对于这个问题不是很了解。那么换个问题就是,一个脚本,如何知道自己所在的目录?这个脚本可以被放在很多位置。
  方法一

  第一种方法就是这个stack.sh脚本中所提到的,
[python] viewplaincopy

  • TOP_DIR=$(cd $(dirname &quot;$0&quot;) && pwd)  
一开始看时,可能不太明白其含义,现解释如下:[python] viewplaincopy

  • $0 这个只能是在脚本里面使用,在命令行里面只会输出-bash。$0表示的意思是运行脚本的全路径。  
  •   
  • dirname这个命令就是截取全路径中的目录部分比如  
  • $ dirname /root/stack/nova.sh  
  • /root/stack  
  •   
  • 实际上,在脚本中,使用dirname $0可能就已经够用了。之所以再用cd && pwd我觉得主要的原因是怕$0给出的不是全路径。  

方法二  来个简洁版的,
[python] viewplaincopy

  • TOP_DIR=`dirname $0`  
从而可以确定脚本所在目录。  

  脚本学习---如何取得全路径中的路径
  方法一
  dirname $par
  方法二
  pkg_dir=${par%/*}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-125226-1-1.html 上篇帖子: openstack排错经历 下篇帖子: openstack_ice之wsgi详解(paste从ini配置文件->routesr具体发布流程)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表