changeset 7:a7637c994964

cull_network.py, parse_load_balance.py, vmss_setup.sh x 2, vmss_create.sh: From kenneth, slight mods in some cases share.sh: Distribute files to workers wrun.sh, invoke.sh: From last year, slight mods
author Henry S. Thompson <ht@markup.co.uk>
date Sun, 30 Sep 2018 20:53:43 +0000
parents a3b30e707e83
children 5db6015689a2
files master/bin/cull_network.py master/bin/internal/invoke.sh master/bin/internal/parse_load_balance.py master/bin/internal/vmss_setup.sh master/bin/share.sh master/bin/vmss_create.sh master/bin/vmss_setup.sh master/bin/wrun.sh
diffstat 8 files changed, 242 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/cull_network.py	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,50 @@
+#!/usr/bin/env python2
+#Print commands to delete load balancers, NICs, and IPs not attached to any VM
+#Plenty of bugs here.  Assumes a stock setup with nothing fancy.
+import json
+import subprocess
+
+def query(command):
+  return json.load(subprocess.Popen(command, stdout=subprocess.PIPE).stdout)
+
+lb = query(["az", "network", "lb", "list", "-o", "json"])
+vmss = query(["az", "vmss", "list", "-o", "json"])
+
+in_use_lbs = []
+for m in vmss:
+  balancer = m["virtualMachineProfile"]["networkProfile"]["networkInterfaceConfigurations"][0]["ipConfigurations"][0]["loadBalancerBackendAddressPools"]
+  if balancer:
+    in_use_lbs.append('/'.join(balancer[0]["id"].split('/')[0:9]))
+ 
+allocated_lbs = ['/'.join(l["frontendIpConfigurations"][0]["id"].split('/')[0:9]) for l in lb]
+
+unused_lbs = set(allocated_lbs) - set(in_use_lbs)
+for l in unused_lbs:
+  split = l.split('/')
+  print "az network lb delete -g " +  split[4] +  " -n " + split[8]
+
+#TODO: exclude the lbs that are to be deleted
+ip_used_by_lb = [l["frontendIpConfigurations"][0]["publicIpAddress"]["id"] for l in lb]
+
+
+vm = query(["az", "vm", "list", "-o", "json"])
+in_use_nics = [v["networkProfile"]["networkInterfaces"][0]["id"] for v in vm]
+nics = query(["az", "network", "nic", "list", "-o", "json"])
+allocated_nics = [n["id"] for n in nics]
+for n in set(allocated_nics) - set(in_use_nics):
+  split = n.split('/')
+  print "az network nic delete -g " + split[4] + " -n " + split[8]
+
+#TODO: exclude the nics that are to be deleted
+ip_used_by_nic = [n["ipConfigurations"][0]["publicIpAddress"]["id"] for n in nics]
+in_use_ips = ip_used_by_lb + ip_used_by_nic
+
+allocated_ips = [i["id"] for i in query(["az", "network", "public-ip", "list", "-o", "json"])]
+for i in set(allocated_ips) - set(in_use_ips):
+  split = i.split('/')
+  print "az network public-ip delete -g " + split[4] + " -n " + split[8]
+
+nsg_all = [n['id'] for n in query(["az", "network", "nsg", "list", "-o", "json"])]
+for i in set(nsg_all) - set([n['networkSecurityGroup']['id'] for n in nics]):
+  split = i.split('/')
+  print "az network nsg delete -g " + split[4] + " -n " + split[8]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/internal/invoke.sh	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Helper for ../wrun, q.v.
+# Usage: invoke.sh [-x] me cmd ifile id port ip [args...]
+#  Runs 
+#   cmd [id] args...
+#  via ssh to ip:port
+#  If ifile is not /dev/null, feed in as stdin
+#  Unless -x, worker id is passed as first arg
+if [ "$1" = "-x" ]
+then
+ shift
+ id=
+ me=
+else
+ me=$1
+ id=$4
+fi
+cmd="$2"
+ifile=$3
+port=$5
+ip=$6
+shift 6
+echo "#$(date)#$cmd#$ifile#$id#$port#$ip#$@#" 1>&2
+if [ "$ifile" != "/dev/null" ]
+then
+  echo "# from $ifile" 1>&2
+  scp -P $port $ifile $ip:ifile.txt && \
+  ssh -t -p $port $ip "nohup $cmd $id $me ""$@"" > nohup.cc"
+else
+  ssh -p $port $ip "$cmd $id $me ""$@"
+fi || echo ssh failed, status=$? 1>&2
+echo "#$(date)#$id#" 1>&2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/internal/parse_load_balance.py	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+#Parses the output of az network lb show --resource-group $resource --name ${name}LB -o json
+#Prints a table of instanceId and port.
+import sys
+import json
+if len(sys.argv) == 2:
+  ip = " " + sys.argv[1]
+else:
+  ip = ""
+for m in json.load(sys.stdin)['inboundNatRules']:
+  print(m['name'].split('.')[-1] + ' ' + str(m['frontendPort']) + ip)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/internal/vmss_setup.sh	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,15 @@
+#!/bin/bash
+set -e
+#Arguments are instance port host --resource-group $resource --name $name
+instance=$1
+port=$2
+host=$3
+shift 3
+#Accept host keys :-(
+if ! ssh-keyscan -p $port $host >>~/.ssh/known_hosts 2>/dev/null; then
+  echo $host:$port does not seem to be up, might be overprovisioned 1>&2
+  exit 1
+fi
+#Copy azure credentials over and make delete/deallocate commands
+(echo $@ --instance-ids $instance; cd ~; tar c .azure) |ssh -o ConnectTimeout=5 -p $port $host 'read a; echo -e '\''#!/bin/bash\n'\''az vmss deallocate $a >deallocate && echo -e '\''#!/bin/bash\n'\''az vmss delete-instances $a >delete && chmod +x deallocate delete && tar x && sudo mv deallocate delete /bin'
+echo ssh -p $port $host "  #id $instance"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/share.sh	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,33 @@
+#!/bin/bash
+# Share file(s) with all workers in a scale set
+if [ $# -lt 2 ]; then
+  cat 1>&2 <<EOF
+Usage: $0 SSname [-d dir] files...
+Where name is the name of a VM scale set.
+
+Distribute files to every machine in the SS named SSname
+  if -d, into dir, otherwise home directory
+ by doing as it were
+  tar -czf - files | ssh machine "[cd $dir &&] tar -xzf -"
+EOF
+ exit 1
+fi
+group=cc
+name=$1
+shift
+if [ "$1" = "-d" ]
+then
+ dodir="cd $2 && "
+ shift 2
+else
+ dodir=""
+fi
+cmd="${dodir}tar -xzf -"
+np=$(az vmss get-instance-view -g $group -n $name | jq '.virtualMachine.statusesSummary | .[0].count')
+echo "|$name|$cmd|$@|$np|" 1>&2
+az vmss list-instance-connection-info -g $group -n $name | tr -s ',": ' '\t' | \
+    tail -n +2 | head -$np |cut -f 3-5 | tee /dev/stderr |\
+  while read id ip port
+    do tar -czf - "$@" | "$(dirname "$0")"/internal/invoke.sh -x "" bash /dev/null "" $port $ip -c \""$cmd"\"
+  done
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/vmss_create.sh	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -e
+if ([ "$1" != "--resource-group" ] && [ "$1" != "-g" ]) || ([ "$3" != "--name" ] && [ "$3" != "-n" ]); then
+  cat 1>&2 <<EOF
+Usage: $0 --resource-group group --name name ...
+All arguments are forwarded to az vmss create, so see that for help.
+However, you must have --resource-group group --name name at the beginning.
+That way they can be passed to $(dirname "$0")/vmss_setup.sh
+EOF
+fi
+az vmss create "$@"
+echo created, setting up...
+"$(dirname "$0")"/vmss_setup.sh $1 $2 $3 $4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/vmss_setup.sh	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -e -o pipefail
+if [ $# != 4 ]; then
+  cat 1>&2 <<EOF
+Usage: $0 --resource-group group --name name
+Where name is the name of a VM scale set."
+
+This script:
+1. Insecurely gets SSH host keys and adds them to your ~/.ssh/known_hosts .
+2. Copies your ~/.azure to the machines so they have command line access.
+3. Installs delete and deallocate commands so you can stop them from inside.
+4. Prints SSH connection information.
+EOF
+  exit 1
+fi
+ip=$(az vmss list-instance-connection-info $@ --output tsv | head -n 1 | cut -d : -f 1)
+echo IP address $ip 1>&2
+az network lb show $1 $2 $3 ${4}LB --o json | \
+  "$(dirname "$0")"/internal/parse_load_balance.py $ip | \
+  parallel --gnu --colsep ' ' "$(dirname "$0")"/internal/vmss_setup.sh {} $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/bin/wrun.sh	Sun Sep 30 20:53:43 2018 +0000
@@ -0,0 +1,68 @@
+#!/bin/bash
+set -e -o pipefail
+if [ $# -lt 2 ]; then
+  cat 1>&2 <<EOF
+Usage: $0 SSname cmd [-x] [-np n] [-f file] [...args]
+Where name is the name of a VM scale set.
+
+Runs cmd on every machine in a scale set,
+ passing args and, 
+  if -f, one line from file per worker
+  unless -x, worker id
+ by doing as it were
+  [ echo line-from-file |] ssh machine "$cmd [id] "$args"" 
+EOF
+  exit 1
+fi
+group=cc
+name=$1
+cmd="$2"
+shift 2
+if [ "$1" = "-x" ]
+then
+ id=-x
+ shift
+else
+ id=
+fi
+if [ "$1" = "-np" ]
+then
+ np=$2
+ shift
+ shift
+else
+ np=$(az vmss get-instance-view -g $group -n $name | jq '.virtualMachine.statusesSummary | .[0].count')
+fi
+echo "|$name|$cmd|$@|$np|" 1>&2
+me=$(az vm list-ip-addresses -g cc -n Deb1 | jq -r '.[0].virtualMachine.network.publicIpAddresses|.[0].ipAddress')
+echo me=$me 1>&2
+if [ "$1" = "-f" ]
+then
+ file=$2
+ shift 2
+ tfile=$(mktemp)
+ split -n r/$np -u --filter="cat > $tfile\$\$" $file &
+ # Wait for fifos to be built
+ while [ $np -gt $(ls ${tfile}* | wc -l) ]; do echo -n . 1>&2; sleep 1; done
+ echo 1>&2
+ wc -l ${tfile}* 1>&2
+else
+ file=/dev/null
+fi
+echo "|$np|$file|$tfile|" 1>&2
+paste -d ' ' \
+ <(if [ "$file" = "/dev/null" ]
+   then yes /dev/null|head -$np
+   else
+     echo ${tfile}?* | tr ' ' '\012' # the name of the file of
+                                    # input lines for each worker
+   fi
+  ) \
+ <(az vmss list-instance-connection-info -g $group -n $name | tr -s ',": ' '\t' | \
+    tail -n +2 | head -$np |cut -f 3-5 | \
+  while read i ip port
+   do
+    echo $i $port $ip
+   done) | \
+ parallel --gnu --colsep ' ' -j $np "$(dirname "$0")"/internal/invoke.sh $id $me "$cmd" {} "$@" || { r=$? ; echo parallel exited with status=$r 1>&2;}
+if [ "${tfile}" ]; then rm ${tfile}*; fi