forked from frenetic-lang/pyretic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pyretic.py
executable file
·211 lines (191 loc) · 8.34 KB
/
pyretic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/python
################################################################################
# The Pyretic Project #
# frenetic-lang.org/pyretic #
# author: Joshua Reich ([email protected]) #
################################################################################
# Licensed to the Pyretic Project by one or more contributors. See the #
# NOTICES file distributed with this work for additional information #
# regarding copyright and ownership. The Pyretic Project licenses this #
# file to you under the following license. #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided the following conditions are met: #
# - Redistributions of source code must retain the above copyright #
# notice, this list of conditions and the following disclaimer. #
# - Redistributions in binary form must reproduce the above copyright #
# notice, this list of conditions and the following disclaimer in #
# the documentation or other materials provided with the distribution. #
# - The names of the copyright holds and contributors may not be used to #
# endorse or promote products derived from this work without specific #
# prior written permission. #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT #
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the #
# LICENSE file distributed with this work for specific language governing #
# permissions and limitations under the License. #
################################################################################
from pyretic.core.runtime import Runtime
from pyretic.backend.backend import Backend
import sys
import threading
import signal
import subprocess
from importlib import import_module
from optparse import OptionParser
import re
import os
import logging
from multiprocessing import Queue, Process
import pyretic.core.util as util
import yappi
of_client = None
enable_profile = False
def signal_handler(signal, frame):
print '\n----starting pyretic shutdown------'
# for thread in threading.enumerate():
# print (thread,thread.isAlive())
if of_client:
print "attempting to kill of_client"
of_client.kill()
# print "attempting get output of of_client:"
# output = of_client.communicate()[0]
# print output
print "pyretic.py done"
# Print profile information if enabled
if enable_profile:
funcstats = yappi.get_func_stats()
funcstats.sort("tsub")
funcstats.print_all(columns={0:("name",38), 1:("ncall",8), 2:("tsub",8),
3:("ttot",8), 4:("tavg", 8)})
sys.exit(0)
def parseArgs():
"""Parse command-line args and return options object.
returns: opts parse options dict"""
desc = ( 'Pyretic runtime' )
usage = ( '%prog [options]\n'
'(type %prog -h for details)' )
end_args = 0
for arg in sys.argv[1:]:
if not re.match('-',arg):
end_args = sys.argv.index(arg)
kwargs_to_pass = None
if end_args > 0:
kwargs_to_pass = sys.argv[end_args+1:]
sys.argv = sys.argv[:end_args+1]
op = OptionParser( description=desc, usage=usage )
op.add_option( '--frontend-only', '-f', action="store_true",
dest="frontend_only", help = 'only start the frontend' )
op.add_option( '--mode', '-m', type='choice',
choices=['interpreted','i','reactive0','r0','proactive0','p0','proactive1','p1'],
help = '|'.join( ['interpreted/i','reactive0/r0','proactiveN/pN for N={0,1}'] ) )
op.add_option( '--verbosity', '-v', type='choice',
choices=['low','normal','high','please-make-it-stop'],
default = 'low',
help = '|'.join( ['low','normal','high','please-make-it-stop'] ) )
op.add_option( '--enable_profile', '-p', action="store_true",
dest="enable_profile",
help = 'enable yappi multithreaded profiler' )
op.set_defaults(frontend_only=False,mode='reactive0',enable_profile=False)
options, args = op.parse_args()
return (op, options, args, kwargs_to_pass)
def main():
global of_client, enable_profile
(op, options, args, kwargs_to_pass) = parseArgs()
if options.mode == 'i':
options.mode = 'interpreted'
elif options.mode == 'r0':
options.mode = 'reactive0'
elif options.mode == 'p0':
options.mode = 'proactive0'
elif options.mode == 'p1':
options.mode = 'proactive1'
try:
module_name = args[0]
except IndexError:
print 'Module must be specified'
print ''
op.print_usage()
sys.exit(1)
try:
module = import_module(module_name)
except ImportError, e:
print 'Must be a valid python module'
print 'e.g, full module name,'
print ' no .py suffix,'
print ' located on the system PYTHONPATH'
print ''
print 'Exception message for ImportError was:'
print e
sys.exit(1)
main = module.main
try:
path_main = module.path_main
except:
path_main = None
kwargs = { k : v for [k,v] in [ i.lstrip('--').split('=') for i in kwargs_to_pass ]}
sys.setrecursionlimit(1500) #INCREASE THIS IF "maximum recursion depth exceeded"
# Set up multiprocess logging.
verbosity_map = { 'low' : logging.ERROR,
'normal' : logging.WARNING,
'high' : logging.INFO,
'please-make-it-stop' : logging.DEBUG }
logging_queue = Queue()
# Make a logging process.
def log_writer(queue, log_level):
formatter = logging.Formatter('%(levelname)s:%(name)s:%(asctime)s: %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
handler.setLevel(log_level)
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(log_level)
while(True):
try:
to_log = queue.get()
except KeyboardInterrupt, e:
print "\nkilling log"
import sys
sys.exit(0)
logger.handle(to_log)
log_level = verbosity_map.get(options.verbosity, logging.DEBUG)
log_process = Process(target=log_writer,args=(logging_queue, log_level,))
log_process.daemon = True
log_process.start()
# Set default handler.
logger = logging.getLogger()
handler = util.QueueStreamHandler(logging_queue)
logger.addHandler(handler)
logger.setLevel(log_level)
runtime = Runtime(Backend(),main,path_main,kwargs,options.mode,options.verbosity)
if not options.frontend_only:
try:
output = subprocess.check_output('echo $PYTHONPATH',shell=True).strip()
except:
print 'Error: Unable to obtain PYTHONPATH'
sys.exit(1)
poxpath = None
for p in output.split(':'):
if re.match('.*pox/?$',p):
poxpath = os.path.abspath(p)
break
if poxpath is None:
print 'Error: pox not found in PYTHONPATH'
sys.exit(1)
pox_exec = os.path.join(poxpath,'pox.py')
python=sys.executable
# TODO(josh): pipe pox_client stdout to subprocess.PIPE or
# other log file descriptor if necessary
of_client = subprocess.Popen([python,
pox_exec,
'of_client.pox_client' ],
stdout=sys.stdout,
stderr=subprocess.STDOUT)
if options.enable_profile:
enable_profile = True
yappi.start()
signal.signal(signal.SIGINT, signal_handler)
signal.pause()
if __name__ == '__main__':
main()