aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbuild/bin/entrypoint111
-rw-r--r--deploy/ovn-daemonset.yaml239
-rw-r--r--utilities/docker/debian/Dockerfile40
-rwxr-xr-xutilities/docker/debian/ovn4nfv-k8s.sh158
4 files changed, 527 insertions, 21 deletions
diff --git a/build/bin/entrypoint b/build/bin/entrypoint
index 77084a3..c9646a0 100755
--- a/build/bin/entrypoint
+++ b/build/bin/entrypoint
@@ -1,32 +1,101 @@
-#!/bin/sh -e
+#!/bin/bash
+set -e
+CNI_VERSION=${CNI_VERSION:-"v0.8.5"}
+IMAGE_ARC=${IMAGE_ARC:-"amd64"}
+
+create_kubeconfig() {
+ # Make a ovn4nfv.d directory (for our kubeconfig)
+ # Inspired from t.ly/Xgbbe
+ mkdir -p $CNI_CONF_DIR/ovn4nfv-k8s.d
+ OVN4NFV_KUBECONFIG=$CNI_CONF_DIR/ovn4nfv-k8s.d/ovn4nfv-k8s.kubeconfig
+ SERVICE_ACCOUNT_PATH=/var/run/secrets/kubernetes.io/serviceaccount
+ KUBE_CA_FILE=${KUBE_CA_FILE:-$SERVICE_ACCOUNT_PATH/ca.crt}
+ SERVICEACCOUNT_TOKEN=$(cat $SERVICE_ACCOUNT_PATH/token)
+ SKIP_TLS_VERIFY=${SKIP_TLS_VERIFY:-false}
+
+ # Check if we're running as a k8s pod.
+ if [ -f "$SERVICE_ACCOUNT_PATH/token" ]; then
+ # We're running as a k8d pod - expect some variables.
+ if [ -z ${KUBERNETES_SERVICE_HOST} ]; then
+ error "KUBERNETES_SERVICE_HOST not set"; exit 1;
+ fi
+ if [ -z ${KUBERNETES_SERVICE_PORT} ]; then
+ error "KUBERNETES_SERVICE_PORT not set"; exit 1;
+ fi
+
+ if [ "$SKIP_TLS_VERIFY" == "true" ]; then
+ TLS_CFG="insecure-skip-tls-verify: true"
+ elif [ -f "$KUBE_CA_FILE" ]; then
+ TLS_CFG="certificate-authority-data: $(cat $KUBE_CA_FILE | base64 | tr -d '\n')"
+ fi
+
+ # Write a kubeconfig file for the CNI plugin. Do this
+ # to skip TLS verification for now. We should eventually support
+ # writing more complete kubeconfig files. This is only used
+ # if the provided CNI network config references it.
+ touch $OVN4NFV_KUBECONFIG
+ chmod ${KUBECONFIG_MODE:-600} $OVN4NFV_KUBECONFIG
+ cat > $OVN4NFV_KUBECONFIG <<EOF
+# Kubeconfig file for OVN4NFV-K8S CNI plugin.
+apiVersion: v1
+kind: Config
+clusters:
+- name: local
+ cluster:
+ server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://[${KUBERNETES_SERVICE_HOST}]:${KUBERNETES_SERVICE_PORT}
+ $TLS_CFG
+users:
+- name: ovn4nfv
+ user:
+ token: "${SERVICEACCOUNT_TOKEN}"
+contexts:
+- name: ovn4nfv-context
+ context:
+ cluster: local
+ user: ovn4nfv
+current-context: ovn4nfv-context
+EOF
+ else
+ warn "Doesn't look like we're running in a kubernetes environment (no serviceaccount token)"
+ fi
+}
+
+install_cni_plugins() {
+ curl --insecure --compressed -O -L https://github.com/containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-linux-$IMAGE_ARC-$CNI_VERSION.tgz
+ tar -zxvf cni-plugins-linux-$IMAGE_ARC-$CNI_VERSION.tgz -C $CNI_BIN_DIR
+ rm -rf cni-plugins-linux-$IMAGE_ARC-$CNI_VERSION.tgz
+}
cmd=${1:-""}
case ${cmd} in
- "cni")
- CNI_BIN_DIR="/host/opt/cni/bin"
- OVN4NFV_CONF_DIR="/host/etc/openvswitch"
- OVN4NFV_BIN_FILE="/usr/local/bin/ovn4nfvk8s-cni"
- OVN4NFV_CONF_FILE="/tmp/ovn4nfv-conf/ovn4nfv_k8s.conf"
- cp -f $OVN4NFV_BIN_FILE $CNI_BIN_DIR
- cp -f $OVN4NFV_CONF_FILE $OVN4NFV_CONF_DIR
- # Sleep forever.
- sleep infinity
- ;;
+ "cni")
+ CNI_BIN_DIR="/host/opt/cni/bin"
+ OVN4NFV_CONF_DIR="/host/etc/openvswitch"
+ OVN4NFV_BIN_FILE="/usr/local/bin/ovn4nfvk8s-cni"
+ OVN4NFV_CONF_FILE="/tmp/ovn4nfv-conf/ovn4nfv_k8s.conf"
+ OVN4NFV_NET_CONF_FILE="/tmp/ovn4nfv-cni/00-network.conf"
+ CNI_CONF_DIR="/host/etc/cni/net.d"
+
+ cp -f $OVN4NFV_BIN_FILE $CNI_BIN_DIR
+ cp -f $OVN4NFV_CONF_FILE $OVN4NFV_CONF_DIR
+ cp -f $OVN4NFV_NET_CONF_FILE $CNI_CONF_DIR
+ create_kubeconfig
+ install_cni_plugins
+ # Sleep forever.
+ sleep infinity
+ ;;
"operator")
- shift
- exec ${OPERATOR} $@
- ;;
+ shift
+ exec ${OPERATOR} $@
+ ;;
"agent")
- shift
- exec ${AGENT} $@
- ;;
-
-
+ shift
+ exec ${AGENT} $@
+ ;;
*)
- echo "invalid command ${cmd}"
-
+ echo "invalid command ${cmd}"
esac
diff --git a/deploy/ovn-daemonset.yaml b/deploy/ovn-daemonset.yaml
new file mode 100644
index 0000000..bb64d84
--- /dev/null
+++ b/deploy/ovn-daemonset.yaml
@@ -0,0 +1,239 @@
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: ovn-nb-tcp
+ namespace: kube-system
+spec:
+ ports:
+ - name: ovn-nb-tcp
+ protocol: TCP
+ port: 6641
+ targetPort: 6641
+ type: ClusterIP
+ selector:
+ app: ovn-control-plane
+ sessionAffinity: None
+
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: ovn-sb-tcp
+ namespace: kube-system
+spec:
+ ports:
+ - name: ovn-sb-tcp
+ protocol: TCP
+ port: 6642
+ targetPort: 6642
+ type: ClusterIP
+ selector:
+ app: ovn-control-plane
+ sessionAffinity: None
+
+---
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: ovn-control-plane
+ namespace: kube-system
+ annotations:
+ kubernetes.io/description: |
+ OVN control plane deployment using tcp: ovn-northd-tcp, ovn-nb-tcp and ovn-sb-tcp.
+spec:
+ replicas: 1
+ strategy:
+ rollingUpdate:
+ maxSurge: 0%
+ maxUnavailable: 100%
+ type: RollingUpdate
+ selector:
+ matchLabels:
+ app: ovn-control-plane
+ template:
+ metadata:
+ labels:
+ app: ovn-control-plane
+ spec:
+ tolerations:
+ - operator: Exists
+ effect: NoSchedule
+ affinity:
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - labelSelector:
+ matchLabels:
+ app: ovn-control-plane
+ topologyKey: kubernetes.io/hostname
+ priorityClassName: system-cluster-critical
+ hostNetwork: true
+ containers:
+ - name: ovn-control-plane
+ image: integratedcloudnative/ovn-images:master
+ imagePullPolicy: "IfNotPresent"
+ command: ["ovn4nfv-k8s", "start_ovn_control_plane"]
+ securityContext:
+ capabilities:
+ add: ["SYS_NICE"]
+ env:
+ - name: POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ resources:
+ requests:
+ cpu: 500m
+ memory: 300Mi
+ volumeMounts:
+ - mountPath: /var/run/openvswitch
+ name: host-run-ovs
+ - mountPath: /var/run/ovn
+ name: host-run-ovn
+ - mountPath: /sys
+ name: host-sys
+ readOnly: true
+ - mountPath: /etc/openvswitch
+ name: host-config-openvswitch
+ - mountPath: /var/log/openvswitch
+ name: host-log-ovs
+ - mountPath: /var/log/ovn
+ name: host-log-ovn
+ readinessProbe:
+ exec:
+ command: ["ovn4nfv-k8s", "check_ovn_control_plane"]
+ periodSeconds: 3
+ livenessProbe:
+ exec:
+ command: ["ovn4nfv-k8s", "check_ovn_control_plane"]
+ initialDelaySeconds: 30
+ periodSeconds: 7
+ failureThreshold: 5
+ nodeSelector:
+ beta.kubernetes.io/os: "linux"
+ ovn4nfv-k8s-plugin: ovn-control-plane
+ volumes:
+ - name: host-run-ovs
+ hostPath:
+ path: /run/openvswitch
+ - name: host-run-ovn
+ hostPath:
+ path: /run/ovn
+ - name: host-sys
+ hostPath:
+ path: /sys
+ - name: host-config-openvswitch
+ hostPath:
+ path: /etc/origin/openvswitch
+ - name: host-log-ovs
+ hostPath:
+ path: /var/log/openvswitch
+ - name: host-log-ovn
+ hostPath:
+ path: /var/log/ovn
+
+---
+kind: DaemonSet
+apiVersion: apps/v1
+metadata:
+ name: ovn-controller
+ namespace: kube-system
+ annotations:
+ kubernetes.io/description: |
+ OVN controller: Start ovsdb-server & ovs-vswitchd components, and ovn controller
+spec:
+ selector:
+ matchLabels:
+ app: ovn-controller
+ updateStrategy:
+ type: OnDelete
+ template:
+ metadata:
+ labels:
+ app: ovn-controller
+ spec:
+ tolerations:
+ - operator: Exists
+ effect: NoSchedule
+ priorityClassName: system-cluster-critical
+ hostNetwork: true
+ hostPID: true
+ containers:
+ - name: ovn-controller
+ image: integratedcloudnative/ovn-images:master
+ imagePullPolicy: IfNotPresent
+ command: ["ovn4nfv-k8s", "start_ovn_controller"]
+ securityContext:
+ runAsUser: 0
+ privileged: true
+ env:
+ - name: POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ volumeMounts:
+ - mountPath: /lib/modules
+ name: host-modules
+ readOnly: true
+ - mountPath: /var/run/openvswitch
+ name: host-run-ovs
+ - mountPath: /var/run/ovn
+ name: host-run-ovn
+ - mountPath: /sys
+ name: host-sys
+ readOnly: true
+ - mountPath: /etc/openvswitch
+ name: host-config-openvswitch
+ - mountPath: /var/log/openvswitch
+ name: host-log-ovs
+ - mountPath: /var/log/ovn
+ name: host-log-ovn
+ readinessProbe:
+ exec:
+ command: ["ovn4nfv-k8s", "check_ovn_controller"]
+ periodSeconds: 5
+ livenessProbe:
+ exec:
+ command: ["ovn4nfv-k8s", "check_ovn_controller"]
+ initialDelaySeconds: 10
+ periodSeconds: 5
+ failureThreshold: 5
+ resources:
+ requests:
+ cpu: 200m
+ memory: 300Mi
+ limits:
+ cpu: 1000m
+ memory: 800Mi
+ nodeSelector:
+ beta.kubernetes.io/os: "linux"
+ volumes:
+ - name: host-modules
+ hostPath:
+ path: /lib/modules
+ - name: host-run-ovs
+ hostPath:
+ path: /run/openvswitch
+ - name: host-run-ovn
+ hostPath:
+ path: /run/ovn
+ - name: host-sys
+ hostPath:
+ path: /sys
+ - name: host-config-openvswitch
+ hostPath:
+ path: /etc/origin/openvswitch
+ - name: host-log-ovs
+ hostPath:
+ path: /var/log/openvswitch
+ - name: host-log-ovn
+ hostPath:
+ path: /var/log/ovn
diff --git a/utilities/docker/debian/Dockerfile b/utilities/docker/debian/Dockerfile
new file mode 100644
index 0000000..674ee7e
--- /dev/null
+++ b/utilities/docker/debian/Dockerfile
@@ -0,0 +1,40 @@
+FROM ubuntu:18.04 as base
+
+USER root
+
+RUN apt-get update && apt-get install -y iproute2 curl software-properties-common setpriv dpkg-dev netcat
+
+RUN mkdir -p /opt/ovn4nfv-k8s-plugin/dist/ubuntu/deb
+RUN bash -xc "\
+pushd /opt/ovn4nfv-k8s-plugin/dist/ubuntu/deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/libopenvswitch-dev_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/libopenvswitch_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-common_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-datapath-dkms_2.12.0-1_all.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-datapath-source_2.12.0-1_all.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-dbg_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-ipsec_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-pki_2.12.0-1_all.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-switch_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-testcontroller_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-test_2.12.0-1_all.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/openvswitch-vtep_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/ovn-central_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/ovn-common_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/ovn-controller-vtep_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/ovn-docker_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/ovn-host_2.12.0-1_amd64.deb; \
+curl --insecure --compressed -O -L https://github.com/akraino-icn/ovs/releases/download/v2.12.0/python-openvswitch_2.12.0-1_all.deb; \
+dpkg-scanpackages . | gzip -c9 > Packages.gz; \
+popd; \
+"
+RUN ls -lt /opt/ovn4nfv-k8s-plugin/dist/ubuntu/deb
+RUN echo "deb [trusted=yes] file:///opt/ovn4nfv-k8s-plugin/dist/ubuntu/deb ./" | tee -a /etc/apt/sources.list > /dev/null
+RUN apt-get update
+RUN apt-get install -y openvswitch-switch=2.12.0-1 openvswitch-common=2.12.0-1 ovn-central=2.12.0-1 ovn-common=2.12.0-1 ovn-host=2.12.0-1
+RUN mkdir -p /var/run/openvswitch && \
+ mkdir -p /var/run/ovn
+
+COPY ovn4nfv-k8s.sh /usr/local/bin/ovn4nfv-k8s
+
+ENTRYPOINT ["ovn4nfv-k8s"]
diff --git a/utilities/docker/debian/ovn4nfv-k8s.sh b/utilities/docker/debian/ovn4nfv-k8s.sh
new file mode 100755
index 0000000..ee60e2b
--- /dev/null
+++ b/utilities/docker/debian/ovn4nfv-k8s.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+OVS_RUNDIR=/var/run/openvswitch
+OVS_LOGDIR=/var/log/openvswitch
+
+DB_NB_ADDR=${DB_NB_ADDR:-::}
+DB_NB_PORT=${DB_NB_PORT:-6641}
+DB_SB_ADDR=${DB_SB_ADDR:-::}
+DB_SB_PORT=${DB_SB_PORT:-6642}
+cmd=${1:-""}
+
+if [[ -f /usr/bin/ovn-appctl ]] ; then
+ # ovn-appctl is present. Use new ovn run dir path.
+ OVN_RUNDIR=/var/run/ovn
+ OVNCTL_PATH=/usr/share/ovn/scripts/ovn-ctl
+ OVN_LOGDIR=/var/log/ovn
+ OVN_ETCDIR=/etc/ovn
+else
+ # ovn-appctl is not present. Use openvswitch run dir path.
+ OVN_RUNDIR=/var/run/openvswitch
+ OVNCTL_PATH=/usr/share/openvswitch/scripts/ovn-ctl
+ OVN_LOGDIR=/var/log/openvswitch
+ OVN_ETCDIR=/etc/openvswitch
+fi
+
+check_ovn_control_plane() {
+ /usr/share/ovn/scripts/ovn-ctl status_northd
+ /usr/share/ovn/scripts/ovn-ctl status_ovnnb
+ /usr/share/ovn/scripts/ovn-ctl status_ovnsb
+}
+
+check_ovn_controller() {
+ /usr/share/ovn/scripts/ovn-ctl status_controller
+}
+
+# wait for ovn-sb ready
+wait_ovn_sb() {
+ if [[ -z "${OVN_SB_TCP_SERVICE_HOST}" ]]; then
+ echo "env OVN_SB_SERVICE_HOST not exists"
+ exit 1
+ fi
+ if [[ -z "${OVN_SB_TCP_SERVICE_PORT}" ]]; then
+ echo "env OVN_SB_SERVICE_PORT not exists"
+ exit 1
+ fi
+ while ! nc -z "${OVN_SB_TCP_SERVICE_HOST}" "${OVN_SB_TCP_SERVICE_PORT}" </dev/null;
+ do
+ echo "sleep 10 seconds, waiting for ovn-sb ${OVN_SB_TCP_SERVICE_HOST}:${OVN_SB_TCP_SERVICE_PORT} ready "
+ sleep 10;
+ done
+}
+
+start_ovs_vswitch() {
+ wait_ovn_sb
+ function quit {
+ /usr/share/openvswitch/scripts/ovs-ctl stop
+ /usr/share/openvswitch/scripts/ovn-ctl stop_controller
+ exit 0
+ }
+ trap quit EXIT
+ /usr/share/openvswitch/scripts/ovs-ctl restart --no-ovs-vswitchd --system-id=random
+ # Restrict the number of pthreads ovs-vswitchd creates to reduce the
+ # amount of RSS it uses on hosts with many cores
+ # https://bugzilla.redhat.com/show_bug.cgi?id=1571379
+ # https://bugzilla.redhat.com/show_bug.cgi?id=1572797
+ if [[ `nproc` -gt 12 ]]; then
+ ovs-vsctl --no-wait set Open_vSwitch . other_config:n-revalidator-threads=4
+ ovs-vsctl --no-wait set Open_vSwitch . other_config:n-handler-threads=10
+ fi
+
+ # Start ovsdb
+ /usr/share/openvswitch/scripts/ovs-ctl restart --no-ovsdb-server --system-id=random
+ /usr/share/openvswitch/scripts/ovs-ctl --protocol=udp --dport=6081 enable-protocol
+
+}
+
+#cleanup_ovs_server() {
+#}
+
+#cleanup_ovs_controller() {
+#}
+
+function get_default_inteface_ipaddress {
+ local _ip=$1
+ local _default_interface=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
+ local _ipv4address=$(ip addr show dev $_default_interface | awk '$1 == "inet" { sub("/.*", "", $2); print $2 }')
+ eval $_ip="'$_ipv4address'"
+}
+
+start_ovn_control_plane() {
+ function quit {
+ /usr/share/openvswitch/scripts/ovn-ctl stop_northd
+ exit 0
+ }
+ trap quit EXIT
+ /usr/share/openvswitch/scripts/ovn-ctl restart_northd
+ ovn-nbctl set-connection ptcp:"${DB_NB_PORT}":["${DB_NB_ADDR}"]
+ ovn-nbctl set Connection . inactivity_probe=0
+ ovn-sbctl set-connection ptcp:"${DB_SB_PORT}":["${DB_SB_ADDR}"]
+ ovn-sbctl set Connection . inactivity_probe=0
+ tail -f /var/log/openvswitch/ovn-northd.log
+}
+
+start_ovn_controller() {
+ function quit {
+ /usr/share/openvswitch/scripts/ovn-ctl stop_controller
+ exit 0
+ }
+ trap quit EXIT
+ wait_ovn_sb
+ get_default_inteface_ipaddress node_ipv4_address
+ /usr/share/openvswitch/scripts/ovn-ctl restart_controller
+ # Set remote ovn-sb for ovn-controller to connect to
+ ovs-vsctl set open . external-ids:ovn-remote=tcp:"${OVN_SB_TCP_SERVICE_HOST}":"${OVN_SB_TCP_SERVICE_PORT}"
+ ovs-vsctl set open . external-ids:ovn-remote-probe-interval=10000
+ ovs-vsctl set open . external-ids:ovn-openflow-probe-interval=180
+ ovs-vsctl set open . external-ids:ovn-encap-type=geneve
+ ovs-vsctl set open . external-ids:ovn-encap-ip=$node_ipv4_address
+ tail -f /var/log/openvswitch/ovn-controller.log
+}
+
+set_nbclt() {
+ wait_ovn_sb
+ ovn-nbctl --db=tcp:["${OVN_NB_TCP_SERVICE_HOST}"]:"${OVN_NB_TCP_SERVICE_PORT}" --pidfile --detach --overwrite-pidfile
+}
+
+check_ovs_vswitch() {
+ /usr/share/openvswitch/scripts/ovs-ctl status
+}
+
+case ${cmd} in
+ "start_ovn_control_plane")
+ start_ovn_control_plane
+ ;;
+ "check_ovn_control_plane")
+ check_ovn_control_plane
+ ;;
+ "start_ovn_controller")
+ start_ovs_vswitch
+ set_nbclt
+ start_ovn_controller
+ ;;
+ "check_ovs_vswitch")
+ check_ovs_vswitch
+ ;;
+ "check_ovn_controller")
+ check_ovs_vswitch
+ check_ovn_controller
+ ;;
+ "cleanup_ovs_controller")
+ cleanup_ovs_controller
+ ;;
+ *)
+ echo "invalid command ${cmd}"
+ echo "valid commands: start-ovn-control-plane check_ovn_control_plane start-ovs-vswitch"
+ exit 0
+esac
+
+exit 0