From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail04.ics.ntt-tx.co.jp (mail05.ics.ntt-tx.co.jp [210.232.35.69]) by dpdk.org (Postfix) with ESMTP id 203E08E01 for ; Thu, 28 Dec 2017 05:56:11 +0100 (CET) Received: from gwchk03.silk.ntt-tx.co.jp (gwchk03.silk.ntt-tx.co.jp [10.107.0.111]) by mail04.ics.ntt-tx.co.jp (unknown) with ESMTP id vBS4uARi025206 for unknown; Thu, 28 Dec 2017 13:56:10 +0900 Received: (from root@localhost) by gwchk03.silk.ntt-tx.co.jp (unknown) id vBS4u82Z027450 for unknown; Thu, 28 Dec 2017 13:56:08 +0900 Received: from gwchk.silk.ntt-tx.co.jp [10.107.0.110] by gwchk03.silk.ntt-tx.co.jp with ESMTP id PAA27444; Thu, 28 Dec 2017 13:56:08 +0900 Received: from imss03.silk.ntt-tx.co.jp (localhost [127.0.0.1]) by imss03.silk.ntt-tx.co.jp (unknown) with ESMTP id vBS4u8aa011171 for unknown; Thu, 28 Dec 2017 13:56:08 +0900 Received: from mgate01.silk.ntt-tx.co.jp (smtp02.silk.ntt-tx.co.jp [10.107.0.37]) by imss03.silk.ntt-tx.co.jp (unknown) with ESMTP id vBS4u8Gp011168 for unknown; Thu, 28 Dec 2017 13:56:08 +0900 Message-Id: <201712280456.vBS4u8Gp011168@imss03.silk.ntt-tx.co.jp> Received: from localhost by mgate01.silk.ntt-tx.co.jp (unknown) id vBS4u4bL025622 ; Thu, 28 Dec 2017 13:56:07 +0900 From: x-fn-spp@sl.ntt-tx.co.jp To: spp@dpdk.org Date: Thu, 28 Dec 2017 13:55:54 +0900 X-Mailer: git-send-email 1.9.1 In-Reply-To: <4aae78ff-3b6c-cdfe-a8b7-24ec08b73935@lab.ntt.co.jp> References: <4aae78ff-3b6c-cdfe-a8b7-24ec08b73935@lab.ntt.co.jp> X-TM-AS-MML: No Subject: [spp] [PATCH 47/57] spp_vf: add spp_vf.py instead of spp.py X-BeenThere: spp@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Soft Patch Panel List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 28 Dec 2017 04:56:13 -0000 From: Hiroyuki Nakamura Add spp_vf.py as a modified version of spp.py. Signed-off-by: Kentaro Watanabe Signed-off-by: Yasufum Ogawa --- src/spp_vf.py | 443 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100755 src/spp_vf.py diff --git a/src/spp_vf.py b/src/spp_vf.py new file mode 100755 index 0000000..40310db --- /dev/null +++ b/src/spp_vf.py @@ -0,0 +1,443 @@ +#!/usr/bin/python +"""Soft Patch Panel""" + +from __future__ import print_function +from Queue import Queue +from thread import start_new_thread +from threading import Thread +import cmd +import getopt +import select +import socket +import sys + +import json + +class GrowingList(list): + """GrowingList""" + + def __setitem__(self, index, value): + if index >= len(self): + self.extend([None]*(index + 1 - len(self))) + list.__setitem__(self, index, value) + +MAX_SECONDARY = 16 + +# init +PRIMARY = '' +SECONDARY_LIST = [] +SECONDARY_COUNT = 0 + +#init primary comm channel +MAIN2PRIMARY = Queue() +PRIMARY2MAIN = Queue() + +#init secondary comm channel list +MAIN2SEC = GrowingList() +SEC2MAIN = GrowingList() + +def connectionthread(name, client_id, conn, m2s, s2m): + """Manage secondary process connections""" + + cmd_str = 'hello' + + #infinite loop so that function do not terminate and thread do not end. + while True: + try: + _, _, _ = select.select([conn,], [conn,], [], 5) + except select.error: + break + + #Sending message to connected secondary + try: + cmd_str = m2s.get(True) + conn.send(cmd_str) #send only takes string + except KeyError: + break + except Exception, excep: + print (str(excep)) + break + + #Receiving from secondary + try: + data = conn.recv(1024) # 1024 stands for bytes of data to be received + if data: + s2m.put("recv:" + str(conn.fileno()) + ":" + "{" + data + "}") + else: + s2m.put("closing:" + str(conn)) + break + except Exception, excep: + print (str(excep)) + break + + SECONDARY_LIST.remove(client_id) + conn.close() + +def getclientid(conn): + """Get client_id from client""" + + try: + conn.send("_get_client_id") + #conn.send("{\"commands\":[{\"command\":\"process\"}]}") + except KeyError: + return -1 + + data = conn.recv(1024) + if data == None: + return -1 + + #client_id = int(data.strip('\0')) + json_dict = json.loads(data) + client_id = int(json_dict['client_id']) + + if client_id < 0 or client_id > MAX_SECONDARY: + return -1 + + print ("secondary id %d" % client_id) + return client_id + + found = 0 + for i in SECONDARY_LIST: + if client_id == i: + found = 1 + break + + if found == 0: + return client_id + + # client_id in use, find a free one + free_client_id = -1 + for i in range(MAX_SECONDARY): + found = -1 + for j in SECONDARY_LIST: + if i == j: + found = i + break + if found == -1: + free_client_id = i + break + + if free_client_id < 0: + return -1 + + conn.send("_set_client_id %u" % free_client_id) + data = conn.recv(1024) + + return free_client_id + +def acceptthread(sock, main2sec, sec2main): + """Listen for secondary processes""" + + global SECONDARY_COUNT + + try: + while True: + #Accepting incoming connections + conn, _ = sock.accept() + + client_id = getclientid(conn) + if client_id < 0: + break + + #Creating new thread. + #Calling secondarythread function for this function and passing + #conn as argument. + + SECONDARY_LIST.append(client_id) + main2sec[client_id] = Queue() + sec2main[client_id] = Queue() + start_new_thread(connectionthread, + ('secondary', client_id, conn, + main2sec[client_id], + sec2main[client_id], )) + SECONDARY_COUNT += 1 + except Exception, excep: + print (str(excep)) + sock.close() + +def command_primary(command): + """Send command to primary process""" + + if PRIMARY: + MAIN2PRIMARY.put(command) + print (PRIMARY2MAIN.get(True)) + else: + print ("primary not started") + +def command_secondary(sec_id, command): + """Send command to secondary process with sec_id""" + + if sec_id in SECONDARY_LIST: + MAIN2SEC[sec_id].put(command) + print (SEC2MAIN[sec_id].get(True)) + else: + print ("secondary id %d not exist" % sec_id) + +def print_status(): + """Display information about connected clients""" + + print ("Soft Patch Panel Status :") + print ("primary: %d" % PRIMARY) + print ("secondary count: %d" % len(SECONDARY_LIST)) + for i in SECONDARY_LIST: + print ("Connected secondary id: %d" % i) + +def primarythread(sock, main2primary, primary2main): + """Manage primary process connection""" + + global PRIMARY + cmd_str = '' + + while True: + #waiting for connection + PRIMARY = False + conn, addr = sock.accept() + PRIMARY = True + + while conn: + try: + _, _, _ = select.select([conn,], [conn,], [], 5) + except select.error: + break + + #Sending message to connected primary + try: + cmd_str = main2primary.get(True) + conn.send(cmd_str) #send only takes string + except KeyError: + break + except Exception, excep: + print (str(excep)) + break + + #Receiving from primary + try: + data = conn.recv(1024) # 1024 stands for bytes of data to be received + if data: + primary2main.put("recv:" + str(addr) + ":" + "{" + data + "}") + else: + primary2main.put("closing:" + str(addr)) + conn.close() + break + except Exception, excep: + print (str(excep)) + break + + print ("primary communication thread end") + +def close_all_secondary(): + """Exit all secondary processes""" + + return; + + global SECONDARY_COUNT + + tmp_list = [] + for i in SECONDARY_LIST: + tmp_list.append(i) + for i in tmp_list: + command_secondary(i, 'exit') + SECONDARY_COUNT = 0 + +def check_sec_cmds(cmds): + """Validate secondary commands before sending""" + + return 1 + + level1 = ['status', 'exit', 'forward', 'stop'] + level2 = ['add', 'patch', 'del'] + patch_args = ['reset'] + add_del_args = ['ring', 'vhost'] + cmdlist = cmds.split(' ') + valid = 0 + + length = len(cmdlist) + if length == 1: + if cmdlist[0] in level1: + valid = 1 + elif length == 2: + if cmdlist[0] == 'patch': + if cmdlist[1] in patch_args: + valid = 1 + elif length == 3: + if cmdlist[0] in level2: + if cmdlist[0] == 'add' or cmdlist[0] == 'del': + if cmdlist[1] in add_del_args: + if str.isdigit(cmdlist[2]): + valid = 1 + elif cmdlist[0] == 'patch': + if str.isdigit(cmdlist[1]) and str.isdigit(cmdlist[2]): + valid = 1 + + return valid + +class Shell(cmd.Cmd): + """SPP command prompt""" + + intro = 'Welcome to the spp. Type help or ? to list commands.\n' + prompt = 'spp > ' + recorded_file = None + + COMMANDS = ['status', 'add', 'patch', 'ring', 'vhost', + 'reset', 'exit', 'forward', 'stop', 'clear'] + + def complete_pri(self, text, line, begidx, endidx): + """Completion for primary process commands""" + + if not text: + completions = self.COMMANDS[:] + else: + completions = [p + for p in self.COMMANDS + if p.startswith(text) + ] + return completions + + def complete_sec(self, text, line, begidx, endidx): + """Completion for secondary process commands""" + + if not text: + completions = self.COMMANDS[:] + else: + completions = [p + for p in self.COMMANDS + if p.startswith(text) + ] + return completions + + def do_status(self, _): + """Display Soft Patch Panel Status""" + + print_status() + + def do_pri(self, command): + """Send command to primary process""" + + if command and command in self.COMMANDS: + command_primary(command) + else: + print ("primary invalid command") + + def do_sec(self, arg): + """Send command to secondary process""" + + cmds = arg.split(';') + if len(cmds) < 2: + print ("error") + elif str.isdigit(cmds[0]): + sec_id = int(cmds[0]) + if check_sec_cmds(cmds[1]): + command_secondary(sec_id, cmds[1]) + else: + print ("invalid cmd") + else: + print (cmds[0]) + print ("first %s" % cmds[1]) + + def do_record(self, arg): + """Save future commands to filename: RECORD filename.cmd""" + + self.recorded_file = open(arg, 'w') + + def do_playback(self, arg): + """Playback commands from a file: PLAYBACK filename.cmd""" + + self.close() + try: + with open(arg) as recorded_file: + lines = [] + for line in recorded_file: + if line.strip().startswith("#"): + continue + lines.append(line) + self.cmdqueue.extend(lines) + except IOError: + print ("Error: File does not exist.") + + def precmd(self, line): + line = line.lower() + if self.recorded_file and 'playback' not in line: + print(line, file=self.recorded_file) + return line + + def close(self): + """Close record file""" + + if self.recorded_file: + print("closing file") + self.recorded_file.close() + self.recorded_file = None + + def do_bye(self, arg): + """Stop recording, close SPP, and exit: BYE""" + + cmds = arg.split(' ') + if cmds[0] == 'sec': + close_all_secondary() + elif cmds[0] == 'all': + close_all_secondary() + command_primary('exit') + elif cmds[0] == '': + print('Thank you for using Soft Patch Panel') + self.close() + return True + +def main(argv): + """main""" + + # Defining server address and port + host = '' #'localhost' or '127.0.0.1' or '' are all same + + try: + opts, _ = getopt.getopt(argv, "p:s:h", ["help", "primary = ", "secondary"]) + except getopt.GetoptError: + print ('spp.py -p -s ') + sys.exit(2) + for opt, arg in opts: + if opt in ("-h", "--help"): + print ('spp.py -p -s ') + sys.exit() + elif opt in ("-p", "--primary"): + primary_port = int(arg) + print ("primary port : %d" % primary_port) + elif opt in ("-s", "--secondary"): + secondary_port = int(arg) + print ('secondary port : %d' % secondary_port) + + #Creating primary socket object + primary_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + #Binding primary socket to a address. bind() takes tuple of host and port. + primary_sock.bind((host, primary_port)) + + #Listening primary at the address + primary_sock.listen(1) #5 denotes the number of clients can queue + + primary_thread = Thread(target=primarythread, + args=(primary_sock, MAIN2PRIMARY, PRIMARY2MAIN,)) + primary_thread.daemon = True + primary_thread.start() + + #Creating secondary socket object + secondary_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + #Binding secondary socket to a address. bind() takes tuple of host and port. + secondary_sock.bind((host, secondary_port)) + + #Listening secondary at the address + secondary_sock.listen(MAX_SECONDARY) + + # secondary process handling thread + start_new_thread(acceptthread, (secondary_sock, MAIN2SEC, SEC2MAIN)) + + shell = Shell() + shell.cmdloop() + shell = None + + primary_sock.shutdown(socket.SHUT_RDWR) + primary_sock.close() + secondary_sock.shutdown(socket.SHUT_RDWR) + secondary_sock.close() + +if __name__ == "__main__": + main(sys.argv[1:]) -- 1.9.1