#!/usr/bin/env python #code taken from https://www.redhat.com/en/blog/container-migration-around-world and partially modified import socket import sys from thread import * import json import os import distutils.util import subprocess #import dns.update #import dns.query #import dns.tsigkeyring def migrate_server(app_using_tcp): HOST = '' # Symbolic name meaning all available interfaces PORT = 18863 # Arbitrary non-privileged port 8888 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print 'Socket created' #Bind socket to local host and port try: s.bind((HOST, PORT)) except socket.error as msg: print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1] sys.exit() print 'Socket bind complete' #Start listening on socket s.listen(10) print 'Socket now listening' #Function for handling connections. This will be used to create threads def clientthread(conn, addr, app_using_tcp): #Sending message to connected client #infinite loop so that function do not terminate and thread do not end. while True: reply = "" #Receiving from client data = conn.recv(1024) if not data: break if data == 'exit': break try: msg = json.loads(data) if 'restore' in msg: os.system('criu -V') try: lazy = bool(distutils.util.strtobool(msg['restore']['lazy'])) except: lazy = False old_cwd = os.getcwd() os.chdir(msg['restore']['path']) cmd = 'time -p runc restore --console-socket ' + msg['restore']['path'] cmd += '/console.sock -d --image-path ' + msg['restore']['image_path'] cmd += ' --work-path ' + msg['restore']['image_path'] if lazy: cmd += ' --lazy-pages' cmd += ' ' + msg['restore']['name'] print "Running " + cmd p = subprocess.Popen(cmd, shell=True) if lazy: cmd = "criu lazy-pages --page-server --address " + addr cmd += " --port 27 -vv -D " cmd += msg['restore']['image_path'] cmd += " -W " cmd += msg['restore']['image_path'] print "Running lazy-pages server: " + cmd lp = subprocess.Popen(cmd, shell=True) ret = p.wait() if ret == 0: reply = "runc restored %s successfully" % msg['restore']['name'] else: reply = "runc failed(%d)" % ret os.chdir(old_cwd) else: print "Unkown request : " + msg except: continue print reply #update the DNS server with the new IP address of the container #this is done only in case of app_using_tcp = True #if QUIC is used instead, this is not done #if app_using_tcp: #print("Starting update of DNS") #os.system("python3 /home/oem/master/dnsupdater.py") #keyring = dns.tsigkeyring.from_text({ # 'mycompanion.com.': 'OlOAxkefYKheCw8+rm92jw==' #}) #update = dns.update.Update('mycompanion.com', keyring=keyring, keyalgorithm='hmac-md5.sig-alg.reg.int') #update.replace('myservice', 5, 'A', '192.168.3.1') #response = dns.query.tcp(update, '192.168.1.53') #print(str(response)) conn.sendall(reply) #came out of loop conn.close() #now keep talking with the client while 1: #wait to accept a connection - blocking call conn, addr = s.accept() print 'Connected with ' + addr[0] + ':' + str(addr[1]) #start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function. start_new_thread(clientthread ,(conn, str(addr[0]), app_using_tcp)) s.close() if __name__ == '__main__': #app_using_tcp is a boolean that indicates whether (True) or not (False) the client-server application is using TCP at transport layer #this is used to decide if DNS server needs to be updated with the new IP address of the container app_using_tcp = sys.argv[1] migrate_server(app_using_tcp)