diff --git a/Makefile b/Makefile
index 0777dba001..376becda29 100644
--- a/Makefile
+++ b/Makefile
@@ -117,6 +117,7 @@ server:
tmux rename-window 'Tower'
tmux select-window -t tower:0
tmux split-window -v 'exec make celeryd'
+ tmux split-window -h 'exec make socketservice'
tmux split-window -v 'exec make receiver'
tmux split-window -h 'exec make taskmanager'
tmux -2 attach-session -t tower
@@ -136,6 +137,9 @@ receiver:
taskmanager:
$(PYTHON) manage.py run_task_system
+socketservice:
+ $(PYTHON) manage.py run_socketio_service
+
# Run all API unit tests.
test:
$(PYTHON) manage.py test -v2 main
diff --git a/awx/main/management/commands/run_socketio_service.py b/awx/main/management/commands/run_socketio_service.py
new file mode 100644
index 0000000000..ea9000d3d8
--- /dev/null
+++ b/awx/main/management/commands/run_socketio_service.py
@@ -0,0 +1,88 @@
+# Copyright (c) 2014 AnsibleWorks, Inc.
+# All Rights Reserved.
+
+# Python
+import os
+import datetime
+import logging
+import json
+import signal
+import time
+from optparse import make_option
+from multiprocessing import Process
+
+# Django
+from django.conf import settings
+from django.core.management.base import NoArgsCommand, CommandError
+from django.db import transaction, DatabaseError
+from django.contrib.auth.models import User
+from django.utils.dateparse import parse_datetime
+from django.utils.timezone import now, is_aware, make_aware
+from django.utils.tzinfo import FixedOffset
+
+# AWX
+from awx.main.models import *
+
+# ZeroMQ
+import zmq
+
+# gevent & socketio
+from socketio import socketio_manage
+from socketio.server import SocketIOServer
+from socketio.namespace import BaseNamespace
+
+class TestNamespace(BaseNamespace):
+
+ def recv_connect(self):
+ print("Received client connect for test namespace from %s" % str(self.environ['REMOTE_ADDR']))
+ #print("Env: " + str(self.environ))
+ self.emit('connect', True)
+
+class TowerSocket(object):
+
+ def __call__(self, environ, start_response):
+ path = environ['PATH_INFO'].strip('/') or 'index.html'
+ print path
+ if path.startswith('socket.io'):
+ socketio_manage(environ, {'/socket.io/test': TestNamespace})
+ socketio_manage(environ, {'/socket.io/jobs': JobNamespace})
+ socketio_manage(environ, {'/socket.io/job_events': JobEventNamespace})
+ else:
+ start_response('404 Not Found', [])
+ return ['
Not Found
']
+
+class Command(NoArgsCommand):
+ '''
+ SocketIO event emitter Tower service
+ Receives notifications from other services destined for UI notification
+ '''
+
+ help = 'Launch the SocketIO event emitter service'
+
+ option_list = NoArgsCommand.option_list + (
+ make_option('--receive_port', dest='receive_port', type='int', default=5559,
+ help='Port to listen for new events that will be destined for a client'),
+ make_option('--socketio_port', dest='socketio_port', type='int', default=8080,
+ help='Port to accept socketio requests from clients'),)
+
+ def init_logging(self):
+ log_levels = dict(enumerate([logging.ERROR, logging.INFO,
+ logging.DEBUG, 0]))
+ self.logger = logging.getLogger('awx.main.commands.run_socketio_service')
+ self.logger.setLevel(log_levels.get(self.verbosity, 0))
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter('%(message)s'))
+ self.logger.addHandler(handler)
+ self.logger.propagate = False
+
+ def handle_noargs(self, **options):
+ self.verbosity = int(options.get('verbosity', 1))
+ self.init_logging()
+ socketio_listen_port = settings.SOCKETIO_LISTEN_PORT
+ socketio_notification_port = settings.SOCKETIO_NOTIFICATION_PORT
+ try:
+ print 'Listening on port http://0.0.0.0:' + str(socketio_listen_port)
+ server = SocketIOServer(('0.0.0.0', socketio_listen_port), TowerSocket(), resource='socket.io')
+ server.serve_forever()
+ except KeyboardInterrupt:
+ pass
diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py
index 2789a1c7e8..4b82f03f6f 100644
--- a/awx/main/models/schedules.py
+++ b/awx/main/models/schedules.py
@@ -104,7 +104,9 @@ class Schedule(CommonModel):
self.dtend = make_aware(datetime.datetime.strptime(until_date, "%Y%m%dT%H%M%SZ"), get_default_timezone())
if 'count' in self.rrule.lower():
self.dtend = future_rs[-1]
- self.unified_job_template.update_computed_fields()
+ from awx.main.signals import ignore_inventory_computed_fields
+ with ignore_inventory_computed_fields():
+ self.unified_job_template.update_computed_fields()
def save(self, *args, **kwargs):
self.update_computed_fields()
diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py
index 32cc9a659b..f2f437aef2 100644
--- a/awx/settings/defaults.py
+++ b/awx/settings/defaults.py
@@ -387,6 +387,9 @@ CALLBACK_QUEUE_PORT = "ipc:///tmp/callback_receiver.ipc"
TASK_COMMAND_PORT = "tcp://127.0.0.1:6556"
+SOCKETIO_NOTIFICATION_PORT = "tcp://127.0.0.1:6557"
+SOCKETIO_LISTEN_PORT = 8080
+
# Logging configuration.
LOGGING = {
'version': 1,