Browse Source

Move deployment scripts from AIAB to Treasuremap

* Last AIAB commit ID: d7d345f
* Also moving from Quagga to FRRouting for bgp router script.
* Synced the recent changes in aiab and removed the frrouting change
which is still open in aiab

Change-Id: If5e4e030dacaa7fcf525f9767f50c82b07516e27
changes/96/686496/15
Alexander Noskov 4 months ago
parent
commit
7853db4f36
74 changed files with 4456 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +221
    -0
      tools/deployment/seaworthy-virt/README.rst
  3. +1
    -0
      tools/deployment/seaworthy-virt/airship_gate/.gitignore
  4. +50
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/cluster-objects.sh
  5. +81
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/debug-report-lite.sh
  6. +24
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/drydock.sh
  7. +72
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/namespace-objects.sh
  8. +24
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/rsync.sh
  9. +24
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/scp.sh
  10. +24
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/shipyard.sh
  11. +24
    -0
      tools/deployment/seaworthy-virt/airship_gate/bin/ssh.sh
  12. +168
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/airship.sh
  13. +25
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/all.sh
  14. +37
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/bgp.sh
  15. +173
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/bootaction-runner.sh
  16. +514
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/config.sh
  17. +6
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/const.sh
  18. +27
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/docker.sh
  19. +54
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/ingress.sh
  20. +47
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/kube.sh
  21. +84
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/log.sh
  22. +37
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/nginx.sh
  23. +17
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/promenade.sh
  24. +76
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/registry.sh
  25. +78
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/ssh.sh
  26. +664
    -0
      tools/deployment/seaworthy-virt/airship_gate/lib/virsh.sh
  27. +130
    -0
      tools/deployment/seaworthy-virt/airship_gate/manifest-schema.json
  28. +61
    -0
      tools/deployment/seaworthy-virt/airship_gate/manifests/deploy_site.json
  29. +236
    -0
      tools/deployment/seaworthy-virt/airship_gate/manifests/multinode_deploy.json
  30. +105
    -0
      tools/deployment/seaworthy-virt/airship_gate/manifests/multinode_genesis.json
  31. +22
    -0
      tools/deployment/seaworthy-virt/airship_gate/manifests/one.json
  32. +67
    -0
      tools/deployment/seaworthy-virt/airship_gate/manifests/update_site.json
  33. +43
    -0
      tools/deployment/seaworthy-virt/airship_gate/on_error/collect_genesis_info.sh
  34. +23
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/bgp-router.sh
  35. +57
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/build-scripts.sh
  36. +22
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/create-vms.sh
  37. +33
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/debug-report-lite.sh
  38. +36
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/gate-setup.sh
  39. +29
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/generate-certificates.sh
  40. +26
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/genesis-setup.sh
  41. +30
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/genesis.sh
  42. +24
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/ingress-dns.sh
  43. +97
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/pegleg-collect.sh
  44. +84
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/pegleg-render.sh
  45. +21
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/registry-load.sh
  46. +33
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/report-disk-io.sh
  47. +22
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/shipyard-deploy-site.sh
  48. +83
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/shipyard-load-design.sh
  49. +22
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/shipyard-test-site.sh
  50. +22
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/shipyard-update-site.sh
  51. +20
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/stop-non-genesis-vms.sh
  52. +30
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/validate-genesis.sh
  53. +121
    -0
      tools/deployment/seaworthy-virt/airship_gate/stages/validate-kube.sh
  54. +1
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/authorized_keys.sub
  55. +20
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/bgpd_conf.sub
  56. +7
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/daemons.sub
  57. +19
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/debian_conf.sub
  58. +4
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/disk-data.sub
  59. +9
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/ingress_corefile.sub
  60. +1
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/ingress_entry.sub
  61. +4
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/ingress_header.sub
  62. +4
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/l2network-definition.sub
  63. +7
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/l3network-definition.sub
  64. +3
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/meta-data.sub
  65. +1
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/mount-data.sub
  66. +5
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/ssh-config-global.sub
  67. +2
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/ssh-config-node.sub
  68. +20
    -0
      tools/deployment/seaworthy-virt/airship_gate/templates/user-data.sub
  69. +97
    -0
      tools/deployment/seaworthy-virt/gate.sh
  70. +13
    -0
      tools/deployment/seaworthy-virt/registry/IMAGES
  71. +26
    -0
      tools/deployment/seaworthy-virt/registry/start.sh
  72. +18
    -0
      tools/deployment/seaworthy-virt/registry/stop.sh
  73. +29
    -0
      tools/deployment/seaworthy-virt/registry/update_cache.sh
  74. +114
    -0
      tools/deployment/seaworthy-virt/setup_gate.sh

+ 1
- 0
.gitignore View File

@@ -3,3 +3,4 @@ peggles/
# Unit test / coverage reports
.tox/
config-ssh

+ 221
- 0
tools/deployment/seaworthy-virt/README.rst View File

@@ -0,0 +1,221 @@
..
Copyright 2018 AT&T Intellectual Property.
All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.

.. _gates:

Gates
=====
Treasuremap contains scripts to aid developers and automation of Airship.
These tools are found in ./treasuremap/tools/deployment

Setup and Use
-------------

1. First time, and only needed once per node, ./setup_gate.sh will prepare the
node for use by setting up the necessary users, virsh and some dependencies.
2. gate.sh is the starting point to run each of the named gates, found in
./airship_gate/manifests, e.g.::

$ ./gate.sh multinode_deploy

where the argument for the gate.sh script is the filename of the json file
in ./airship_gate/manifests without the json extension.
if you run the script without any arguments::

$ ./gate.sh

then it will by default stand up a four-node Airship cluster.

If you'd like to bring your own manifest to drive the framework, you can
set and export the GATE_MANIFEST env var prior to running gate.sh

Each of the defined manifests used for the gate defines a virtual machine
configuration, and the steps to run as part of that gate. Additional
information found in each file is a configuration that targets a particular
set of Airship site configurations, which in some of the provided manifests are
found in the deployment_files/site directory.

Other Utilities
---------------
Several useful utilities are found in ./airship_gate/bin to facilitate
interactions with the VMs created. These commands are effectively wrapped
scripts providing the functionality of the utility they wrap, but also
incorporating the necessary identifying information needed for a particular
run of a gate. E.g.::

$ ./airship_gate/bin/ssh.sh n0

Writing Manifests
-----------------

Custom manifests can be used to drive this framework with testing outside
the default virtual site deployment scenario. Here is some information on
how to create a manifest to define custom network or VM configuration or
run a custom stage pipeline. Manifest files are written in JSON and the
documentation below will use dotted JSON paths when describing structure.
Unless the root is otherwise defined, assume it is from the document root.

Network Configuration
#####################

The ``.networking`` key defines the network topology of the site. Each
subkey is the name of a network. Under each network name is a semi-recursive stanza
defining the layer 2 and layer 3 attributes of the network:

.. code-block: json

{
"roles": ['string'],
"layer2": {
"mtu": integer,
"address": 'mac_address'
},
"layer3": {
"cidr": "CIDR",
"address": "ip_address",
"gateway": "ip_address",
"routing": {
"mode": "nat"
}
}
}

or

.. code-block: json

{
"layer2": {
"mtu": integer,
"vlans": {
"integer": {
"layer2":...,
"layer3":...
},
"integer": {
"layer2":...,
"layer3":...
}
}
}
}


* roles - These strings are used to select the correct network for internal gate
functions - supported: "ssh", "dns", "bgp"
* layer2 - Define Layer 2 attributes
* layer3 - Valid if the ``layer2`` attribute is NOT defining VLANs, then define
Layer 3 attributes.

Disk Layouts
############

The ``.disk_layouts`` key defines the various disk layouts that can be assigned
to VMs being built. Each named layout key then defines one or more block devices
that will be created as file-backed volumes.

.. code-block: json

{
"simple": {
"vda": {
"size": 30,
"io_profile": "fast",
"bootstrap": true
}
},
"multi": {
"vda": {
"size": 15,
"io_profile": "fast",
"bootstrap": true
},
"vdb": {
"size": 15,
"io_profile": "fast",
"format": {"type": "ext4", "mountpoint": "/var"}
}
}
}


* size - Size of the volume in gigabytes
* io_profile - One of the below I/O configurations
* fast - In the case of a VM disruption, synchronous I/O may be lost. Better throughput.
* safe - Synchronous I/O fully written to disk, slower throughput.
* bootstrap - For VMs that are bootstrapped by the framework, not Airship, use this disk
* format - For VMs that are bootstrapped by the framework, describe how the disk should be
formatted and mounted when desired.
* type - Filesystem type (e.g. 'xfs' or 'ext4')
* mountpoint - Path to mountpoint

VM Configuration
################

Under the ``.vm`` key is a mapping of all the VMs that will be created via virt-install.
This can be a mix of VMs that are bootstrapped via virsh/cloud-init and those deployed
via Airship. Each key is the name of a VM and value is a JSON object:

.. code-block: json

{
"memory": integer,
"vcpus": integer,
"disk_layout": "simple",
"networking": {
"ens3": {
"mac": "52:54:00:00:be:31",
"pci": {
"slot": 3,
"port": 0
},
"attachment": {
"network": "pxe"
}
},
"addresses": {
"pxe": {
"ip": "172.24.1.9"
}
}
},
"bootstrap": true,
"userdata": "packages: [docker.io]"
}

* memory - VM RAM in megabytes
* vcpus - Number of VM CPUs
* disk_layout - A disk profile for the VM matching one defined under ``.disk_layouts``
* bootstrap - True/False for whether the framework should bootstrap the VM's OS
* userdata - Cloud-init userdata to feed the VM when bootstrapped for further customization
* networking - Network attachment and addressing configuration. Every key but ``addresses``
is assumed to be a desired NIC on the VM. For each NIC stanza, the following fields are respected:

* mac - A MAC address for the NIC
* pci - A JSON object specifying ``slot`` and ``port`` specifying the PCI address for the NIC
* attachment - What network from ``.networking`` is attached to this NIC

The ``addresses`` key specifies the IP address for each layer 3 network that the VM is attached to.

Stage Pipeline
##############

TODO

External Access
###############

TODO

+ 1
- 0
tools/deployment/seaworthy-virt/airship_gate/.gitignore View File

@@ -0,0 +1 @@
config-ssh

+ 50
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/cluster-objects.sh View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Copyright 2017 The Openstack-Helm Authors.
# Copyright 2019 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

export CLUSTER_TYPE="${CLUSTER_TYPE:="node,clusterrole,clusterrolebinding,storageclass,namespace"}"
export PARALLELISM_FACTOR="${PARALLELISM_FACTOR:=2}"

function list_objects () {
printf "%s" ${CLUSTER_TYPE} | xargs -d ',' -I {} -P1 -n1 bash -c 'echo "$@"' _ {}
}

export -f list_objects

function name_objects () {
export OBJECT=$1
kubectl get "${OBJECT}" -o name | xargs -L1 -I {} -P1 -n1 bash -c "echo ${OBJECT} ${1#*/}" _ {}
}

export -f name_objects

function get_objects () {
input=($1)
export OBJECT=${input[0]}
export NAME=${input[1]#*/}
echo "${OBJECT}/${NAME}"
export BASE_DIR="${BASE_DIR:="/tmp"}"
DIR="${BASE_DIR}/objects/cluster/${OBJECT}"
mkdir -p "${DIR}"
kubectl get "${OBJECT}" "${NAME}" -o yaml > "${DIR}/${NAME}.yaml"
kubectl describe "${OBJECT}" "${NAME}" > "${DIR}/${NAME}.txt"
}

export -f get_objects
list_objects | \
xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'name_objects "$@"' _ {} | \
xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'get_objects "$@"' _ {}

+ 81
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/debug-report-lite.sh View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Copyright 2019 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


set -ux

SUBDIR_NAME=debug-$(hostname)

# NOTE(mark-burnett): This should add calicoctl to the path.
export PATH=${PATH}:/opt/cni/bin

TEMP_DIR=$(mktemp -d)
export TEMP_DIR
export BASE_DIR="${TEMP_DIR}/${SUBDIR_NAME}"
export HELM_DIR="${BASE_DIR}/helm"
export CALICO_DIR="${BASE_DIR}/calico"

mkdir -p "${BASE_DIR}"

export OBJECT_TYPE="${OBJECT_TYPE:="pods"}"
export CLUSTER_TYPE="${CLUSTER_TYPE:="namespace"}"
export PARALLELISM_FACTOR="${PARALLELISM_FACTOR:=2}"

function get_releases () {
helm list --all --short
}

function get_release () {
input=($1)
RELEASE=${input[0]}
helm status "${RELEASE}" > "${HELM_DIR}/${RELEASE}.txt"

}
export -f get_release

if which helm; then
mkdir -p "${HELM_DIR}"
helm list --all > "${HELM_DIR}/list"
get_releases | \
xargs -r -n 1 -P "${PARALLELISM_FACTOR}" -I {} bash -c 'get_release "$@"' _ {}
fi

kubectl get --all-namespaces -o wide pods > "${BASE_DIR}/pods.txt"
kubectl get pods --all-namespaces -o yaml > "${BASE_DIR}/pods_long.yaml"
kubectl describe pods --all-namespaces > "${BASE_DIR}/pods_describe.txt"

./tools/deployment/seaworthy-virt/airship_gate/bin/namespace-objects.sh
./tools/deployment/seaworthy-virt/airship_gate/bin/cluster-objects.sh

iptables-save > "${BASE_DIR}/iptables"

cat /var/log/syslog > "${BASE_DIR}/syslog"
cat /var/log/armada/bootstrap-armada.log > "${BASE_DIR}/bootstrap-armada.log"

ip addr show > "${BASE_DIR}/ifconfig"
ip route show > "${BASE_DIR}/ip-route"
cp -p /etc/resolv.conf "${BASE_DIR}/"

env | sort --ignore-case > "${BASE_DIR}/environment"
docker images > "${BASE_DIR}/docker-images"

if which calicoctl; then
mkdir -p "${CALICO_DIR}"
for kind in bgpPeer hostEndpoint ipPool nodes policy profile workloadEndpoint; do
calicoctl get "${kind}" -o yaml > "${CALICO_DIR}/${kind}.yaml"
done
fi

tar zcf "${SUBDIR_NAME}.tgz" -C "${TEMP_DIR}" "${SUBDIR_NAME}"

+ 24
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/drydock.sh View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

SCRIPT_DIR="$(realpath "$(dirname "$0")")"
WORKSPACE="$(realpath "${SCRIPT_DIR}/../../..")"
GATE_UTILS="${WORKSPACE}/seaworthy-virt/airship_gate/lib/all.sh"

source "${GATE_UTILS}"

drydock_cmd "$@"

+ 72
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/namespace-objects.sh View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# Copyright 2017 The Openstack-Helm Authors.
# Copyright 2019 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

export OBJECT_TYPE="${OBJECT_TYPE:="configmaps,cronjobs,daemonsets,deployment,endpoints,ingresses,jobs,networkpolicies,pods,podsecuritypolicies,persistentvolumeclaims,rolebindings,roles,secrets,serviceaccounts,services,statefulsets"}"
export PARALLELISM_FACTOR="${PARALLELISM_FACTOR:=2}"

function get_namespaces () {
kubectl get namespaces -o name | awk -F '/' '{ print $NF }'
}

function list_namespaced_objects () {
export NAMESPACE=$1
printf "%s" "${OBJECT_TYPE}" | xargs -d ',' -I {} -P1 -n1 bash -c "echo ${NAMESPACE} ${1#*/}" _ {}
}

export -f list_namespaced_objects

function name_objects () {
input=($1)
export NAMESPACE=${input[0]}
export OBJECT=${input[1]}
kubectl get -n "${NAMESPACE}" "${OBJECT}" -o name | xargs -L1 -I {} -P1 -n1 bash -c "echo ${NAMESPACE} ${OBJECT} ${1#*/}" _ {}
}

export -f name_objects

function get_objects () {
input=($1)
export NAMESPACE=${input[0]}
export OBJECT=${input[1]}
export NAME=${input[2]#*/}
echo "${NAMESPACE}/${OBJECT}/${NAME}"
export BASE_DIR="${BASE_DIR:="/tmp"}"
DIR="${BASE_DIR}/namespaces/${NAMESPACE}/${OBJECT}"
mkdir -p "${DIR}"
kubectl get -n "${NAMESPACE}" "${OBJECT}" "${NAME}" -o yaml > "${DIR}/${NAME}.yaml"
kubectl describe -n "${NAMESPACE}" "${OBJECT}" "${NAME}" > "${DIR}/${NAME}.txt"

LOG_DIR="${BASE_DIR}/pod-logs"
mkdir -p ${LOG_DIR}

if [ ${OBJECT_TYPE} = "pods" ]; then
POD_DIR="${LOG_DIR}/${NAME}"
mkdir -p "${POD_DIR}"
CONTAINERS=$(kubectl get pod "${NAME}" -n "${NAMESPACE}" -o json | jq -r '.spec.containers[].name')
for CONTAINER in ${CONTAINERS}; do
kubectl logs -n "${NAMESPACE}" "${NAME}" -c "${CONTAINER}" > "${POD_DIR}/${CONTAINER}.txt"
done
fi
}

export -f get_objects

get_namespaces | \
xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'list_namespaced_objects "$@"' _ {} | \
xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'name_objects "$@"' _ {} | \
xargs -r -n 1 -P ${PARALLELISM_FACTOR} -I {} bash -c 'get_objects "$@"' _ {}

+ 24
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/rsync.sh View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

SCRIPT_DIR="$(realpath "$(dirname "$0")")"
WORKSPACE="$(realpath "${SCRIPT_DIR}/../../..")"
GATE_UTILS="${WORKSPACE}/seaworthy-virt/airship_gate/lib/all.sh"

source "${GATE_UTILS}"

exec rsync -e "ssh -F ${SSH_CONFIG_DIR}/config" "$@"

+ 24
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/scp.sh View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

SCRIPT_DIR="$(realpath "$(dirname "$0")")"
WORKSPACE="$(realpath "${SCRIPT_DIR}/../../..")"
GATE_UTILS="${WORKSPACE}/seaworthy-virt/airship_gate/lib/all.sh"

source "${GATE_UTILS}"

exec scp -F "${SSH_CONFIG_DIR}/config" "$@"

+ 24
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/shipyard.sh View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -x

SCRIPT_DIR="$(realpath "$(dirname "$0")")"
WORKSPACE="$(realpath "${SCRIPT_DIR}/../../..")"
GATE_UTILS="${WORKSPACE}/seaworthy-virt/airship_gate/lib/all.sh"

source "${GATE_UTILS}"

shipyard_cmd "$@"

+ 24
- 0
tools/deployment/seaworthy-virt/airship_gate/bin/ssh.sh View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

SCRIPT_DIR="$(realpath "$(dirname "$0")")"
WORKSPACE="$(realpath "${SCRIPT_DIR}/../../..")"
GATE_UTILS="${WORKSPACE}/seaworthy-virt/airship_gate/lib/all.sh"

source "${GATE_UTILS}"

exec ssh -F "${SSH_CONFIG_DIR}/config" "$@"

+ 168
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/airship.sh View File

@@ -0,0 +1,168 @@
#!/bin/bash

install_ingress_ca() {
ingress_ca=$(config_ingress_ca)
if [[ -z "$ingress_ca" ]]
then
echo "Not installing ingress root CA."
return
fi
local_file="${TEMP_DIR}/ingress_ca.pem"
remote_file="${BUILD_WORK_DIR}/ingress_ca.pem"
cat <<< "$ingress_ca" > "$local_file"
rsync_cmd "$local_file" "${BUILD_NAME}":"$remote_file"
}

shipard_cmd_stdout() {
# needed to reach airship endpoints
dns_netspec="$(config_netspec_for_role "dns")"
dns_server=$(config_vm_net_ip "${BUILD_NAME}" "$dns_netspec")
install_ingress_ca
ssh_cmd "${BUILD_NAME}" \
docker run -t --network=host \
--dns "${dns_server}" \
-v "${BUILD_WORK_DIR}:/work" \
-e OS_AUTH_URL="${AIRSHIP_KEYSTONE_URL}" \
-e OS_USERNAME=shipyard \
-e OS_USER_DOMAIN_NAME=default \
-e OS_PASSWORD="${SHIPYARD_PASSWORD}" \
-e OS_PROJECT_DOMAIN_NAME=default \
-e OS_PROJECT_NAME=service \
-e REQUESTS_CA_BUNDLE=/work/ingress_ca.pem \
--entrypoint /usr/local/bin/shipyard "${IMAGE_SHIPYARD_CLI}" "$@" 2>&1
}

shipyard_cmd() {
if [[ ! -z "${LOG_FILE}" ]]
then
set -o pipefail
shipard_cmd_stdout "$@" | tee -a "${LOG_FILE}"
set +o pipefail
else
shipard_cmd_stdout "$@"
fi
}

drydock_cmd_stdout() {
dns_netspec="$(config_netspec_for_role "dns")"
dns_server="$(config_vm_net_ip "${BUILD_NAME}" "$dns_netspec")"
install_ingress_ca
ssh_cmd "${BUILD_NAME}" \
docker run -t --network=host \
--dns "${dns_server}" \
-v "${BUILD_WORK_DIR}:/work" \
-e DD_URL=http://drydock-api.ucp.svc.cluster.local:9000 \
-e OS_AUTH_URL="${AIRSHIP_KEYSTONE_URL}" \
-e OS_USERNAME=shipyard \
-e OS_USER_DOMAIN_NAME=default \
-e OS_PASSWORD="${SHIPYARD_PASSWORD}" \
-e OS_PROJECT_DOMAIN_NAME=default \
-e OS_PROJECT_NAME=service \
-e REQUESTS_CA_BUNDLE=/work/ingress_ca.pem \
--entrypoint /usr/local/bin/drydock "${IMAGE_DRYDOCK_CLI}" "$@" 2>&1
}
drydock_cmd() {
if [[ ! -z "${LOG_FILE}" ]]
then
set -o pipefail
drydock_cmd_stdout "$@" | tee -a "${LOG_FILE}"
set +o pipefail
else
drydock_cmd_stdout "$@"
fi
}

# Create a shipyard action
# and poll until completion
shipyard_action_wait() {
action="$1"
timeout="${2:-3600}"
poll_time="${3:-60}"

if [[ $action == "update_site" ]]
then
options="--allow-intermediate-commits"
else
options=""
fi

end_time=$(date -d "+${timeout} seconds" +%s)

log "Starting Shipyard action ${action}, will timeout in ${timeout} seconds."

ACTION_ID=$(shipyard_cmd create action ${options} "${action}")
ACTION_ID=$(echo "${ACTION_ID}" | grep -oE 'action/[0-9A-Z]+')

echo "Action ${ACTION_ID} has been created."

while true;
do
if [[ $(date +%s) -ge ${end_time} ]]
then
log "Shipyard action ${action} did not complete in ${timeout} seconds."
ACTION=$(shipyard_cmd describe "${ACTION_ID}")
log "${ACTION}"
return 2
fi

ACTION_STATUS=$(shipyard_cmd describe "${ACTION_ID}" | grep -i "Lifecycle" | \
awk '{print $2}')

ACTION_STEPS=$(shipyard_cmd describe "${ACTION_ID}" | grep -i "step/" | \
awk '{print $3}')

# Verify lifecycle status
if [ "${ACTION_STATUS}" == "Failed" ]; then
echo -e "\n${ACTION_ID} FAILED\n"
shipyard_cmd describe "${ACTION_ID}"
exit 1
fi

if [ "${ACTION_STATUS}" == "Complete" ]; then
# Verify status of each action step
for step in ${ACTION_STEPS}; do
if [ "${step}" == "failed" ]; then
echo -e "\n${ACTION_ID} FAILED\n"
shipyard_cmd describe "${ACTION_ID}"
exit 1
fi
done

echo -e "\n${ACTION_ID} completed SUCCESSFULLY\n"
shipyard_cmd describe "${ACTION_ID}"
exit 0
fi

sleep "${poll_time}"
done
}

# Re-use the ssh key from ssh-config
# for MAAS-deployed nodes
collect_ssh_key() {
mkdir -p "${GATE_DEPOT}"
if [[ ! -r ${SSH_CONFIG_DIR}/id_rsa.pub ]]
then
ssh_keypair_declare
fi

if [[ -n "${USE_EXISTING_SECRETS}" ]]; then
log "Using existing manifests for secrets"
return 0
fi

cat << EOF > "${GATE_DEPOT}/airship_ubuntu_ssh_key.yaml"
---
schema: deckhand/Certificate/v1
metadata:
schema: metadata/Document/v1
name: ubuntu_ssh_key
layeringDefinition:
layer: site
abstract: false
storagePolicy: cleartext
data: |-
EOF
sed -e 's/^/ /' >> "${GATE_DEPOT}/airship_ubuntu_ssh_key.yaml" < "${SSH_CONFIG_DIR}/id_rsa.pub"
}


+ 25
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/all.sh View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -e

LIB_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
export LIB_DIR
REPO_ROOT=$(realpath "$(dirname "${BASH_SOURCE[0]}")/../../../../..")
export REPO_ROOT

source "$LIB_DIR"/config.sh
source "$LIB_DIR"/const.sh
source "$LIB_DIR"/docker.sh
source "$LIB_DIR"/kube.sh
source "$LIB_DIR"/log.sh
source "$LIB_DIR"/nginx.sh
source "$LIB_DIR"/promenade.sh
source "$LIB_DIR"/registry.sh
source "$LIB_DIR"/ssh.sh
source "$LIB_DIR"/virsh.sh
source "$LIB_DIR"/airship.sh
source "$LIB_DIR"/ingress.sh
source "$LIB_DIR"/bgp.sh

if [[ -v GATE_DEBUG && ${GATE_DEBUG} = "1" ]]; then
set -x
fi

+ 37
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/bgp.sh View File

@@ -0,0 +1,37 @@
#!/bin/bash

QUAGGA_DAEMONS="${TEMP_DIR}/daemons"
QUAGGA_DEBIAN_CONF="${TEMP_DIR}/debian.conf"
QUAGGA_BGPD_CONF="${TEMP_DIR}/bgpd.conf"

bgp_router_config() {
quagga_as_number="$(config_bgp_as "quagga_as")"
calico_as_number="$(config_bgp_as "calico_as")"
bgp_net="$(config_netspec_for_role "bgp")"
quagga_ip="$(config_vm_net_ip "build" "$bgp_net")"

# shellcheck disable=SC2016
QUAGGA_AS=${quagga_as_number} CALICO_AS=${calico_as_number} QUAGGA_IP=${quagga_ip} envsubst '${QUAGGA_AS} ${CALICO_AS} ${QUAGGA_IP}' < "${TEMPLATE_DIR}/bgpd_conf.sub" > "${QUAGGA_BGPD_CONF}"

cp "${TEMPLATE_DIR}/daemons.sub" "${QUAGGA_DAEMONS}"
cp "${TEMPLATE_DIR}/debian_conf.sub" "${QUAGGA_DEBIAN_CONF}"

}

bgp_router_start() {
# nodename where BGP router should run
nodename=$1
remote_work_dir="/var/tmp/quagga"

remote_daemons_file="${remote_work_dir}/$(basename "$QUAGGA_DAEMONS")"
remote_debian_conf_file="${remote_work_dir}/$(basename "$QUAGGA_DEBIAN_CONF")"
remote_bgpd_conf_file="${remote_work_dir}/$(basename "$QUAGGA_BGPD_CONF")"

ssh_cmd "${nodename}" mkdir -p "${remote_work_dir}"

rsync_cmd "$QUAGGA_DAEMONS" "${nodename}:${remote_daemons_file}"
rsync_cmd "$QUAGGA_DEBIAN_CONF" "${nodename}:${remote_debian_conf_file}"
rsync_cmd "$QUAGGA_BGPD_CONF" "${nodename}:${remote_bgpd_conf_file}"

ssh_cmd "${nodename}" docker run -ti -d --net=host --privileged -v /var/tmp/quagga:/etc/quagga --restart always --name Quagga "$IMAGE_QUAGGA"
}

+ 173
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/bootaction-runner.sh View File

@@ -0,0 +1,173 @@
#!/bin/bash
#
# Copyright 2019 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


###############################################################################
# Helper functions
###############################################################################

# Key/value lookups from manifests
manifests_lookup(){
local file="$1"
local schema="$2"
local mdata_name="$3"
local key_path="$4"
local oper="$5"
local allow_fail="$6"

FAIL=false
RESULT=$(python3 -c "
import yaml,sys
y = yaml.load_all(open('$file'))
for x in y:
if x.get('schema') == '$schema':
if x['metadata']['name'] == '$mdata_name':
if isinstance(x$key_path,list):
if '$oper' == 'get_size':
print(len(x$key_path))
break
else:
for i in x$key_path:
print(i)
break
else:
if '$oper' == 'dict_keys':
print(' '.join(x$key_path.keys()))
break
else:
print(x$key_path)
break
else:
sys.exit(1)" 2>&1) || FAIL=true

if [[ $FAIL = true ]] && [[ $allow_fail != true ]]; then
echo "Lookup failed for schema '$schema', metadata.name '$mdata_name', key path '$key_path'"
exit 1
fi
}


install_file(){
local path="$1"
local content="$2"
local permissions="$3"
local dirname
dirname=$(dirname "$path")

if [[ ! -d $dirname ]]; then
mkdir -p "$dirname"
fi

if [[ ! -f $path ]] || [ "$(cat "$path")" != "$content" ]; then
echo "$content" > "$path"
chmod "$permissions" "$path"
export FILE_UPDATED=true
else
export FILE_UPDATED=false
fi
}


###############################################################################
# Script inputs and validations
###############################################################################

if [[ $EUID -ne 0 ]]; then
echo "This script must be run as sudo/root"
exit 1
fi

if ([[ -z $1 ]] && [[ -z $RENDERED ]]) || [[ $1 =~ .*[hH][eE][lL][pP].* ]]; then
echo "Missing required script argument"
echo "Usage: ./$(basename "${BASH_SOURCE[0]}") /path/to/rendered/site/manifest.yaml"
exit 1
fi

if [[ -n $1 ]]; then
rendered_file="$1"
else
rendered_file="$RENDERED"
fi
if [[ ! -f $rendered_file ]]; then
echo "Specified rendered manifests file '$rendered_file' does not exist"
exit 1
fi
echo "Using rendered manifests file '$rendered_file'"

# env vars which can be set if you want to disable
: "${DISABLE_SECCOMP_PROFILE:=}"
: "${DISABLE_APPARMOR_PROFILES:=}"


###############################################################################
# bootaction: seccomp-profiles
###############################################################################

if [[ ! $DISABLE_SECCOMP_PROFILE ]]; then

# Fetch seccomp profile data
manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"seccomp-profiles" "['data']['assets'][0]['path']"
path="$RESULT"
echo "seccomp profiles asset[0] path located: '$path'"
manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"seccomp-profiles" "['data']['assets'][0]['permissions']"
permissions="$RESULT"
echo "seccomp profiles asset[0] permissions located: '$permissions'"
manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"seccomp-profiles" "['data']['assets'][0]['data']"
content="$RESULT"
echo "seccomp profiles assets[0] data located: '$content'"

# seccomp_default
install_file "$path" "$content" "$permissions"
fi

###############################################################################
# bootaction: apparmor-profiles
###############################################################################

if [[ ! $DISABLE_APPARMOR_PROFILES ]]; then

manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"apparmor-profiles" "['data']['assets']" "get_size" "true"

if [[ -n "$RESULT" ]] && [[ $RESULT -gt 0 ]]; then

# Fetch apparmor profile data
LAST=$(( RESULT - 1 ))
for i in $(seq 0 $LAST); do

manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"apparmor-profiles" "['data']['assets'][$i]['path']"
path="$RESULT"
echo "apparmor profiles asset[$i] path located: '$path'"
manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"apparmor-profiles" "['data']['assets'][$i]['permissions']"
permissions="$RESULT"
echo "apparmor profiles asset[$i] permissions located: '$permissions'"
manifests_lookup "$rendered_file" "drydock/BootAction/v1" \
"apparmor-profiles" "['data']['assets'][$i]['data']"
content="$RESULT"
echo "apparmor profiles assets[$i] data located: '$content'"

install_file "$path" "$content" "$permissions"
done

# reload all apparmor profiles
systemctl reload apparmor.service
fi
fi

+ 514
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/config.sh View File

@@ -0,0 +1,514 @@
#!/bin/bash
export TEMP_DIR=${TEMP_DIR:-$(mktemp -d -p /var/tmp)}
export NAMEKEY_FILE=${NAMEKEY_FILE:-"$HOME/.airship_key"}
export DEFINITION_DEPOT="${TEMP_DIR}/site_yaml/"
export RENDERED_DEPOT="${TEMP_DIR}/rendered_yaml/"
export CERT_DEPOT="${TEMP_DIR}/cert_yaml/"
export GATE_DEPOT="${TEMP_DIR}/gate_yaml/"
export SCRIPT_DEPOT="${TEMP_DIR}/scripts/"
export BUILD_WORK_DIR=${BUILD_WORK_DIR:-/work}
export BASE_IMAGE_SIZE=${BASE_IMAGE_SIZE:-68719476736}
export BASE_IMAGE_URL=${BASE_IMAGE_URL:-https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img}
export IMAGE_PROMENADE_CLI=${IMAGE_PROMENADE_CLI:-quay.io/airshipit/promenade:cfb8aa498c294c2adbc369ba5aaee19b49550d22}
export IMAGE_PEGLEG_CLI=${IMAGE_PEGLEG_CLI:-quay.io/airshipit/pegleg:50ce7a02e08a0a5277c2fbda96ece6eb5782407a}
export IMAGE_SHIPYARD_CLI=${IMAGE_SHIPYARD_CLI:-quay.io/airshipit/shipyard:4dd6b484d11e86ad51da733841b9ef137421d461}
export IMAGE_COREDNS=${IMAGE_COREDNS:-docker.io/coredns/coredns:1.2.2}
export IMAGE_QUAGGA=${IMAGE_QUAGGA:-docker.io/cumulusnetworks/quagga:CL3.3.2}
export IMAGE_DRYDOCK_CLI=${IMAGE_DRYDOCK_CLI:-quay.io/airshipit/drydock:d93d6d5a0a370ced536180612d1ade708e29cd47}
export IMAGE_DOCKER_REGISTRY=${IMAGE_DOCKER_REGISTRY:-"docker.io/registry:2"}
export IMAGE_HYPERKUBE=${IMAGE_HYPERKUBE:-gcr.io/google_containers/hyperkube-amd64:v1.12.9}
export PROMENADE_DEBUG=${PROMENADE_DEBUG:-0}
export PROMENADE_TMP_LOCAL=${PROMENADE_TMP_LOCAL:-cache}
export REGISTRY_DATA_DIR=${REGISTRY_DATA_DIR:-/mnt/registry}
export VIRSH_POOL=${VIRSH_POOL:-airship}
export VIRSH_POOL_PATH=${VIRSH_POOL_PATH:-/var/lib/libvirt/airship}
export VIRSH_CPU_OPTS=${VIRSH_CPU_OPTS:-host}
export UPSTREAM_DNS=${UPSTREAM_DNS:-"8.8.8.8 8.8.4.4"}
export NTP_POOLS=${NTP_POOLS:-"0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org"}
export NTP_SERVERS=${NTP_SERVERS:-""}
export PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY:-MjI1N2ZiMjMzYjI0ZmVkZDU4}

# key-pair used for drydock/maas auth towards libvirt and access to
# the virtual nodes; auto-generated if no value provided
export GATE_SSH_KEY=${GATE_SSH_KEY:-""}

# skip generation of certificates, and other security manifests
# auto-generated by default
export USE_EXISTING_SECRETS=${USE_EXISTING_SECRETS:-""}

export SHIPYARD_PASSWORD=${SHIPYARD_OS_PASSWORD:-'password18'}
export AIRSHIP_KEYSTONE_URL=${AIRSHIP_KEYSTONE_URL:-'http://keystone.gate.local:80/v3'}

config_vm_memory() {
nodename=${1}
jq -cr ".vm.${nodename}.memory" < "${GATE_MANIFEST}"
}

config_vm_names() {
jq -cr '.vm | keys | join(" ")' < "${GATE_MANIFEST}"
}

config_vm_iface_list() {
nodename="$1"
jq -cr ".vm.${nodename}.networking | del(.addresses) | keys | .[]" < "${GATE_MANIFEST}"
}

config_vm_iface_mac() {
nodename="$1"
interface="$2"
jq -cr ".vm.${nodename}.networking.${interface}.mac" < "${GATE_MANIFEST}"
}

# What network this VM interface should be attached to
config_vm_iface_network() {
nodename="$1"
interface="$2"
jq -cr ".vm.${nodename}.networking.${interface}.attachment.network" < "${GATE_MANIFEST}"
}

# What VLANs on a network should be attached to this node
config_vm_iface_vlans() {
nodename="$1"
interface="$2"
jq -cr ".vm.${nodename}.networking.${interface}.attachment.vlans | select(.!=null)" < "${GATE_MANIFEST}"
}

# PCI slot for this VM interface
config_vm_iface_slot() {
nodename="$1"
interface="$2"
jq -cr ".vm.${nodename}.networking.${interface}.pci.slot" < "${GATE_MANIFEST}"
}

# PCI card port for this VM interface
config_vm_iface_port() {
nodename="$1"
interface="$2"
jq -cr ".vm.${nodename}.networking.${interface}.pci.port" < "${GATE_MANIFEST}"
}

# The IP address for the VM for a network. If vlan is also specified, the VLAN
# on the network
config_vm_net_ip() {
nodename="$1"
network="$2"
vlan="$3"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".vm.${nodename}.networking.addresses.${network}.ip"
else
query=".vm.${nodename}.networking.addresses.${network}.vlans.${vlan}.ip"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_vm_vcpus() {
nodename=${1}
jq -cr ".vm.${nodename}.vcpus" < "${GATE_MANIFEST}"
}

config_vm_bootstrap() {
nodename=${1}
val=$(jq -cr ".vm.${nodename}.bootstrap" < "${GATE_MANIFEST}")
if [[ "${val}" == "true" ]]
then
echo "true"
else
echo "false"
fi
}

config_disk_list() {
layout_name="${1}"
jq -cr ".disk_layouts.${layout_name} | keys | join(\" \")" < "${GATE_MANIFEST}"
}

config_disk_details() {
layout_name="${1}"
disk_device="${2}"
jq -cr ".disk_layouts.${layout_name}.${disk_device}" < "${GATE_MANIFEST}"
}

config_disk_size() {
layout_name="$1"
disk_device="$2"
jq -cr ".disk_layouts.${layout_name}.${disk_device}.size" < "${GATE_MANIFEST}"
}

config_disk_format() {
layout_name="$1"
disk_device="$2"
do_format=$(jq -cr ".disk_layouts.${layout_name}.${disk_device} | has(\"format\")")

if [[ "$do_format" == "true" && "$disk_device" != "$(config_layout_bootstrap "$layout_name")" ]]
then
jq -cr ".disk_layouts.${layout_name}.${disk_device}.format" < "${GATE_MANIFEST}"
else
echo ""
fi
}

config_format_type() {
JSON="$1"
echo "$JSON" | jq -cr '.type'
}

config_format_mount() {
JSON="$1"
echo "$JSON" | jq -cr '.mountpoint'
}

config_disk_ioprofile() {
layout_name="$1"
disk_device="$2"
jq -cr ".disk_layouts.${layout_name}.${disk_device}.io_profile" < "${GATE_MANIFEST}"
}

# Find which disk in a layout should
# get the bootstrap image
config_layout_bootstrap() {
layout_name="${1}"
jq -cr ".disk_layouts.${layout_name} | keys[] as \$k | {device: (\$k)} + (.[\$k]) | select(.bootstrap) | .device " < "${GATE_MANIFEST}"
}

config_vm_disk_layout() {
nodename=${1}
jq -cr ".vm.${nodename}.disk_layout" < "${GATE_MANIFEST}"
}

config_vm_userdata() {
nodename=${1}
val=$(jq -cr ".vm.${nodename}.userdata" < "${GATE_MANIFEST}")

if [[ "${val}" != "null" ]]
then
echo "${val}"
fi
}

config_bgp_as() {
as_number=${1}
jq -cr ".bgp.${as_number}" < "${GATE_MANIFEST}"
}

config_net_list() {
jq -cr '.networking | keys | .[]' < "${GATE_MANIFEST}"
}

config_net_vlan_list() {
network="$1"

jq -cr ".networking.${network}.layer2.vlans // {} | keys | .[]" < "${GATE_MANIFEST}"
}

config_net_cidr() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".networking.${network}.layer3.cidr"
else
query=".networking.${network}.vlans.${vlan}.layer3.cidr"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_net_is_layer3() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".networking.${network} | has(\"layer3\")"
else
query=".network.${network}.vlans.${vlan} | has(\"layer3\")"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

# Find the layer 3 network tagged for a particular
# role - this can be either a native or vlan network
# If multiple networks have a role, the results is
# undefined
config_netspec_for_role() {
role="$1"

set -e

for net in $(config_net_list)
do
if config_net_has_role "$net" "$role"
then
netspec="$net"
fi

for vlan in $(config_net_vlan_list "$net")
do
if config_vlan_has_role "$net" "$vlan" "$role"
then
netspec="${vlan}@${net}"
fi
done
done

echo -n "$netspec"
}

config_net_has_role() {
netname="$1"
role="$2"

value="$(jq -cr ".networking.${netname}.roles | contains([\"${role}\"])" < "$GATE_MANIFEST")"

if [ "$value" == "true" ]
then
return 0
else
return 1
fi
}

config_vlan_has_role() {
netname="$1"
vlan="$2"
role="$3"

value="$(jq -cr " .networking.${netname}.vlans.${vlan}.roles | contains([\"${role}\"])" < "$GATE_MANIFEST")"

if [ "$value" == "true" ]
then
return 0
else
return 1
fi
}

config_net_selfip() {
network="$1"
vlan="$2"

if [[ -z "$vlan" ]]
then
query=".networking.${network}.layer3.address"
else
query=".networking.${network}.vlans.${vlan}.layer3.address"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_net_selfip_cidr() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

selfip=$(config_net_selfip "$network" "$vlan")
netcidr=$(config_net_cidr "$network" "$vlan")
netbits=$(echo "$netcidr" | awk -F '/' '{print $2}')

printf "%s/%s" "$selfip" "$netbits"
}

config_net_gateway() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".networking.${network}.layer3.gateway"
else
query=".networking.${network}.vlans.${vlan}.layer3.gateway"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_net_routemode() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".networking.${network}.layer3.routing.mode"
else
query=".networking.${network}.vlans.${vlan}.layer3.routing.mode"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_net_mtu() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".networking.${network}.layer2.mtu // 1500"
else
query=".networking.${network}.vlans.${vlan}.layer2.mtu // 1500"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_net_mac() {
network="$1"
vlan="$2"

if is_netspec "$network"
then
vlan=$(netspec_vlan "$network")
network=$(netspec_netname "$network")
fi

if [[ -z "$vlan" ]]
then
query=".networking.${network}.layer2.address"
else
query=".networking.${network}.vlans.${vlan}.layer2.address"
fi

jq -cr "$query" < "${GATE_MANIFEST}"
}

config_ingress_domain() {
jq -cr '.ingress.domain' < "${GATE_MANIFEST}"
}

config_ingress_ca() {
if [[ ! -z "$GATE_MANIFEST" ]]
then
jq -cr '.ingress.ca' < "${GATE_MANIFEST}"
fi
}

config_ingress_ips() {
jq -cr '.ingress | keys | map(select(test("([0-9]{1,3}.?){4}"))) | join(" ")' < "${GATE_MANIFEST}"
}

config_ingress_entries() {
IP=$1
jq -cr ".ingress[\"${IP}\"] | join(\" \")" < "${GATE_MANIFEST}"
}

config_pegleg_primary_repo() {
jq -cr ".configuration.primary_repo" < "${GATE_MANIFEST}"
}

config_pegleg_sitename() {
jq -cr ".configuration.site" < "${GATE_MANIFEST}"
}

config_pegleg_aux_repos() {
jq -cr '.configuration.aux_repos | join(" ")' < "${GATE_MANIFEST}"
}

join_array() {
local IFS=$1
shift
echo "$*"
}

besteffort() {
set +e
"$@"
set -e
}

get_namekey() {
if [[ -r "$NAMEKEY_FILE" ]]
then
key=$(cat "$NAMEKEY_FILE")
else
key=$(openssl rand -hex 4)
echo -n "$key" > "$NAMEKEY_FILE"
fi

echo -n "$key"
}

is_netspec(){
value="$1"

if echo -n "$value" | grep -q "[0-9]+@.+"
then
return 0
else
return 1
fi
}

netspec_netname(){
netspec="$1"

echo -n "$netspec" | awk -F'@' '{print $2}'
}

netspec_vlan(){
netspec="$1"

echo -n "$netspec" | awk -F'@' '{print $1}'
}

# We'll just add the conversions as needed
cidr_to_netmask() {
cidr="$1"
netbits="$(echo "$cidr" | awk -F'/' '{print $2}')"

case "$netbits" in
32)
netmask="255.255.255.255"
;;
24)
netmask="255.255.255.0"
;;
esac

echo "$netmask"
}

+ 6
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/const.sh View File

@@ -0,0 +1,6 @@
#!/bin/bash
export GENESIS_NAME=n0
export BUILD_NAME=build
export SSH_CONFIG_DIR=${WORKSPACE}/seaworthy-virt/airship_gate/config-ssh
export TEMPLATE_DIR=${WORKSPACE}/seaworthy-virt/airship_gate/templates
export XML_DIR=${WORKSPACE}/seaworthy-virt/airship_gate/xml

+ 27
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/docker.sh View File

@@ -0,0 +1,27 @@
#!/bin/bash
docker_ps() {
VIA="${1}"
ssh_cmd "${VIA}" docker ps -a
}

docker_info() {
VIA="${1}"
ssh_cmd "${VIA}" docker info 2>&1
}

docker_exited_containers() {
VIA="${1}"
ssh_cmd "${VIA}" docker ps -q --filter "status=exited"
}

docker_inspect() {
VIA="${1}"
CONTAINER_ID="${2}"
ssh_cmd "${VIA}" docker inspect "${CONTAINER_ID}"
}

docker_logs() {
VIA="${1}"
CONTAINER_ID="${2}"
ssh_cmd "${VIA}" docker logs "${CONTAINER_ID}"
}

+ 54
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/ingress.sh View File

@@ -0,0 +1,54 @@
#!/bin/bash

DNS_ZONE_FILE="${TEMP_DIR}/ingress.dns"
COREFILE="${TEMP_DIR}/ingress.corefile"
RESOLV_CONF="${TEMP_DIR}/resolv.conf"

ingress_dns_config() {
ingress_domain="$(config_ingress_domain)"

#shellcheck disable=SC2016
INGRESS_DOMAIN="${ingress_domain}" envsubst '${INGRESS_DOMAIN}' < "${TEMPLATE_DIR}/ingress_header.sub" > "${DNS_ZONE_FILE}"

read -r -a ingress_ip_list <<< "$(config_ingress_ips)"

for ip in "${ingress_ip_list[@]}"
do
# TODO(sthussey) shift config_ingress_entries to printf w/ quotes
# shellcheck disable=SC2046
read -r -a ip_entries <<< $(config_ingress_entries "$ip")
for entry in "${ip_entries[@]}"
do
HOSTNAME="${entry}" HOSTIP="${ip}" envsubst < "${TEMPLATE_DIR}/ingress_entry.sub" >> "${DNS_ZONE_FILE}"
done
done

DNS_DOMAIN="${ingress_domain}" ZONE_FILE="$(basename "$DNS_ZONE_FILE")" DNS_SERVERS="$UPSTREAM_DNS" envsubst < "${TEMPLATE_DIR}/ingress_corefile.sub" > "${COREFILE}"
}

ingress_resolv_conf() {
# Update node DNS settings
touch "$RESOLV_CONF"
for server in $UPSTREAM_DNS; do
if ! grep "nameserver $server" "$RESOLV_CONF"; then
echo "nameserver $server" >> "$RESOLV_CONF"
fi
done
}

ingress_dns_start() {
# nodename where DNS should run
nodename="$1"
remote_work_dir="/var/tmp/coredns"

remote_zone_file="${remote_work_dir}/$(basename "$DNS_ZONE_FILE")"
remote_corefile="${remote_work_dir}/$(basename "$COREFILE")"
remote_resolv_conf="/etc/$(basename "$RESOLV_CONF")"
ssh_cmd "${nodename}" mkdir -p "${remote_work_dir}"
rsync_cmd "$DNS_ZONE_FILE" "${nodename}:${remote_zone_file}"
rsync_cmd "$COREFILE" "${nodename}:${remote_corefile}"
rsync_cmd "$RESOLV_CONF" "${nodename}:${remote_resolv_conf}"
ssh_cmd "${nodename}" systemctl stop systemd-resolved
ssh_cmd "${nodename}" systemctl disable systemd-resolved
ssh_cmd "${nodename}" docker run -d -v /var/tmp/coredns:/data -w /data --network host --restart always -P "$IMAGE_COREDNS" -conf "$(basename "$remote_corefile")"
}

+ 47
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/kube.sh View File

@@ -0,0 +1,47 @@
#!/bin/bash

kubectl_apply() {
VIA=${1}
FILE=${2}
ssh_cmd_raw "${VIA}" "KUBECONFIG=${KUBECONFIG}" "cat ${FILE} | kubectl apply -f -"
}

kubectl_cmd() {
VIA=${1}

shift

ssh_cmd_raw "${VIA}" "KUBECONFIG=${KUBECONFIG}" kubectl "${@}"

}

kubectl_wait_for_pod() {
VIA=${1}
NAMESPACE=${2}
POD_NAME=${3}
SEC=${4:-600}
log Waiting "${SEC}" seconds for termination of pod "${POD_NAME}"

POD_PHASE_JSONPATH='{.status.phase}'

end=$(($(date +%s) + SEC))
while true; do
POD_PHASE=$(kubectl_cmd "${VIA}" --request-timeout 10s --namespace "${NAMESPACE}" get -o jsonpath="${POD_PHASE_JSONPATH}" pod "${POD_NAME}")
if [[ ${POD_PHASE} = "Succeeded" ]]; then
log Pod "${POD_NAME}" succeeded.
break
elif [[ $POD_PHASE = "Failed" ]]; then
log Pod "${POD_NAME}" failed.
kubectl_cmd "${VIA}" --request-timeout 10s --namespace "${NAMESPACE}" get -o yaml pod "${POD_NAME}" 1>&2
exit 1
else
now=$(date +%s)
if [[ $now -gt $end ]]; then
log Pod did not terminate before timeout.
kubectl_cmd "${VIA}" --request-timeout 10s --namespace "${NAMESPACE}" get -o yaml pod "${POD_NAME}" 1>&2
exit 1
fi
sleep 1
fi
done
}

+ 84
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/log.sh View File

@@ -0,0 +1,84 @@
#!/bin/bash
if [[ -v GATE_COLOR && ${GATE_COLOR} = "1" ]]; then
C_CLEAR="\e[0m"
C_ERROR="\e[38;5;160m"
C_HEADER="\e[38;5;164m"
C_HILIGHT="\e[38;5;27m"
C_MUTE="\e[38;5;238m"
C_SUCCESS="\e[38;5;46m"
C_TEMP="\e[38;5;226m"
else
C_CLEAR=""
C_ERROR=""
C_HEADER=""
C_HILIGHT=""
C_MUTE=""
C_SUCCESS=""
C_TEMP=""
fi

log() {
d=$(date --utc)
echo -e "${C_MUTE}${d}${C_CLEAR} ${*}" 1>&2
echo -e "${d} ${*}" >> "${LOG_FILE}"
}

log_warn() {
d=$(date --utc)
echo -e "${C_MUTE}${d}${C_CLEAR} ${C_HILIGHT}WARN${C_CLEAR} ${*}" 1>&2
echo -e "${d} ${*}" >> "${LOG_FILE}"
}

log_error() {
d=$(date --utc)
echo -e "${C_MUTE}${d}${C_CLEAR} ${C_ERROR}ERROR${C_CLEAR} ${*}" 1>&2
echo -e "${d} ${*}" >> "${LOG_FILE}"
}


log_stage_diagnostic_header() {
echo -e " ${C_ERROR}= Diagnostic Report =${C_CLEAR}"
}

log_color_reset() {
echo -e "${C_CLEAR}"
}

log_huge_success() {
echo -e "${C_SUCCESS}=== HUGE SUCCESS ===${C_CLEAR}"
}

log_note() {
echo -e "${C_HILIGHT}NOTE:${C_CLEAR} ${*}"
}

log_stage_error() {
NAME=${1}
echo -e " ${C_ERROR}== Error in stage ${C_HILIGHT}${NAME}${C_ERROR} ( ${C_TEMP}${LOG_FILE}${C_ERROR} ) ==${C_CLEAR}"
}

log_stage_footer() {
NAME=${1}
echo -e "${C_HEADER}=== Finished stage ${C_HILIGHT}${NAME}${C_HEADER} ===${C_CLEAR}"
}

log_stage_header() {
NAME=${1}
echo -e "${C_HEADER}=== Executing stage ${C_HILIGHT}${NAME}${C_HEADER} ===${C_CLEAR}"
}

log_stage_success() {
echo -e " ${C_SUCCESS}== Stage Success ==${C_CLEAR}"
}

log_temp_dir() {
echo -e "Working in ${C_TEMP}${TEMP_DIR}${C_CLEAR}"
}

if [[ -v GATE_DEBUG && ${GATE_DEBUG} = "1" ]]; then
export LOG_FILE=/dev/stderr
elif [[ -v TEMP_DIR ]]; then
export LOG_FILE=${TEMP_DIR}/gate.log
else
export LOG_FILE=/dev/null
fi

+ 37
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/nginx.sh View File

@@ -0,0 +1,37 @@
#!/bin/bash

nginx_down() {
REGISTRY_ID=$(docker ps -qa -f name=promenade-nginx)
if [ "x${REGISTRY_ID}" != "x" ]; then
log Removing nginx server
docker rm -fv "${REGISTRY_ID}" &>> "${LOG_FILE}"
fi
}

nginx_up() {
log Starting nginx server to serve configuration files
mkdir -p "${NGINX_DIR}"
docker run -d \
-p 7777:80 \
--restart=always \
--name promenade-nginx \
-v "${TEMP_DIR}/nginx:/usr/share/nginx/html:ro" \
nginx:stable &>> "${LOG_FILE}"
}

nginx_cache_and_replace_tar_urls() {
log "Finding tar_url options to cache.."
TAR_NUM=0
mkdir -p "${NGINX_DIR}"
for file in "$@"; do
grep -Po "^ +tar_url: \K.+$" "${file}" | while read -r tar_url ; do
# NOTE(mark-burnet): Does not yet ignore repeated files.
DEST_PATH="${NGINX_DIR}/cached-tar-${TAR_NUM}.tgz"
log "Caching ${tar_url} in file: ${DEST_PATH}"
REPLACEMENT_URL="${NGINX_URL}/cached-tar-${TAR_NUM}.tgz"
curl -Lo "${DEST_PATH}" "${tar_url}"
sed -i "s;${tar_url};${REPLACEMENT_URL};" "${file}"
TAR_NUM=$((TAR_NUM + 1))
done
done
}

+ 17
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/promenade.sh View File

@@ -0,0 +1,17 @@
#!/bin/bash

promenade_health_check() {
VIA=${1}
log "Checking Promenade API health"
MAX_HEALTH_ATTEMPTS=6
for attempt in $(seq ${MAX_HEALTH_ATTEMPTS}); do
if ssh_cmd "${VIA}" curl -v --fail "${PROMENADE_BASE_URL}/api/v1.0/health"; then
log "Promenade API healthy"
break
elif [[ $attempt == "${MAX_HEALTH_ATTEMPTS}" ]]; then
log "Promenade health check failed, max retries (${MAX_HEALTH_ATTEMPTS}) exceeded."
exit 1
fi
sleep 10
done
}

+ 76
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/registry.sh View File

@@ -0,0 +1,76 @@
#!/bin/bash

registry_down() {
REGISTRY_ID=$(docker ps -qa -f name=registry)
if [[ ! -z ${REGISTRY_ID} ]]; then
log Removing docker registry
docker rm -fv "${REGISTRY_ID}" &>> "${LOG_FILE}"
fi
}

registry_list_images() {
FILES=($(find "${DEFINITION_DEPOT}" -type f -name '*.yaml'))

HOSTNAME_REGEX='[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}'
DOMAIN_NAME_REGEX="${HOSTNAME_REGEX}(\.${HOSTNAME_REGEX})*"
PORT_REGEX='[0-9]+'
NETLOC_REGEX="${DOMAIN_NAME_REGEX}(:${PORT_REGEX})?"

REPO_COMPONENT_REGEX='[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}'
REPO_REGEX="${REPO_COMPONENT_REGEX}(/${REPO_COMPONENT_REGEX})*"

TAG_REGEX='[a-zA-Z0-9][a-zA-Z0-9.-]{0,127}'

cat "${FILES[@]}" \
| grep -v '^ *#' \
| tr ' \t' '\n' | tr -s '\n' \
| grep -E "^(${NETLOC_REGEX}/)?${REPO_REGEX}:${TAG_REGEX}$" \
| sort -u \
| grep -v 'registry:5000'
}

registry_populate() {
log Validating local registry is populated
for image in $(registry_list_images); do
if [[ ${image} =~ promenade ]]; then
continue
fi

if [[ ${image} =~ .*:(latest|master) ]] || ! docker pull "localhost:5000/${image}" &> /dev/null; then
log Loading image "${image}" into local registry
{
docker pull "${image}"
docker tag "${image}" "localhost:5000/${image}"
docker push "localhost:5000/${image}"
} &>> "${LOG_FILE}" || echo "Failed to cache ${image}"
fi
done
}

registry_replace_references() {
FILES=(${@})
for image in $(registry_list_images); do
sed -i "s;${image}\$;registry:5000/${image};g" "${FILES[@]}"
done
}

registry_up() {
log Validating local registry is up
REGISTRY_ID=$(docker ps -qa -f name=registry)
RUNNING_REGISTRY_ID=$(docker ps -q -f name=registry)
if [[ -z ${RUNNING_REGISTRY_ID} && ! -z ${REGISTRY_ID} ]]; then
log Removing stopped docker registry
docker rm -fv "${REGISTRY_ID}" &>> "${LOG_FILE}"
fi

if [[ -z ${RUNNING_REGISTRY_ID} ]]; then
log Starting docker registry
docker run -d \
-p 5000:5000 \
-e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
--restart=always \
--name registry \
-v "${REGISTRY_DATA_DIR}:/var/lib/registry" \
"${IMAGE_DOCKER_REGISTRY}" &>> "${LOG_FILE}"
fi
}

+ 78
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/ssh.sh View File

@@ -0,0 +1,78 @@
#!/bin/bash

rsync_cmd() {
rsync -e "ssh -F ${SSH_CONFIG_DIR}/config" "${@}"
}

ssh_cmd_raw() {
# shellcheck disable=SC2068
ssh -F "${SSH_CONFIG_DIR}/config" $@
}

ssh_cmd() {
HOST=${1}
shift
args=$(shell-quote -- "${@}")
if [[ -v GATE_DEBUG && ${GATE_DEBUG} = "1" ]]; then
# shellcheck disable=SC2029
ssh -F "${SSH_CONFIG_DIR}/config" -v "${HOST}" "${args}"
else
# shellcheck disable=SC2029
ssh -F "${SSH_CONFIG_DIR}/config" "${HOST}" "${args}"
fi
}

ssh_config_declare() {
log Creating SSH config
env -i \
"SSH_CONFIG_DIR=${SSH_CONFIG_DIR}" \
envsubst < "${TEMPLATE_DIR}/ssh-config-global.sub" > "${SSH_CONFIG_DIR}/config"
for n in $(config_vm_names)
do
ssh_net="$(config_netspec_for_role "ssh")"
env -i \
"SSH_CONFIG_DIR=${SSH_CONFIG_DIR}" \
"SSH_NODE_HOSTNAME=${n}" \
"SSH_NODE_IP=$(config_vm_net_ip "${n}" "$ssh_net")" \
envsubst < "${TEMPLATE_DIR}/ssh-config-node.sub" >> "${SSH_CONFIG_DIR}/config"
if [[ "$(config_vm_bootstrap "${n}")" == "true" ]]
then
echo " User root" >> "${SSH_CONFIG_DIR}/config"
else
echo " User ubuntu" >> "${SSH_CONFIG_DIR}/config"
fi
done
}

ssh_keypair_declare() {
log Validating SSH keypair exists
if [ ! -s "${SSH_CONFIG_DIR}/id_rsa" ]; then
if [[ "${GATE_SSH_KEY}" ]]; then
log "Using existing SSH keys for VMs"
cp "${GATE_SSH_KEY}" "${SSH_CONFIG_DIR}/id_rsa"
chmod 600 "${SSH_CONFIG_DIR}/id_rsa"

cp "${GATE_SSH_KEY}.pub" "${SSH_CONFIG_DIR}/id_rsa.pub"
else
log Generating SSH keypair
ssh-keygen -N '' -f "${SSH_CONFIG_DIR}/id_rsa" &>> "${LOG_FILE}"
fi
fi
}

ssh_load_pubkey() {
cat "${SSH_CONFIG_DIR}/id_rsa.pub"
}

ssh_setup_declare() {
mkdir -p "${SSH_CONFIG_DIR}"
ssh_keypair_declare
ssh_config_declare
}

ssh_wait() {
NAME=${1}
while ! ssh_cmd "${NAME}" /bin/true; do
sleep 0.5
done
}

+ 664
- 0
tools/deployment/seaworthy-virt/airship_gate/lib/virsh.sh View File

@@ -0,0 +1,664 @@
#!/bin/bash
img_base_declare() {
log Validating base image exists
if ! virsh vol-key --pool "${VIRSH_POOL}" --vol airship-gate-base.img > /dev/null; then
log Installing base image from "${BASE_IMAGE_URL}"

cd "${TEMP_DIR}"
curl -q -L -o base.img "${BASE_IMAGE_URL}"

{
virsh vol-create-as \
--pool "${VIRSH_POOL}" \
--name airship-gate-base.img \
--format qcow2 \
--capacity "${BASE_IMAGE_SIZE}" \
--prealloc-metadata
virsh vol-upload \
--vol airship-gate-base.img \
--file base.img \
--pool "${VIRSH_POOL}"
} &>> "${LOG_FILE}"
fi
}

netconfig_gen_mtu() {
MTU="$1"

set +e
IFS= read -r -d '' MTU_TMP <<'EOF'
mtu: ${MTU}
EOF
set -e

MTU="$MTU" envsubst <<< "$MTU_TMP" >> network-config
}

netconfig_gen_physical() {
IFACE_NAME="$1"
MTU="$2"

set +e
IFS= read -r -d '' PHYS_TMP <<'EOF'
- type: physical
name: ${IFACE_NAME}
EOF
set -e

IFACE_NAME="$IFACE_NAME" envsubst <<< "$PHYS_TMP" >> network-config

if [ ! -z "$MTU" ]
then
netconfig_gen_mtu "$MTU"
fi
}

netconfig_gen_subnet() {
IP_ADDR="$1"
IP_MASK="$2"
GW_ADDR="$3"

set +e
IFS= read -r -d '' SUBNET_TMP <<'EOF'
subnets:
- type: static
address: ${IP_ADDR}
netmask: ${IP_MASK}
EOF

IFS= read -r -d '' SUBNET_GW_TMP <<'EOF'
gateway: ${GW_ADDR}
EOF
set -e

IP_ADDR="$IP_ADDR" IP_MASK="$IP_MASK" envsubst <<< "$SUBNET_TMP" >> network-config

if [ ! -z "$GW_ADDR" ]
then
GW_ADDR="$GW_ADDR" envsubst <<< "$SUBNET_GW_TMP" >> network-config
fi

}

netconfig_gen_vlan() {
IFACE_NAME="$1"
VLAN_TAG="$2"
MTU="$3"

set +e
IFS= read -r -d '' VLAN_TMP <<'EOF'
- type: vlan
name: ${IFACE_NAME}.${VLAN_TAG}
vlan_link: ${IFACE_NAME}
vlan_id: ${VLAN_TAG}
EOF
set -e

IFACE_NAME="$IFACE_NAME" VLAN_TAG="$VLAN_TAG" envsubst <<< "$VLAN_TMP" >> network-config

if [ ! -z "$MTU" ]
then
netconfig_gen_mtu "$MTU"
fi

}

netconfig_gen_nameservers() {
NAMESERVERS="$1"

set +e
IFS= read -r -d '' NS_TMP <<'EOF'
- type: nameserver
address: [${DNS_SERVERS}]
EOF
set -e

DNS_SERVERS="$NAMESERVERS" envsubst <<< "$NS_TMP" >> network-config
}

netconfig_gen() {
NAME="$1"

IFS= cat << 'EOF' > network-config
version: 1
config:
EOF

# Generate physical interfaces
for iface in $(config_vm_iface_list "$NAME")
do
iface_network=$(config_vm_iface_network "$NAME" "$iface")
netconfig_gen_physical "$iface" "$(config_net_mtu "$iface_network")"

if [ "$(config_net_is_layer3 "$iface_network")" == "true" ]
then
iface_ip="$(config_vm_net_ip "$NAME" "$iface_network")"
netmask="$(cidr_to_netmask "$(config_net_cidr "$iface_network")")"
net_gw="$(config_net_gateway "$iface_network")"
netconfig_gen_subnet "$iface_ip" "$netmask" "$net_gw"
else
if [ ! -z "$(config_vm_iface_vlans "$NAME" "$iface")" ]
then
for vlan in $(config_vm_iface_vlans "$NAME" "$iface")
do
netconfig_gen_vlan "$iface" "$vlan" "$(config_net_mtu "$iface_network" "$vlan")"
if [ "$(config_net_is_layer3 "$iface_network" "$vlan")" == "true" ]