Base net-virt CLI files on top of which ONOS specific changes will be done
diff --git a/cli/sdncon/rest/config.py b/cli/sdncon/rest/config.py
new file mode 100755
index 0000000..7c0de5c
--- /dev/null
+++ b/cli/sdncon/rest/config.py
@@ -0,0 +1,219 @@
+#
+# 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 json
+import os
+import traceback
+from django.core import serializers
+import sdncon
+
+# FIXME: This code is not thread-safe!!!
+# Currently we don't run Django with multiple threads so this isn't a problem,
+# but if we were to try to enable threading this code would need to be fixed.
+
+pre_save_instance = None
+config_handlers = []
+config_models = set()
+
+
+def add_config_handler(dependencies, handler):
+    """
+    The dependencies argument is a dictionary where the key is a model
+    (i.e. the actual model, not the name of the mode) and the value of the
+    key is a tuple (or list) of field names whose modification should
+    trigger the config handler. The value can also be None to indicate
+    that the config handler should be triggered when any field in the
+    model is modified.
+    
+    The calling convention of a config handler is:
+    
+    def my_config_handler(op, old_instance, new_instance, modified_fields)
+    
+    The 'op' argument is one of 'INSERT', 'UPDATE' or 'DELETE', corresponding
+    to an insertion, update or deletion of a row in the model.
+    The 'old_instance' argument is the instance of the model before the changes.
+    For an 'INSERT' op, old_instance and modified_fields are both None.
+    The 'new_instance' argument is the instance of the model after the changes.
+    For a 'DELETE' op, new_instance and modified_fields are both None.
+    the 'modified_fields' is a dictionary containing the fields that were changed.
+    The dictionary key is the name of the field and the value is the new value.
+    """
+    assert dependencies != None
+    assert handler != None
+    
+    for model in dependencies.keys():
+        config_models.add(model)
+            
+    config_handlers.append((dependencies, handler))
+
+last_config_state_path = None
+
+def get_last_config_state_path():
+    global last_config_state_path
+    if not last_config_state_path:
+        last_config_state_dir = "%s/run/" % sdncon.SDN_ROOT
+        if not os.path.exists(last_config_state_dir):
+            last_config_state_dir = '/tmp'
+        last_config_state_path = os.path.join(last_config_state_dir, 'last-config-state')
+    return last_config_state_path
+
+def reset_last_config_state():
+    path = get_last_config_state_path()
+    try:
+        os.unlink(path)
+    except Exception, _e:
+        pass
+    
+
+def config_read_state():
+    last_config_state = None
+    f = None
+    try:
+        f = open(get_last_config_state_path(), 'r')
+        last_config_state_text = f.read()
+        last_config_state = json.loads(last_config_state_text)
+    except Exception, _e:
+        pass
+    finally:
+        if f:
+            f.close()
+    return last_config_state
+
+def config_write_state(config_state):
+    f = None
+    try:
+        config_state_text = json.dumps(config_state)
+        f = open(get_last_config_state_path(), 'w')
+        f.write(config_state_text)
+    except Exception, e:
+        print "Error writing last config state: %s" % str(e)
+    finally:
+        if f:
+            f.close()
+
+def config_do_insert(sender, new_instance):
+    for config_handler in config_handlers:
+        dependencies = config_handler[0]
+        if sender in dependencies:
+            handler = config_handler[1]
+            try:
+                handler(op='INSERT', old_instance=None, new_instance=new_instance, modified_fields=None)
+            except Exception, _e:
+                traceback.print_exc()
+                
+def config_do_update(sender, old_instance, new_instance):
+    for config_handler in config_handlers:
+        dependencies = config_handler[0]
+        if sender in dependencies:
+            handler = config_handler[1]
+            modified_fields = {}
+            fields = dependencies.get(sender)
+            if not fields:
+                # If no fields were specified for the model then check all of the fields.
+                fields = [field.name for field in sender._meta.fields]
+                
+            for field in fields:
+                old_value = getattr(old_instance, field)
+                new_value = getattr(new_instance, field)
+                if new_value != old_value:
+                    modified_fields[field] = new_value
+            if modified_fields:
+                try:
+                    handler(op='UPDATE', old_instance=old_instance, new_instance=new_instance, modified_fields=modified_fields)
+                except Exception, _e:
+                    traceback.print_exc()
+
+
+def config_do_delete(sender, instance):
+    for config_handler in config_handlers:
+        dependencies = config_handler[0]
+        if sender in dependencies:
+            handler = config_handler[1]
+            try:
+                handler(op='DELETE', old_instance=instance, new_instance=None, modified_fields=None)
+            except Exception, _e:
+                traceback.print_exc()
+
+def model_instances_equal(instance1, instance2):
+    if instance1 == None:
+        return instance1 == instance2
+    
+    for field in instance1._meta.fields:
+        value1 = field.value_from_object(instance1)
+        value2 = field.value_from_object(instance2)
+        if value1 != value2:
+            return False
+    return True
+
+def config_check_state():
+    from sdncon.controller.notification import do_modify_notification, do_delete_notification
+    last_config_state = config_read_state()
+    try:
+        last_config_instances = last_config_state.get('instances')
+    except Exception, _e:
+        last_config_instances = {}
+    
+    current_config_instances = {}
+    
+    for config_model in config_models:
+        try:
+            serialized_old_instances = json.dumps(last_config_instances.get(config_model.__name__,[]))
+            old_instance_info = serializers.deserialize('json', serialized_old_instances)
+            old_instances = [info.object for info in old_instance_info]
+        except Exception, _e:
+            old_instances = []
+            
+        new_instances = config_model.objects.all()
+        
+        for new_instance in new_instances:
+            for index, old_instance in enumerate(old_instances):
+                if new_instance.pk == old_instance.pk:
+                    if not model_instances_equal(new_instance, old_instance):
+                        config_do_update(config_model, old_instance, new_instance)
+                        do_modify_notification(config_model, new_instance)
+                    del old_instances[index]
+                    break
+            else:
+                config_do_insert(config_model, new_instance)
+                do_modify_notification(config_model, new_instance)
+        
+        for deleted_instance in old_instances:
+            config_do_delete(config_model, deleted_instance)
+            do_delete_notification(config_model, deleted_instance)
+
+        try:
+            serialized_new_instances = serializers.serialize("json", new_instances)
+            current_config_instances[config_model.__name__] = json.loads(serialized_new_instances)
+        except:
+            print 'Failed to serialize', config_model.__name__
+        
+    config_write_state({'instances': current_config_instances})
+
+def config_post_save_handler(sender, **kwargs):
+    if not kwargs['raw'] and (kwargs['using'] == "default") and sender in config_models:
+        config_check_state()
+    
+def config_post_delete_handler(sender, **kwargs):
+    if kwargs['using'] == 'default' and sender in config_models:
+        config_check_state()
+
+def is_config_model(model):
+    return model in config_models
+ 
+def init_config():
+    post_save.connect(config_post_save_handler)
+    post_delete.connect(config_post_delete_handler)