Add the renumber remctl. mitchb-renumber-remctl
authorMitchell E Berger <mitchb@mit.edu>
Fri, 18 May 2018 18:40:27 +0000 (14:40 -0400)
committerMitchell E Berger <mitchb@mit.edu>
Fri, 18 May 2018 18:40:27 +0000 (14:40 -0400)
NOTE: The code currently performs as expected, but the functionality
has a still-undiagnosed problem, so this can't be used yet.

debian/changelog
host/usr/sbin/invirt-vmcontrol
server/usr/sbin/invirt-remote-control

index ba2843f..7506e4c 100644 (file)
@@ -1,6 +1,7 @@
 invirt-remote (0.4.21) precise; urgency=low
 
   * Add the long-awaited arp remctl
+  * Add the renumber remctl
 
  -- Mitchell Berger <mitchb@mit.edu>  Fri, 18 May 2018 04:08:00 -0400
 
index eaac5ac..43b3449 100755 (executable)
@@ -57,6 +57,40 @@ case "$ACTION" in
            fi
        done
        ;;
+    renumber)
+       shift; shift;
+       curip="$1"
+        curgw="$2"
+        newip="$3"
+        newgw="$4"
+        xs_prefix="/local/domain/0/backend/vif"
+        domid=`xm domid $MACHINE`
+        renumbered=
+        for vif in `xenstore-list $xs_prefix/$domid`; do
+            ip=`xenstore-read $xs_prefix/$domid/$vif/ip`
+            if [ $ip == $curip ]; then
+                script=`xenstore-read $xs_prefix/$domid/$vif/script`
+                # If the domain is rebooted without a full shutdown, the
+                # invirt-database script doesn't run to fill in arguments for
+                # vif-invirtroute, so we need to rewrite it as if it had
+                # been booted with the IP parameters swapped.
+                script=`echo $script | sed -ne "s/\(.* netdev=[^ ]*\).*/\1 gateway=$newgw other_ip=$curip other_gateway=$curgw/p"`
+                # The sed above will only print something if we matched.  If
+                # the content of the script key wasn't as we expected, don't
+                # mess with it.
+                if [ -n "$script" ]; then
+                    xenstore-write $xs_prefix/$domid/$vif/script "$script"
+                    echo "'$ORIGMACHINE' has been configured to renumber $curip to $newip if it is rebooted without being powered off."
+                    renumbered=yes
+                fi
+            fi
+        done
+        if [ -n "$renumbered" ]; then
+            echo "XVM's DNS server has been updated with your new address information."
+            echo "You MUST reboot your VM immediately in order for it to respond properly"
+            echo "to the old and new addresses."
+        fi
+        ;;
     *)
         echo "ERROR: Invalid Command"
         exit 34
index b205760..68335ca 100755 (executable)
@@ -4,6 +4,7 @@ Sends remctl commands about a running VM to the host it's running on.
 """
 
 from subprocess import PIPE, Popen, call
+from invirt.database import connect, session, models
 import sys
 import yaml
 
@@ -14,6 +15,40 @@ def main(argv):
     machine_name = argv[1]
     command = argv[2]
 
+    renumber = []
+    if command == 'renumber':
+        connect()
+        machine = models.Machine.query.filter_by(name=machine_name).one()
+        for n in machine.nics:
+            if n.other_action and n.other_action == 'renumber_dhcp':
+                renumber.append((n.ip, n.gateway, n.other_ip, n.other_gateway))
+                print "IP %s is set to be renumbered to %s the next time '%s' is powered on." % (n.ip, n.other_ip, machine_name)
+                # We have to switch this now; we can't simply change to the
+                # 'renumber' state, because that will only work if the machine
+                # is turned off fully and then turned back on.  A reboot will
+                # only run vif-invirtroute, not invirt-database, so we need
+                # to edit the vif-invirtroute script parameters in xenstore
+                # if the machine is on.  That's why we have just saved a
+                # tuple of parameters, which we will pass to a host-based
+                # remctl if the machine is actually on (further below in this
+                # script).  As a consequence of us swapping this now, DNS
+                # will change when the user runs this remctl, so if the machine
+                # is on, they will need to reboot it promptly to begin
+                # responding to the new address.  We don't know if it's on
+                # yet, or if we'll succeed in editing the script.  The
+                # remctl from the host will warn them to reboot immediately
+                # if successful.
+                (n.ip, n.netmask, n.gateway,
+                 n.other_ip, n.other_netmask, n.other_gateway, n.other_action) = (
+                 n.other_ip, n.other_netmask, n.other_gateway,
+                 n.ip, n.netmask, n.gateway, 'dnat')
+                session.add(n)
+                session.flush()
+            else:
+                print 'IP %s is not currently slated to be renumbered.' % (n.ip)
+        if not renumber:
+            print >>sys.stderr, "machine '%s' is not currently slated to be renumbered" % (machine_name)
+            return 1
     p = Popen(['/usr/sbin/invirt-remote-proxy-web', 'listvms'], stdout=PIPE)
     output = p.communicate()[0]
     if p.returncode != 0:
@@ -22,10 +57,32 @@ def main(argv):
     vms = yaml.load(output, yaml.CSafeLoader)
 
     if machine_name not in vms:
+        if renumber:
+            # It's okay for the machine not to be on with the 'renumber'
+            # remctl.  There's a DB component of the work (completed above)
+            # and an adjustment to make to the machine if it happens to be on.
+            return 0
         print >>sys.stderr, "machine '%s' is not on" % machine_name
         return 1
     host = vms[machine_name]['host']
 
+    if renumber:
+        # We need to pass parameters from the database that the remctl script
+        # on the host where the VM is running won't easily have access to, and
+        # we may need to do this for multiple NICs, so we'll do this in our
+        # own loop for the 'renumber' remctl, separate from the normal remote
+        # control code path.
+        for nicparams in renumber:
+            p = Popen(['remctl', host, 'remote', 'control'] + argv[1:] + list(nicparams),
+                      stdout=PIPE, stderr=PIPE)
+            (out, err) = p.communicate()
+            if p.returncode == 1:
+                print >>sys.stderr, "machine '%s' is not on" % machine_name
+                return 1
+            sys.stderr.write(err)
+            sys.stdout.write(out)
+        return 0
+
     p = Popen(['remctl', host, 'remote', 'control'] + argv[1:],
               stdout=PIPE, stderr=PIPE)
     (out, err) = p.communicate()