Base net-virt CLI files on top of which ONOS specific changes will be done
diff --git a/cli/sdncon/controller/notification.py b/cli/sdncon/controller/notification.py
new file mode 100755
index 0000000..ba9c97a
--- /dev/null
+++ b/cli/sdncon/controller/notification.py
@@ -0,0 +1,126 @@
+#
+# Copyright (c) 2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+#      http://www.eclipse.org/legal/epl-v10.html
+#
+# 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 for the specific language governing
+# permissions and limitations under the License.
+#
+
+from django.db.models.signals import post_save, post_delete
+import urllib2
+import json
+
+# FIXME: It's not thread-safe to use globals here,
+# but we currently only allow a single thread per Django process,
+# so this shouldn't cause a problem. If/when we support multiple
+# threads we could fix this by using thread local variables.
+# To support true batch notifications across multiple REST calls
+# we'd need to actually store the batched up actions in the DB,
+# since the individual REST calls would be dispatched to
+# multiple processes, so we couldn't use in-memory batching of
+# the actions like we do now. The current batch support only
+# works for single REST updates/deletes with query parameters to
+# select the records that can affect multiple records.
+notifications_inited = False
+batch_level = 0
+actions = None
+
+
+def begin_batch_notification():
+    global batch_level
+    global actions
+    
+    if batch_level == 0:
+        actions = []
+    batch_level += 1
+
+
+# FIXME: Could generalize this so it's not hard-coded for SDNPlatform, but
+# since this is supposed to be a short-term mechanism for triggering the
+# storage notifications in SDNPlatform it's not clear if it makes sense to
+# invest time in cleaning this up.
+def end_batch_notification(reset=False):
+    global batch_level
+    global actions
+    
+    if reset:
+        batch_level = 0
+    elif batch_level > 0:
+        batch_level -= 1
+    
+    if batch_level == 0:
+        if actions:
+            url = 'http://localhost:8080/wm/storage/notify/json'
+            post_data = json.dumps(actions)
+            actions = None
+            request = urllib2.Request(url, post_data, {'Content-Type':'application/json'})
+            try:
+                response = urllib2.urlopen(request)
+                # FIXME: Log error if request had an error
+                _response_text = response.read() 
+            except Exception, _e: 
+                # SDNPlatform may not be running, but we don't want that to be a fatal
+                # error, so we just ignore the exception in that case.
+                pass
+        actions = None
+
+
+def do_action(sender, instance, action):
+    # If we're not already in a batch operation, start a local one
+    # for just this one change. This is so the code that actually
+    # sends the notifications to SDNPlatform can be bottle-necked through
+    # end_batch_notification
+    local_batch = batch_level == 0
+    if local_batch:
+        begin_batch_notification()  
+    
+    last_action = actions[-1] if actions else None
+    if (last_action is None or
+        # pylint: disable=W0212
+        last_action['tableName'] != sender._meta.db_table or
+        last_action['action'] != action):
+        # pylint: disable=W0212
+        last_action = {'tableName': sender._meta.db_table, 'action': action, 'keys': []}
+        actions.append(last_action)
+    
+    keys = last_action['keys']
+    if instance.pk not in keys:
+        keys.append(instance.pk)
+        
+    if local_batch:
+        end_batch_notification()
+
+
+def do_modify_notification(sender, instance):
+    do_action(sender, instance, 'MODIFY')
+    
+def do_delete_notification(sender, instance):
+    do_action(sender, instance, 'DELETE')
+
+def notification_post_save_handler(sender, **kwargs):
+    from sdncon.rest.config import is_config_model
+    if not kwargs['raw'] and (kwargs['using'] == "default") and not is_config_model(sender):
+        do_modify_notification(sender, kwargs['instance'])
+
+
+def notification_post_delete_handler(sender, **kwargs):
+    from sdncon.rest.config import is_config_model
+    if kwargs['using'] == 'default' and not is_config_model(sender):
+        do_delete_notification(sender, kwargs['instance'])
+
+
+def init_notifications():
+    global notifications_inited
+    if not notifications_inited:
+        post_save.connect(notification_post_save_handler)
+        post_delete.connect(notification_post_delete_handler)
+        notifications_inited = True
+