|
#!/usr/bin/env bash |
|
|
|
export OS_CLOUD=BIO180006_IU |
|
|
|
cd /tmp |
|
|
|
git clone https://github.com/MorphoCloud/MorphoCloudWorkflow.git |
|
cd MorphoCloudWorkflow |
|
|
|
INSTANCE_NAME="instance-9999" |
|
VOLUME_NAME="My-Data-9999" |
|
INSTANCE_FLAVOR="g3.xl" |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Check if instance has a floating IP |
|
|
|
# Get instance floating IP |
|
instance_ip=$( |
|
openstack server show $INSTANCE_NAME -c addresses -f json | \ |
|
jq -r '.addresses.auto_allocated_network | if length > 1 then .[1] else "" end' |
|
) |
|
echo "instance_ip [$instance_ip]" |
|
|
|
# Get instance internal IP (also called fixed IP) |
|
instance_internal_ip=$( |
|
openstack server show $INSTANCE_NAME -c addresses -f json | \ |
|
jq -r '.addresses.auto_allocated_network | if length > 0 then .[0] else "" end' |
|
) |
|
echo "instance_internal_ip [$instance_internal_ip]" |
|
|
|
# Determine if instance already has a floating IP assigned |
|
if [[ -n "$instance_ip" ]]; then |
|
has_ip="true" |
|
else |
|
has_ip="false" |
|
fi |
|
echo "has_ip [$has_ip]" |
|
|
|
# Check if an unassigned floating IP matches instance IP |
|
has_ip_assigned="false" |
|
if [[ "$has_ip" == "true" ]]; then |
|
json_output=$(openstack floating ip list -f json | jq \ |
|
--arg fixed_ip "$instance_internal_ip" \ |
|
--arg ip "$instance_ip" \ |
|
'map(select(.Port != null and ."Fixed IP Address" == $fixed_ip and ."Floating IP Address" == $ip))') |
|
floating_ip_address=$(echo "$json_output" | jq -r 'if length > 0 then .[0]["Floating IP Address"] else "" end') |
|
|
|
if [[ -n "$floating_ip_address" ]]; then |
|
has_ip_assigned="true" |
|
fi |
|
fi |
|
|
|
# If previously assigned IP is not available anymore, discard it. |
|
if [[ "$has_ip_assigned" == "false" ]]; then |
|
instance_ip="" |
|
fi |
|
|
|
echo "instance_ip [$instance_ip]" |
|
echo "has_ip_assigned [$has_ip_assigned]" |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Retrieve or create floating IP |
|
|
|
floating_ip_address="" |
|
floating_ip_uuid="" |
|
if [[ $has_ip_assigned == "false" ]]; then |
|
|
|
preferred_ip_address=$instance_ip |
|
echo "preferred_ip_address [$preferred_ip_address]" |
|
|
|
# Retrieve unassigned floating IP |
|
json_output=$(openstack floating ip list -f json | jq 'map(select(.Port == null))') |
|
echo "Filtered JSON Output:" |
|
echo "$json_output" | jq |
|
|
|
if [[ -n "$preferred_ip_address" ]]; then |
|
floating_ip=$(echo "$json_output" | jq -r --arg ip "$preferred_ip_address" \ |
|
'map(select(."Floating IP Address" == $ip)) | if length > 0 then .[0] else null end') |
|
else |
|
floating_ip=$(echo "$json_output" | jq -r 'if length > 0 then .[0] else null end') |
|
fi |
|
|
|
floating_ip_address=$(echo "$floating_ip" | jq -r '."Floating IP Address" // empty') |
|
floating_ip_uuid=$(echo "$floating_ip" | jq -r '."ID" // empty') |
|
|
|
echo "floating_ip_address [$floating_ip_address]" |
|
echo "floating_ip_uuid [$floating_ip_uuid]" |
|
fi |
|
|
|
if [[ -z "$floating_ip_address" ]]; then |
|
json_output=$(openstack floating ip create public -f json) |
|
echo "Floating IP Creation Output:" |
|
echo "$json_output" | jq |
|
|
|
floating_ip_uuid=$( |
|
echo $json_output | |
|
jq -r ".id" |
|
) |
|
echo "floating_ip_uuid [$floating_ip_uuid]" |
|
|
|
floating_ip_address=$( |
|
echo $json_output | |
|
jq -r ".floating_ip_address" |
|
) |
|
echo "floating_ip_address [$floating_ip_address]" |
|
fi |
|
|
|
# ---------------------------------------------------------------------------- |
|
openstack volume create --size 100 "$VOLUME_NAME" |
|
|
|
# ---------------------------------------------------------------------------- |
|
# The MorphCloud workflow uses a unique UUID generated with: |
|
# python3 -c "import uuid; print(uuid.uuid4())" |
|
exoClientUuid=b7b75870-ecb5-4aca-ae73-49474a7f531e |
|
|
|
# See "currentExoServerVersion" in exosphere/src/Types/Server.elm |
|
exoServerVersion=5 |
|
|
|
FLOATING_IP_UUID=$floating_ip_uuid |
|
|
|
echo "INSTANCE_FLAVOR [$INSTANCE_FLAVOR]" |
|
echo "FLOATING_IP_UUID [$FLOATING_IP_UUID]" |
|
echo "exoServerVersion [$exoServerVersion]" |
|
echo "exoClientUuid [$exoClientUuid]" |
|
|
|
openstack server create "$INSTANCE_NAME" \ |
|
--nic net-id="auto_allocated_network" \ |
|
--security-group "exosphere" \ |
|
--flavor $INSTANCE_FLAVOR \ |
|
--image "Featured-Ubuntu22" \ |
|
--key-name "jcfr" \ |
|
--property "exoGuac={\"v\":1,\"ssh\":true,\"vnc\":true}" \ |
|
--property "exoClientUuid=$exoClientUuid" \ |
|
--property "exoServerVersion=$exoServerVersion" \ |
|
--property "[email protected]" \ |
|
--property "exoFloatingIpOption=useFloatingIp" \ |
|
--property "exoFloatingIpReuseOption=$FLOATING_IP_UUID" \ |
|
--property "exoSetup={\"status\":\"waiting\",\"epoch\":null}" \ |
|
--user-data ./cloud-config \ |
|
--wait \ |
|
--column created \ |
|
--column flavor \ |
|
--column image \ |
|
--column name \ |
|
--column status |
|
|
|
# Associate floating IP with created instance |
|
has_ip=$( |
|
openstack server show $INSTANCE_NAME -c addresses -f json | \ |
|
jq -r '.addresses.auto_allocated_network[1] != null' |
|
) |
|
echo "has_ip [$has_ip]" |
|
|
|
if [[ $has_ip != "true" ]]; then |
|
openstack server add floating ip "$INSTANCE_NAME" $FLOATING_IP_ADDRESS |
|
fi |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Poll instance setup status |
|
|
|
echo Polling "$INSTANCE_NAME" setup status |
|
|
|
max_wait_time=1200 # Maximum wait time in seconds (1200s -> 20mins) |
|
wait_interval=5 # Interval between status checks in seconds |
|
total_wait_time=0 |
|
|
|
while [ $total_wait_time -lt $max_wait_time ]; do |
|
|
|
status=$(openstack console log show $INSTANCE_NAME | \ |
|
grep "^\{\"status\":\"" | \ |
|
tail -1 | \ |
|
jq -r '.status // "pending"') |
|
|
|
echo -n "setup status [$status]. " |
|
|
|
if [[ "$status" == "complete" ]]; then |
|
echo "Exiting loop." |
|
break |
|
else |
|
echo "Waiting for completion..." |
|
sleep $wait_interval |
|
total_wait_time=$((total_wait_time + wait_interval)) |
|
fi |
|
done |
|
|
|
if [ $total_wait_time -ge $max_wait_time ]; then |
|
echo "::error ::Maximum wait time ($max_wait_time seconds) exceeded." |
|
fi |
|
|
|
# Explicitly set status to "complete" |
|
exoSetup='{"status":"complete", "epoch": '$(date '+%s')'000}' |
|
|
|
openstack server set \ |
|
--property "exoSetup=$exoSetup" \ |
|
$INSTANCE_NAME |
|
|
|
echo "status [$status]" |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Rename MyData directory to MyData-tmp |
|
|
|
INSTANCE_IP=$FLOATING_IP_ADDRESS |
|
|
|
ssh \ |
|
-o StrictHostKeyChecking=no \ |
|
-o UserKnownHostsFile=/dev/null \ |
|
-o LogLevel=ERROR \ |
|
exouser@$INSTANCE_IP \ |
|
'mv /media/volume/MyData /media/volume/MyData-tmp' |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Attach volume |
|
|
|
instance_id=$(openstack server list -f json | \ |
|
jq \ |
|
--arg instance_name "$INSTANCE_NAME" \ |
|
-c '.[] | select(.Name == $instance_name)' | \ |
|
jq -r '.ID' | tail -1) |
|
echo "instance_id [$instance_id]" |
|
|
|
volume_id=$(openstack volume list -f json | \ |
|
jq \ |
|
--arg volume_name "$VOLUME_NAME" \ |
|
-c '.[] | select(.Name == $volume_name)' | \ |
|
jq -r '.ID' | tail -1) |
|
echo "volume_id [$volume_id]" |
|
|
|
openstack server set \ |
|
--property "exoVolumes::$volume_id={\"name\":\"MyData\"}" \ |
|
$instance_id |
|
|
|
openstack server add volume \ |
|
$instance_id \ |
|
$volume_id |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Copy installed files into attached volume |
|
|
|
function check_mountpoint_ready { |
|
ssh \ |
|
-o StrictHostKeyChecking=no \ |
|
-o UserKnownHostsFile=/dev/null \ |
|
-o LogLevel=ERROR \ |
|
exouser@$INSTANCE_IP \ |
|
'mountpoint -q /media/volume/MyData' |
|
} |
|
set +e |
|
max_attempts=5 |
|
for attempt in $(seq 1 $max_attempts); do |
|
echo "Checking if mount point is ready ($attempt/$max_attempts)" |
|
check_mountpoint_ready |
|
if check_mountpoint_ready; then |
|
echo "Mount point /media/volume/MyData is ready." |
|
break |
|
else |
|
echo "Mount point not ready. Retrying in 5 seconds..." |
|
sleep 5 |
|
fi |
|
done |
|
if ! check_mountpoint_ready; then |
|
echo "::error ::Mount point /media/volume/MyData not ready after $((max_attempts * 5)) seconds." |
|
exit 1 |
|
fi |
|
set -e |
|
ssh \ |
|
-o StrictHostKeyChecking=no \ |
|
-o UserKnownHostsFile=/dev/null \ |
|
-o LogLevel=ERROR \ |
|
exouser@$INSTANCE_IP \ |
|
'[ ! -d /media/volume/MyData/Slicer ] && mv /media/volume/MyData-tmp/Slicer /media/volume/MyData/Slicer && rmdir /media/volume/MyData-tmp || rm -rf /media/volume/MyData-tmp' |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Create Renviron file |
|
ssh \ |
|
-o StrictHostKeyChecking=no \ |
|
-o UserKnownHostsFile=/dev/null \ |
|
-o LogLevel=ERROR \ |
|
exouser@$INSTANCE_IP \ |
|
'echo -e "R_LIBS_SITE=/media/share/MorphoCloudCephShare/R/x86_64-pc-linux-gnu-library/4.4/\nR_LIBS_USER=/media/volume/MyData/R/x86_64-pc-linux-gnu-library/4.4/" > /home/exouser/.Renviron && mkdir -p /media/volume/MyData/R/x86_64-pc-linux-gnu-library/4.4/' |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Reboot instance |
|
|
|
set +e |
|
|
|
ssh \ |
|
-o StrictHostKeyChecking=no \ |
|
-o UserKnownHostsFile=/dev/null \ |
|
-o LogLevel=ERROR \ |
|
exouser@$INSTANCE_IP \ |
|
'sudo shutdown -r now' |
|
|
|
sleep 10 |
|
function check_instance_ready { |
|
ssh \ |
|
-o StrictHostKeyChecking=no \ |
|
-o UserKnownHostsFile=/dev/null \ |
|
-o LogLevel=ERROR \ |
|
exouser@$INSTANCE_IP \ |
|
'true' |
|
} |
|
|
|
max_attempts=3 |
|
instance_ready=false |
|
for attempt in $(seq 1 $max_attempts); do |
|
echo "Checking if instance is ready ($attempt/$max_attempts)" |
|
if check_instance_ready; then |
|
instance_ready=true |
|
echo "Instance '$INSTANCE_NAME' is ready." |
|
break |
|
else |
|
echo "Instance '$INSTANCE_NAME' is not ready. Retrying in 5 seconds..." |
|
sleep 10 |
|
fi |
|
done |
|
if ! $instance_ready; then |
|
echo "::error ::Instance '$INSTANCE_NAME' is not ready after $max_attempts attempts to connect." |
|
fi |
|
set -e |
|
|
|
# ---------------------------------------------------------------------------- |
|
# Explicitly set status to complete |
|
exoSetup='{"status":"complete", "epoch": '$(date '+%s')'000}' |
|
|
|
openstack server set \ |
|
--property "exoSetup=$exoSetup" \ |
|
$INSTANCE_NAME |