Magnum 故障排除指南

本指南面向使用 Magnum 部署和管理容器编排引擎主机集群的用户。它描述了常见的故障情况和故障排除技术。为了帮助用户快速找到相关信息,本指南以故障症状列表的形式组织:每个症状都附有一些建议,并指向故障排除的详细信息。

还有一个单独的部分 针对开发人员,描述了有用的技术,例如调试单元测试和门控测试。

故障症状

我的 cluster-create 花费的时间太长

如果您在小型虚拟机上使用 devstack,cluster-create 将花费很长时间,并且最终可能由于资源不足而失败。另一个可能的原因是其中一个节点上的进程卡住,并且 heat 仍在等待信号。在这种情况下,它最终会因超时而失败,但由于 heat 具有较长的默认超时时间,您可以查看 heat 堆栈 并检查 WaitConditionHandle 资源。

我的 cluster-create 因错误而失败:“Failed to create trustee XXX in domain XXX”

检查 集群的 trustee

Kubernetes cluster-create 失败

检查 heat 堆栈,登录到 master 节点并检查 Kubernetes 服务etcd 服务

在部署 pod 时,我收到错误“Timed out waiting for a reply”

验证 Kubernetes 服务etcd 服务 是否正在 master 节点上运行。

我在 Kubernetes 集群上部署 pod,但状态一直保持“Pending”

pod 状态为“Pending”,同时 Docker 镜像正在下载,因此如果状态长时间没有改变,请登录到 minion 节点并检查 集群互联网访问

我在 Kubernetes 集群上部署 pod 和服务,但应用程序无法正常工作

pod 和服务正在运行,状态看起来正确,但如果应用程序通过服务在 pod 之间进行通信,请验证 Kubernetes 网络

在部署容器时,我收到“Protocol violation”错误

对于 Kubernetes,请检查 Kubernetes 服务,以验证 kube-apiserver 是否正在运行以接受请求。检查 TLSBarbican 服务

我的 cluster-create 因 docker_volume 上的资源错误而失败

检查 Cinder 上的可用卷空间以及 heat 模板中的 请求卷大小。运行“nova volume-list”以检查卷状态。

故障排除详细信息

Heat 堆栈

待填充

集群由一组 heat 堆栈部署:一个顶级堆栈和几个嵌套堆栈。堆栈名称以集群名称为前缀,嵌套堆栈名称包含描述性的内部名称,如 kube_masterskube_minions

要列出集群所有堆栈的状态

heat stack-list -n | grep cluster-name

如果集群失败,则一个或多个 heat 堆栈将失败。从上面的堆栈列表中,查找失败的堆栈,然后通过以下方式查找失败堆栈中失败的特定资源:

heat resource-list failed-stack-name | grep “FAILED”

失败资源的 resource_type 应指向 OpenStack 服务,例如 OS::Cinder::Volume。通过以下方式检查有关失败的更多详细信息:

heat resource-show failed-stack-name failed-resource-name

resource_status_reason 可能会提示失败原因,尽管在某些情况下它可能只显示“Unknown”。

如果失败的资源是 OS::Heat::WaitConditionHandle,则表示正在节点上启动的其中一项服务卡住。登录到发生故障的节点并检查相应的 Kubernetes 服务。如果故障发生在其他脚本中,请将其作为 Heat 软件资源脚本查找。

集群的 trustee

当用户创建集群时,Magnum 将动态创建一个服务帐户用于该集群。该服务帐户将用于集群访问 OpenStack 服务(即 Neutron、Swift 等)。将在创建集群的用户(“trustor”)和为集群创建的服务帐户(“trustee”)之间创建信任关系。

如果 Magnum 无法创建 trustee,请检查 magnum 配置文件(通常位于 /etc/magnum/magnum.conf)。确保 ‘trustee_*’ 和 ‘www_authenticate_uri’ 已设置且其值正确

[keystone_authtoken] www_authenticate_uri = http://controller:5000/v3

[trust] trustee_domain_admin_password = XXX trustee_domain_admin_id = XXX trustee_domain_id = XXX

如果缺少 ‘trust’ 组,您可能需要创建 trustee 域和域管理员

. /opt/stack/devstack/accrc/admin/admin
export OS_IDENTITY_API_VERSION=3
unset OS_AUTH_TYPE
openstack domain create magnum
openstack user create trustee_domain_admin --password secret \
    --domain magnum
openstack role add --user=trustee_domain_admin --user-domain magnum \
    --domain magnum admin

. /opt/stack/devstack/functions
export MAGNUM_CONF=/etc/magnum/magnum.conf
iniset $MAGNUM_CONF trust trustee_domain_id \
    $(openstack domain show magnum | awk '/ id /{print $4}')
iniset $MAGNUM_CONF trust trustee_domain_admin_id \
    $(openstack user show trustee_domain_admin | awk '/ id /{print $4}')
iniset $MAGNUM_CONF trust trustee_domain_admin_password secret

然后,重新启动 magnum-api 和 magnum-cond 以获取新的配置。如果问题仍然存在,您可能需要手动验证您的域管理员凭据以确保它具有正确的权限。为此,请使用替换后的凭据运行以下脚本(您必须在指定的位置使用 ID)。如果失败,则表示您提供的凭据无效。

from keystoneauth1.identity import v3 as ka_v3
from keystoneauth1 import session as ka_session
from keystoneclient.v3 import client as kc_v3

auth = ka_v3.Password(
    auth_url=YOUR_AUTH_URI,
    user_id=YOUR_TRUSTEE_DOMAIN_ADMIN_ID,
    domain_id=YOUR_TRUSTEE_DOMAIN_ID,
    password=YOUR_TRUSTEE_DOMAIN_ADMIN_PASSWORD)

session = ka_session.Session(auth=auth)
domain_admin_client = kc_v3.Client(session=session)
user = domain_admin_client.users.create(
    name='anyname',
    password='anypass')

TLS

在生产部署中,操作员使用 ssl 证书运行 OpenStack API,并且在私有云中,通常使用自签名或由通常未包含在系统默认 CA 捆绑包中的 CA 签名的证书。启用 TLS 的 Magnum 集群拥有自己的 CA,但出于多种原因,它们需要向 OpenStack API 发出请求。例如,获取集群 CA 并签署节点证书(Keystone、Magnum),发出堆栈完成信号(Heat API),创建资源(卷、负载均衡器)或获取每个节点的信息(Cinder、Neutron、Nova)。在这些情况下,集群节点需要用于运行 API 的 CA。

要将 OpenStack CA 捆绑包传递给节点,可以使用 drivers 部分的 Magnum 配置文件(通常是 /etc/magnum/magnum.conf)中的 openstack_ca_file 选项。magnum 中的默认驱动程序会将此 CA 安装到系统中并在可能需要它的所有位置设置它(例如,在配置 kubernetes 云提供程序或 heat-agents 时)。

集群节点默认情况下会在向 OpenStack API(Keystone、Magnum、Heat)发出请求时验证证书颁发机构。如果您需要禁用 CA 验证,可以将配置参数 verify_ca 设置为 False。有关 CA 验证的更多信息。

Barbican 服务

待填充

集群互联网访问

Kubernetes 的节点连接到私有 Neutron 网络,因此为了提供对外部互联网的访问,路由器将私有网络连接到公共网络。使用 devstack 时,默认公共网络是“public”,但可以通过 ClusterTemplate 中的“external-network”参数替换。devstack 中的“public”网络实际上不是真正的外部网络,因此它反过来路由到 devstack 主机的网络接口。这在 local.conf 文件中用变量 PUBLIC_INTERFACE 配置,例如

PUBLIC_INTERFACE=eth1

如果未正确设置到外部互联网的路由,ectd 发现将失败(如果使用公共发现),并且无法下载容器镜像,以及其他故障。

首先,通过 ping 外部 IP(此处显示的 IP 只是一个示例;使用您案例中有效的 IP)来检查与外部互联网的连接

ping 8.8.8.8

如果 ping 失败,则没有到外部互联网的路由。检查以下内容

  • devstack/local.conf 中的 PUBLIC_INTERFACE 是否是正确的网络接口?此接口是否有到外部互联网的路由?

  • 如果 ClusterTemplate 中指定了“external-network”,此网络是否有到外部互联网的路由?

  • 您的 devstack 环境是否位于防火墙后面?对于某些企业或国家/地区可能是这种情况。在这种情况下,请考虑使用 代理服务器

  • 安全组是否阻止了流量?检查 安全组规则

  • 您的主机是否正确 NAT 您的内部网络?检查您的主机 iptables

  • 使用 tcpdump 进行 网络故障排除。您可以在节点上的接口 docker0, flannel0eth0 上运行 tcpdump,然后运行 ping 以查看消息从容器的路径。

如果 ping 成功,请检查 DNS 是否正常工作

wget google.com

如果 DNS 正常工作,您应该会收到一些 HTML 文本行。

如果名称查找失败,请检查以下内容

  • 私有子网的 dns_nameservers 是否正确?尝试“neutron subnet-show <subnet-id>”并检查 dns_nameservers。IP 应该是默认公共 DNS 8.8.8.8 或 ClusterTemplate 中指定的“dns-nameserver”值。

  • 如果您通过在 ClusterTemplate 中指定“dns-nameserver”来使用自己的 DNS 服务器,它是否可访问且正常工作?

  • 有关 DNS 故障排除的更多帮助。

Kubernetes 网络

pod 之间的网络与为集群设置的 neutron 网络不同。Kubernetes 为 pod 和服务提供了一个扁平的网络空间,并使用不同的网络驱动程序来提供这种网络模型。

pod 可以正确启动并能够连接到外部互联网,但它们无法相互访问是有可能的。在这种情况下,pod 中的应用程序可能无法按预期工作。例如,如果您正在尝试 redis 示例,则 key:value 可能无法正确复制。在这种情况下,请使用以下步骤来验证 inter-pods 网络并确定问题。

由于步骤特定于网络驱动程序,请参阅集群正在使用的特定驱动程序。

使用 Flannel 作为网络驱动程序

Flannel 是 Kubernetes 集群的默认网络驱动程序。Flannel 是在 neutron 网络之上运行的覆盖网络。它的工作原理是封装 pod 之间的消息并将它们转发到托管目标 pod 的正确节点。

首先检查节点级别的连接。登录到两个不同的 minion 节点,例如节点 A 和节点 B,在每个节点上运行一个 docker 容器,连接到容器并找到 IP。

例如,在节点 A 上

sudo docker run -it alpine
# ip -f inet -o a | grep eth0 | awk '{print $4}'
10.100.54.2/24

同样,在节点 B 上

sudo docker run -it alpine
# ip -f inet -o a | grep eth0 | awk '{print $4}'
10.100.49.3/24

通过从一个 ping 另一个来检查容器是否可以看到彼此。

在节点 A 上

# ping 10.100.49.3
PING 10.100.49.3 (10.100.49.3): 56 data bytes
64 bytes from 10.100.49.3: seq=0 ttl=60 time=1.868 ms
64 bytes from 10.100.49.3: seq=1 ttl=60 time=1.108 ms

同样,在节点 B 上

# ping 10.100.54.2
PING 10.100.54.2 (10.100.54.2): 56 data bytes
64 bytes from 10.100.54.2: seq=0 ttl=60 time=2.678 ms
64 bytes from 10.100.54.2: seq=1 ttl=60 time=1.240 ms

如果 ping 不成功,请检查以下内容

  • Neutron 是否正常工作?尝试在虚拟机之间 ping。

  • 节点上的 docker0 和 flannel0 接口是否配置正确?登录到每个节点并按以下方式查找 Flannel CIDR:

    cat /run/flannel/subnet.env | grep FLANNEL_SUBNET
    FLANNEL_SUBNET=10.100.54.1/24
    

    然后通过以下方式检查接口:

    ifconfig flannel0
    ifconfig docker0
    

    正确的配置应将 flannel0 分配给子网中的“0”地址,例如 10.100.54.0,并将 docker0 分配给“1”地址,例如 10.100.54.1

  • 验证上面找到的 IP 是否在正确的 Flannel 子网中。如果不是,则 docker 守护程序未通过参数 –bip 正确配置。检查 docker 的 systemd 服务。

  • Flannel 是否正常运行?检查 运行 Flannel

  • Ping 并尝试在两个节点之间的路径上对每个网络接口进行 tcpdump,以查看消息能够传播多远。消息路径应如下所示:

    1. 源节点:docker0

    2. 源节点:flannel0

    3. 源节点:eth0

    4. 目标节点:eth0

    5. 目标节点:flannel0

    6. 目标节点:docker0

如果 ping 成功,则表示 flannel 覆盖网络正常工作。

Kubernetes 为 pod 创建的容器将与直接在 Docker 中创建的容器位于相同的 IP 子网中,因此它们将具有相同的连接性。但是,pod 仍然可能无法相互访问,因为它们通常通过一些 Kubernetes 服务而不是直接连接。这些服务由 kube-proxy 支持,并且规则插入到 iptables 中,因此它们的网络路径有一些额外的跳跃,并且这里可能会出现问题。

要检查 Kubernetes pod 级别的连接性,请登录到 master 节点并创建两个 pod 和一个为其中一个 pod 提供服务的服务。可以使用目录 /etc/kubernetes/examples/ 中提供的示例作为第一个 pod 和服务。这将启动一个 nginx 容器和一个 Kubernetes 服务来暴露端点。创建另一个清单来测试第二个 pod 的端点

cat > alpine.yaml << END
apiVersion: v1
kind: Pod
metadata:
  name: alpine
spec:
  containers:
  - name: alpine
    image: alpine
    args:
    - sleep
    - "1000000"
END

kubectl create -f /etc/kubernetes/examples/pod-nginx-with-label.yaml
kubectl create -f /etc/kubernetes/examples/service.yaml
kubectl create -f alpine.yaml

获取 nginx-service 的端点,该端点应将消息路由到 pod nginx

kubectl describe service nginx-service | grep -e IP: -e Port:
IP:                     10.254.21.158
Port:                   <unnamed>       8000/TCP

请注意用于以下检查的 IP 和端口。登录到运行 alpine pod 的节点。您可以通过在 master 节点上运行此命令来找到宿主机节点

kubectl get pods -o wide  | grep alpine | awk '{print $6}'
k8-gzvjwcooto-0-gsrxhmyjupbi-kube-minion-br73i6ans2b4

要获取节点的 IP,请查询 devstack 上的 Nova

nova list

在此宿主机节点上,连接到 alpine 容器

export DOCKER_ID=`sudo docker ps | grep k8s_alpine | awk '{print $1}'`
sudo docker exec -it $DOCKER_ID sh

alpine pod,您可以使用上面找到的 IP 和端口通过 nginx 服务访问 nginx pod

wget 10.254.21.158:8000

如果连接成功,您应该从 nginx 接收到 index.html 文件。

如果连接不成功,您将收到类似如下错误消息::xs

wget: can’t connect to remote host (10.100.54.9): No route to host

在这种情况下,请检查以下内容

  • kube-proxy 是否正在节点上运行?它在每个节点上作为容器运行。通过登录到 minion 节点并运行

    sudo docker ps | grep k8s_kube-proxy
    
  • 通过在 minion 节点上运行来检查 kube-proxy 的日志

    export PROXY=`sudo docker ps | grep "hyperkube proxy" | awk '{print $1}'`
    sudo docker logs $PROXY
    
  • 尝试其他 服务调试。要查看配置期间发生的情况

    kubectl get events
    

    获取有关所询问的服务的信息

    kubectl describe services <service_name>
    

etcd 服务

etcd 服务由许多其他组件用于键/值对管理,因此如果它无法启动,这些其他组件将无法正确运行。通过以下方式检查 etcd 是否正在 master 节点上运行

sudo service etcd status -l

如果它正在正确运行,您应该看到该服务已成功部署

Active: active (running) since ....

日志消息应显示服务正在发布

etcdserver: published {Name:10.0.0.5 ClientURLs:[http://10.0.0.5:2379]} to cluster 3451e4c04ec92893

在某些情况下,该服务可能显示为 active,但仍可能卡在发现模式中,并且未完全运行。日志消息可能显示如下内容

discovery: waiting for other nodes: error connecting to https://discovery.etcd.io, retrying in 8m32s

如果此条件持续存在,请检查 集群互联网访问

如果守护程序未运行,状态将显示服务失败,如下所示

Active: failed (Result: timeout)

在这种情况下,尝试通过以下方式重新启动 etcd

sudo service etcd start

如果 etcd 继续失败,请检查以下内容

  • 检查 etcd 的日志

    sudo journalctl -u etcd
    
  • etcd 需要发现,默认发现方法是 etcd.io 提供的公共发现服务;因此,失败的常见原因是此公共发现服务不可访问。通过在 master 节点上运行来检查

    . /etc/sysconfig/heat-params
    curl $ETCD_DISCOVERY_URL
    

    您应该收到如下内容

    {"action":"get",
     "node":{"key":"/_etcd/registry/00a6b00064174c92411b0f09ad5466c6",
             "dir":true,
             "nodes":[
               {"key":"/_etcd/registry/00a6b00064174c92411b0f09ad5466c6/7d8a68781a20c0a5",
                "value":"10.0.0.5=http://10.0.0.5:2380",
                "modifiedIndex":978239406,
                "createdIndex":978239406}],
             "modifiedIndex":978237118,
             "createdIndex":978237118}
    }
    

    master IP 列表由 Magnum 在集群部署期间提供,因此它应与 master 节点的当前 IP 匹配。如果公共发现服务不可访问,请检查 集群互联网访问

运行 Flannel

在部署 COE 时,Flannel 可作为某些 COE 类型的网络驱动程序使用。Magnum 当前支持 Flannel 用于 Kubernetes 集群。

Flannel 为集群中的容器提供了一个扁平的网络空间:将 IP 分配给此网络空间,并且它们将具有彼此的连接性。因此,如果 Flannel 失败,某些容器将无法访问集群中其他容器的服务。可以通过从一个容器 ping 或 curl 另一个容器来确认这一点。

Flannel 守护程序在集群的每个节点上作为 systemd 服务运行。要检查 Flannel,请在每个节点上运行

sudo service flanneld status

如果守护程序正在运行,您应该看到该服务已成功部署

Active: active (running) since ....

如果守护程序未运行,状态将显示服务失败,如下所示

Active: failed (Result: timeout) ....

或者

Active: inactive (dead) ....

Flannel 守护程序也可能正在运行但未正确运行。检查以下内容

  • 检查 Flannel 的日志

    sudo journalctl -u flanneld
    
  • 由于 Flannel 依赖于 etcd,因此失败的常见原因是 etcd 服务未在 master 节点上运行。检查 etcd 服务。如果 etcd 服务失败,一旦成功恢复,可以通过以下方式重新启动 Flannel 服务

    sudo service flanneld restart
    
  • Magnum 将 Flannel 的配置写入每个 master 节点上的本地文件。通过以下方式在 master 节点上检查此文件

    cat /etc/sysconfig/flannel-network.json
    

    内容应如下所示

    {
      "Network": "10.100.0.0/16",
      "Subnetlen": 24,
      "Backend": {
        "Type": "udp"
      }
    }
    

    其中参数的值必须与 ClusterTemplate 中的相应参数匹配。

    Magnum 还将此配置加载到 etcd 中,因此,通过在 master 节点上运行 etcdctl 来验证 etcd 中的配置

    . /etc/sysconfig/flanneld
    etcdctl get $FLANNEL_ETCD_KEY/config
    
  • 为每个节点分配网络空间的一部分。通过以下方式在每个节点上检查此段

    grep FLANNEL_SUBNET /run/flannel/subnet.env
    

    此节点上的容器应分配在此范围内的 IP。节点通过 etcd 协商其段,您可以使用 master 节点上的 etcdctl 查询与每个节点关联的网络段

    . /etc/sysconfig/flanneld
    for s in `etcdctl ls $FLANNEL_ETCD_KEY/subnets`
    do
    echo $s
    etcdctl get $s
    done
    
    /atomic.io/network/subnets/10.100.14.0-24
    {"PublicIP":"10.0.0.5"}
    /atomic.io/network/subnets/10.100.61.0-24
    {"PublicIP":"10.0.0.6"}
    /atomic.io/network/subnets/10.100.92.0-24
    {"PublicIP":"10.0.0.7"}
    

    或者,您可以按以下方式读取 ectd 中的完整记录

    curl http://<master_node_ip>:2379/v2/keys/coreos.com/network/subnets
    

    您应该收到一个 JSON 代码片段,其中描述了所有分配的段。

  • 此网络段通过参数 –bip 传递给 Docker。如果未正确配置,Docker 将不会将正确的 IP 分配给 Flannel 网络段中的容器。通过以下方式检查

    cat /run/flannel/docker
    ps -aux | grep docker
    
  • 检查 Flannel 的接口

    ifconfig flannel0
    

    IP 应该是此节点的 Flannel 子网中的第一个地址。

  • Flannel 有几种不同的后端实现,它们有特定的要求。udp 后端是最通用的,并且对网络没有要求。vxlan 后端需要内核中的 vxlan 支持,因此请确保使用的镜像提供 vxlan 支持。host-gw 后端要求所有主机都在同一个 L2 网络上。Magnum 创建的私有 Neutron 子网目前满足此要求;但是,如果使用其他网络拓扑,请确保在使用 host-gw 时满足此要求。

当前已知限制:镜像 fedora-21-atomic-5.qcow2 具有 Flannel 版本 0.5.0。此版本具有已知错误,这些错误会阻止后端 vxland 和 host-gw 正常工作。只有后端 udp 适用于此镜像。版本 0.5.3 及更高版本应正常工作。镜像 fedora-21-atomic-7.qcow2 具有 Flannel 版本 0.5.5。

Kubernetes 服务

待填充

(当 heat 工作但 k8s 不工作时如何内省 k8s)

监控、日志记录和调试部分提供了额外的 Kubernetes 故障排除部分

Barbican 问题

待填充

Docker CLI

待填充

请求卷大小

待填充

Heat 软件资源脚本

待填充

针对开发人员

本节旨在帮助 Magnum 开发人员在开发冒险过程中遇到的问题。

Gate 中的故障排除

模拟 gate 测试

  1. 启动 VM

  2. 像这样配置此 VM

    apt-get update \
    && apt-get upgrade \ # Kernel upgrade, as recommended by README, select to keep existing grub config
    && apt-get install git tmux vim \
    && git clone https://git.openstack.org/openstack-infra/system-config \
    && system-config/install_puppet.sh && system-config/install_modules.sh \
    && puppet apply \
    --modulepath=/root/system-config/modules:/etc/puppet/modules \
    -e "class { openstack_project::single_use_slave: install_users => false,
    ssh_key => \"$( cat .ssh/authorized_keys | awk '{print $2}' )\" }" \
    && echo "jenkins ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
    && cat ~/.ssh/authorized_keys >> /home/jenkins/.ssh/authorized_keys
    
  3. 比较 ~/.ssh/authorized_keys/home/jenkins/.ssh/authorized_keys。您的原始公共 SSH 密钥现在应该在 /home/jenkins/.ssh/authorized_keys 中。如果不是,请显式复制它(这可能会发生在使用 --key-name <name> 启动时,例如)。

  4. 假设一切都顺利,现在是时候 reboot 到最新的内核了

  5. 完成启动到新内核后,再次以 jenkins 用户身份登录以继续设置模拟。

  6. 现在是时候设置工作区了

    export REPO_URL=https://git.openstack.org
    export WORKSPACE=/home/jenkins/workspace/testing
    export ZUUL_URL=/home/jenkins/workspace-cache2
    export ZUUL_REF=HEAD
    export ZUUL_BRANCH=master
    export ZUUL_PROJECT=openstack/magnum
    mkdir -p $WORKSPACE
    git clone $REPO_URL/$ZUUL_PROJECT $ZUUL_URL/$ZUUL_PROJECT \
    && cd $ZUUL_URL/$ZUUL_PROJECT \
    && git checkout remotes/origin/$ZUUL_BRANCH
    
  7. 此时,您可能想测试特定的更改。如果是这样,您可以从 $ZUUL_URL/$ZUUL_PROJECT 目录中拉取更改

    cd $ZUUL_URL/$ZUUL_PROJECT \
    && git fetch https://review.openstack.org/openstack/magnum refs/changes/83/247083/12 && git checkout FETCH_HEAD
    
  8. 现在您可以拉取 devstack-gate 脚本,这些脚本将让您在自己的 VM 上运行 gate 作业

    cd $WORKSPACE \
    && git clone --depth 1 $REPO_URL/openstack-infra/devstack-gate
    
  9. 现在您可以使用以下脚本启动作业(devstack-gate 文档建议从可以在 project-config 存储库中找到的作业中复制,自然它应该是可执行的 (chmod u+x <filename>))

    #!/bin/bash -xe
    cat > clonemap.yaml << EOF
    clonemap:
      - name: openstack-infra/devstack-gate
        dest: devstack-gate
    EOF
    /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
        https://git.openstack.org \
        openstack-infra/devstack-gate
    export PYTHONUNBUFFERED=true
    export DEVSTACK_GATE_TIMEOUT=240 # bump this if you see timeout issues.  Default is 120
    export DEVSTACK_GATE_TEMPEST=0
    export DEVSTACK_GATE_NEUTRON=1
    # Enable tempest for tempest plugin
    export ENABLED_SERVICES=tempest
    export BRANCH_OVERRIDE="default"
    if [ "$BRANCH_OVERRIDE" != "default" ] ; then
        export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
    fi
    export PROJECTS="openstack/magnum $PROJECTS"
    export PROJECTS="openstack/python-magnumclient $PROJECTS"
    export PROJECTS="openstack/barbican $PROJECTS"
    export DEVSTACK_LOCAL_CONFIG="enable_plugin magnum https://git.openstack.org/openstack/magnum"
    export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin ceilometer https://git.openstack.org/openstack/ceilometer"
    # Keep localrc to be able to set some vars in post_test_hook
    export KEEP_LOCALRC=1
    function gate_hook {
         cd /opt/stack/new/magnum/
        ./magnum/tests/contrib/gate_hook.sh api # change this to k8s to run kubernetes functional tests
    }
    export -f gate_hook
    function post_test_hook {
        . $BASE/new/devstack/accrc/admin/admin
        cd /opt/stack/new/magnum/
        ./magnum/tests/contrib/post_test_hook.sh api # change this to k8s to run kubernetes functional tests
    }
    export -f post_test_hook
    cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
    ./safe-devstack-vm-gate-wrap.sh