commit 126a01f2f44d973f09195c529229fbe3ab866843
Author: Kostas Papadimitriou <kpap@grnet.gr>
Date:   Thu Jul 5 14:20:20 2012 +0300

    Cyclades UI integration with the new networks api
    
    - Handles nic connections instead of vms. New NIC model and NICs
      collections created to handle the NIC api logic. NIC models get
      updated on each vms/networks api call.
    - New private network creation form.
    - Display additional fields on private networks list (cidr, nic ip)
    - New cyclades ui settings introduced. UI_NETWORK_AVAILABLE_NETWORK_TYPES,
      UI_NETWORK_AVAILABLE_SUBNETS, UI_NETWORK_ALLOW_DUPLICATE_VM_NICS,
      UI_NETWORK_STRICT_DESTROY.

diff --git a/snf-cyclades-app/synnefo/app_settings/default/ui.py b/snf-cyclades-app/synnefo/app_settings/default/ui.py
index 253ca3e..31b5767 100644
--- a/snf-cyclades-app/synnefo/app_settings/default/ui.py
+++ b/snf-cyclades-app/synnefo/app_settings/default/ui.py
@@ -117,7 +117,8 @@ UI_SKIP_TIMEOUTS = 1
 UI_HANDLE_WINDOW_EXCEPTIONS = True
 
 # A list of os names that support ssh public key assignment
-UI_SUPPORT_SSH_OS_LIST = ['debian', 'fedora', 'okeanos', 'ubuntu', 'kubuntu', 'centos']
+UI_SUPPORT_SSH_OS_LIST = ['debian', 'fedora', 'okeanos', 'ubuntu', 'kubuntu',
+                          'centos', 'archlinux']
 
 # OS/username map to identify default user name for the specified os
 UI_OS_DEFAULT_USER_MAP = {
@@ -126,6 +127,26 @@ UI_OS_DEFAULT_USER_MAP = {
     'windows': 'Administrator'
 }
 
+##########################
+# UI NETWORK VIEW SETTINGS
+##########################
+
+# Available network types for use to choose when creating a private network
+# If only one set, no select options will be displayed
+UI_NETWORK_AVAILABLE_NETWORK_TYPES = {'PRIVATE_FILTERED': 'mac-filtering'}
+
+# Suggested private networks to let the user choose from when creating a private
+# network with dhcp enabled
+UI_NETWORK_AVAILABLE_SUBNETS = ['10.0.0.1/24', '192.168.1.1/24']
+
+# Whether to display already connected vm's to the network connect overlay
+UI_NETWORK_ALLOW_DUPLICATE_VM_NICS = False
+
+# Whether to display destroy action on private networks that contain vms. If
+# set to True, destroy action will only get displayed if user disconnect all
+# virtual machines from the network.
+UI_NETWORK_STRICT_DESTROY = False
+
 
 ###############
 # UI EXTENSIONS
diff --git a/snf-cyclades-app/synnefo/ui/static/snf/css/main.css b/snf-cyclades-app/synnefo/ui/static/snf/css/main.css
index ffff8b2..753e3a6 100644
--- a/snf-cyclades-app/synnefo/ui/static/snf/css/main.css
+++ b/snf-cyclades-app/synnefo/ui/static/snf/css/main.css
@@ -21,6 +21,7 @@ body {
     text-align:center;
     font-size: 16px;
     background-position: 0px 35px; 
+    overflow-y: scroll;
 }
 
 a, a:hover, a:active, a:focus, div:focus, span:focus, li:focus {
@@ -598,9 +599,51 @@ div#footer-text{
     width: 140px;
 }
 
-.overlay-networks-create .form-actions {
-    float: right;
-    margin-top: -26px !important;
+.overlay-networks-create .form-action {
+  float: left;
+}
+
+.overlay-networks-create .fixpos {
+  margin-top: 3px;
+}
+
+.overlay-networks-create .col-fields.bordered {
+  border-bottom: 1px solid #CCC;
+  margin-bottom: 10px;
+  padding-bottom: 10px;
+}
+
+.overlay-networks-create .col-fields {
+  margin-bottom: 10px;
+}
+
+form .fields-desc {
+  font-size: 0.8em !important; 
+  margin: 5px 0;
+  padding-bottom: 0 !important;
+}
+
+.overlay-networks-create form select {
+  padding: 1px;
+}
+
+.overlay-networks-create #network-create-subnet-custom {
+  width: 125px;
+}
+
+.overlay-networks-create .col-fields .form-field {
+  float: left;
+  margin-right: 20px;
+}
+
+.overlay-networks-create .col-fields .right-field {
+  float: right;
+  width: 210px;
+  margin-right: 0px;
+}
+
+.overlay-networks-create .use-dhcp {
+  width: auto;
 }
 
 #wizard #start:hover, #networks-wizard .create:hover {
@@ -1095,7 +1138,7 @@ div.indicator1, div.indicator2, div.indicator3, div.indicator4 {
 }
 
 .error-state .indicator1, .error-state .indicator2, .error-state .indicator3, .error-state .indicator4 {
-    background-color: #ff0000;
+    background-color: #ff0000 !important;
 }
 
 .terminated-state .indicator1, .terminated-state .indicator2, .terminated-state .indicator3, .terminated-state .indicator4 {
@@ -2191,7 +2234,7 @@ div#user .langmenu .sep {
 }
 
 .network-machine h5 {
-    margin-bottom: 26px;
+    margin-bottom: 20px;
     margin-top:0px;
 }
 
@@ -2552,6 +2595,18 @@ div.actions a.selected, div.actions a.selected:hover, div.machine-actions a.sele
     display:none;
 }
 
+div.network-cont.disable-destroy .actions .destroy {
+    display: none !important;
+}
+
+div.network-cont.pending .actions .action-add {
+    display: none;
+}
+
+div.network-cont.in-error .actions .action-add {
+    display: none;
+}
+
 div.network a.action-network-add {
     display: none;
 }
@@ -2754,6 +2809,10 @@ div.network .net-vm-actions .confirm_single {
     right:0;
 }
 
+div.network .nic-ip {
+  font-size: 0.8em;
+}
+
 div.network div.display a {
     visibility: visible;
 }
@@ -3030,6 +3089,7 @@ div.network-add-machine {
 h5.machine-connect {
     font-size: 75%;
     margin-bottom: 3px;
+    height: 23px;
 }
 
 .machine-connect span {
diff --git a/snf-cyclades-app/synnefo/ui/static/snf/js/models.js b/snf-cyclades-app/synnefo/ui/static/snf/js/models.js
index 4d4dcbf..e91204c 100644
--- a/snf-cyclades-app/synnefo/ui/static/snf/js/models.js
+++ b/snf-cyclades-app/synnefo/ui/static/snf/js/models.js
@@ -56,6 +56,8 @@
         var baseurl = baseurl || snf.config.api_urls[this.api_type];
         return baseurl + "/" + this.path;
     }
+
+    var NIC_REGEX = /^nic-([0-9]+)-([0-9]+)$/
     
     // i18n
     BUILDING_MESSAGES = window.BUILDING_MESSAGES || {'INIT': 'init', 'COPY': '{0}, {1}, {2}', 'FINAL': 'final'};
@@ -337,125 +339,6 @@
 
     });
     
-    //network vms list helper
-    var NetworkVMSList = function() {
-        this.initialize = function() {
-            this.vms = [];
-            this.pending = [];
-            this.pending_for_removal = [];
-        }
-        
-        this.add_pending_for_remove = function(vm_id) {
-            if (this.pending_for_removal.indexOf(vm_id) == -1) {
-                this.pending_for_removal.push(vm_id);
-            }
-
-            if (this.pending_for_removal.length) {
-                this.trigger("pending:remove:add");
-            }
-        },
-
-        this.add_pending = function(vm_id) {
-            if (this.pending.indexOf(vm_id) == -1) {
-                this.pending[this.pending.length] = vm_id;
-            }
-
-            if (this.pending.length) {
-                this.trigger("pending:add");
-            }
-        }
-
-        this.check_pending = function() {
-            var len = this.pending.length;
-            var args = [this.pending];
-            this.pending = _.difference(this.pending, this.vms);
-            if (len != this.pending.length) {
-                if (this.pending.length == 0) {
-                    this.trigger("pending:clear");
-                }
-            }
-
-            var len = this.pending_for_removal.length;
-            this.pending_for_removal = _.intersection(this.pending_for_removal, this.vms);
-            if (this.pending_for_removal.length == 0) {
-                this.trigger("pending:remove:clear");
-            }
-
-        }
-
-
-        this.add = function(vm_id) {
-            if (this.vms.indexOf(vm_id) == -1) {
-                this.vms[this.vms.length] = vm_id;
-                this.trigger("network:connect", vm_id);
-                this.check_pending();
-                return true;
-            }
-        }
-
-        this.remove = function(vm_id) {
-            if (this.vms.indexOf(vm_id) > -1) {
-                this.vms = _.without(this.vms, vm_id);
-                this.trigger("network:disconnect", vm_id);
-                this.check_pending();
-                return true;
-            }
-        }
-
-        this.get = function() {
-            return this.vms;
-        }
-
-        this.list = function() {
-            return storage.vms.filter(_.bind(function(vm){
-                return this.vms.indexOf(vm.id) > -1;
-            }, this))
-        }
-
-        this.initialize();
-    };
-    _.extend(NetworkVMSList.prototype, bb.Events);
-    
-    // vm networks list helper
-    var VMNetworksList = function() {
-        this.initialize = function() {
-            this.networks = {};
-            this.network_ids = [];
-        }
-
-        this.add = function(net_id, data) {
-            if (!this.networks[net_id]) {
-                this.networks[net_id] = data || {};
-                this.network_ids[this.network_ids.length] = net_id;
-                this.trigger("network:connect", net_id);
-                return true;
-            }
-        }
-
-        this.remove = function(net_id) {
-            if (this.networks[net_id]) {
-                delete this.networks[net_id];
-                this.network_ids = _.without(this.network_ids, net_id);
-                this.trigger("network:disconnect", net_id);
-                return true;
-            }
-            return false;
-        }
-
-        this.get = function() {
-            return this.networks;
-        }
-
-        this.list = function() {
-            return storage.networks.filter(_.bind(function(net){
-                return this.network_ids.indexOf(net.id) > -1;
-            }, this))
-        }
-
-        this.initialize();
-    };
-    _.extend(VMNetworksList.prototype, bb.Events);
-        
     models.ParamsList = function(){this.initialize.apply(this, arguments)};
     _.extend(models.ParamsList.prototype, bb.Events, {
 
@@ -559,27 +442,14 @@
     models.Network = models.Model.extend({
         path: 'networks',
         has_status: true,
+        defaults: {'connecting':0},
         
         initialize: function() {
-            this.vms = new NetworkVMSList();
-            this.vms.bind("pending:add", _.bind(this.handle_pending_connections, this, "add"));
-            this.vms.bind("pending:clear", _.bind(this.handle_pending_connections, this, "clear"));
-            this.vms.bind("pending:remove:add", _.bind(this.handle_pending_connections, this, "add"));
-            this.vms.bind("pending:remove:clear", _.bind(this.handle_pending_connections, this, "clear"));
-
             var ret = models.Network.__super__.initialize.apply(this, arguments);
-
-            storage.vms.bind("change:linked_to_nets", _.bind(this.update_connections, this, "vm:change"));
-            storage.vms.bind("add", _.bind(this.update_connections, this, "add"));
-            storage.vms.bind("remove", _.bind(this.update_connections, this, "remove"));
-            storage.vms.bind("reset", _.bind(this.update_connections, this, "reset"));
-
-            this.bind("change:linked_to", _.bind(this.update_connections, this, "net:change"));
-            this.update_connections();
-            this.update_state();
-            
             this.set({"actions": new models.ParamsList(this, "actions")});
-
+            this.update_state();
+            this.bind("change:nics", _.bind(synnefo.storage.nics.update_net_nics, synnefo.storage.nics));
+            this.bind("change:status", _.bind(this.update_state, this));
             return ret;
         },
 
@@ -588,107 +458,101 @@
             attrs.actions = _.clone(this.get("actions").actions);
             return attrs;
         },
+        
+        set_state: function(val) {
+            if (val == "PENDING" && this.get("state") == "DESTORY") {
+                return "DESTROY";
+            }
+            return val;
+        },
 
         update_state: function() {
-            if (this.vms.pending.length) {
+            if (this.get("connecting") > 0) {
                 this.set({state: "CONNECTING"});
                 return
             }
-
-            if (this.vms.pending_for_removal.length) {
+            
+            if (this.get_nics(function(nic){ return nic.get("removing") == 1}).length > 0) {
                 this.set({state: "DISCONNECTING"});
                 return
             }   
             
-            var firewalling = false;
-            _.each(this.vms.get(), _.bind(function(vm_id){
-                var vm = storage.vms.get(vm_id);
-                if (!vm) { return };
-                if (!_.isEmpty(vm.pending_firewalls)) {
-                    this.set({state:"FIREWALLING"});
-                    firewalling = true;
-                    return false;
-                }
-            },this));
-            if (firewalling) { return };
+            if (this.contains_firewalling_nics() > 0) {
+                this.set({state: "FIREWALLING"});
+                return
+            }   
+            
+            if (this.get("state") == "DESTROY") { 
+                this.set({"destroyed":1});
+            }
+            
+            this.set({state:this.get('status')});
+        },
 
-            this.set({state:"NORMAL"});
+        is_public: function() {
+            return this.id === "public";
         },
 
-        handle_pending_connections: function(action) {
+        decrease_connecting: function() {
+            var conn = this.get("connecting");
+            if (!conn) { conn = 0 };
+            if (conn > 0) {
+                conn--;
+            }
+            this.set({"connecting": conn});
             this.update_state();
         },
 
-        // handle vm/network connections
-        update_connections: function(action, model) {
-            
-            // vm removed disconnect vm from network
-            if (action == "remove") {
-                var removed_from_net = this.vms.remove(model.id);
-                var removed_from_vm = model.networks.remove(this.id);
-                if (removed_from_net) {this.trigger("vm:disconnect", model, this); this.change()};
-                if (removed_from_vm) {model.trigger("network:disconnect", this, model); this.change()};
-                return;
-            }
-            
-            // update links for all vms
-            var links = this.get("linked_to");
-            storage.vms.each(_.bind(function(vm) {
-                var vm_links = vm.get("linked_to") || [];
-                if (vm_links.indexOf(this.id) > -1) {
-                    // vm has connection to current network
-                    if (links.indexOf(vm.id) > -1) {
-                        // and network has connection to vm, so try
-                        // to append it
-                        var add_to_net = this.vms.add(vm.id);
-                        var index = _.indexOf(vm_links, this.id);
-                        var add_to_vm = vm.networks.add(this.id, vm.get("linked_to_nets")[index]);
-                        
-                        // call only if connection did not existed
-                        if (add_to_net) {this.trigger("vm:connect", vm, this); this.change()};
-                        if (add_to_vm) {vm.trigger("network:connect", this, vm); vm.change()};
-                    } else {
-                        // no connection, try to remove it
-                        var removed_from_net = this.vms.remove(vm.id);
-                        var removed_from_vm = vm.networks.remove(this.id);
-                        if (removed_from_net) {this.trigger("vm:disconnect", vm, this); this.change()};
-                        if (removed_from_vm) {vm.trigger("network:disconnect", this, vm); vm.change()};
-                    }
-                } else {
-                    // vm has no connection to current network, try to remove it
-                    var removed_from_net = this.vms.remove(vm.id);
-                    var removed_from_vm = vm.networks.remove(this.id);
-                    if (removed_from_net) {this.trigger("vm:disconnect", vm, this); this.change()};
-                    if (removed_from_vm) {vm.trigger("network:disconnect", this, vm); vm.change()};
-                }
-            },this));
+        increase_connecting: function() {
+            var conn = this.get("connecting");
+            if (!conn) { conn = 0 };
+            conn++;
+            this.set({"connecting": conn});
+            this.update_state();
         },
 
-        is_public: function() {
-            return this.id == "public";
+        connected_to: function(vm) {
+            return this.get('linked_to').indexOf(""+vm.id) > -1;
         },
 
-        contains_vm: function(vm) {
-            var net_vm_exists = this.vms.get().indexOf(vm.id) > -1;
-            var vm_net_exists = vm.is_connected_to(this);
-            return net_vm_exists && vm_net_exists;
+        connected_with_nic_id: function(nic_id) {
+            return _.keys(this.get('nics')).indexOf(nic_id) > -1;
         },
-        
+
+        get_nics: function(filter) {
+            var nics = synnefo.storage.nics.filter(function(nic) {
+                return nic.get('network_id') == this.id;
+            }, this);
+
+            if (filter) {
+                return _.filter(nics, filter);
+            }
+            return nics;
+        },
+
+        contains_firewalling_nics: function() {
+            return this.get_nics(function(n){return n.get('pending_firewall')}).length
+        },
+
         call: function(action, params, success, error) {
             if (action == "destroy") {
                 this.set({state:"DESTROY"});
-                this.get("actions").remove("destroy");
+                this.get("actions").remove("destroy", params);
                 this.remove(_.bind(function(){
                     success();
                 }, this), error);
             }
             
             if (action == "disconnect") {
-                _.each(params, _.bind(function(vm_id) {
-                    var vm = snf.storage.vms.get(vm_id);
-                    this.get("actions").remove("disconnect", vm_id);
-                    if (vm) {
-                        this.remove_vm(vm, success, error);
+                if (this.get("state") == "DESTROY") {
+                    return;
+                }
+
+                _.each(params, _.bind(function(nic_id) {
+                    var nic = snf.storage.nics.get(nic_id);
+                    this.get("actions").remove("disconnect", nic_id);
+                    if (nic) {
+                        this.remove_nic(nic, success, error);
                     }
                 }, this));
             }
@@ -700,18 +564,21 @@
             return this.api_call(this.api_path() + "/action", "create", 
                                  payload,
                                  _.bind(function(){
-                                     this.vms.add_pending(vm.id);
+                                     //this.vms.add_pending(vm.id);
+                                     this.increase_connecting();
                                      if (callback) {callback()}
                                  },this), error);
         },
 
-        remove_vm: function (vm, callback, error, options) {
-            var payload = {remove:{serverRef:"" + vm.id}};
+        remove_nic: function (nic, callback, error, options) {
+            var payload = {remove:{attachment:"" + nic.get("attachment_id")}};
             payload._options = options || {};
             return this.api_call(this.api_path() + "/action", "create", 
-                                 {remove:{serverRef:"" + vm.id}},
+                                 payload,
                                  _.bind(function(){
-                                     this.vms.add_pending_for_remove(vm.id);
+                                     nic.set({"removing": 1});
+                                     nic.get_network().update_state();
+                                     //this.vms.add_pending_for_remove(vm.id);
                                      if (callback) {callback()}
                                  },this), error);
         },
@@ -730,15 +597,21 @@
         },
 
         get_connectable_vms: function() {
-            var servers = this.vms.list();
             return storage.vms.filter(function(vm){
-                return servers.indexOf(vm) == -1 && !vm.in_error_state();
+                return !vm.in_error_state();
             })
         },
 
         state_message: function() {
-            if (this.get("state") == "NORMAL" && this.is_public()) {
-                return "Public network";
+            if (this.get("state") == "ACTIVE" && !this.is_public()) {
+                if (this.get("cidr") && this.get("dhcp") == true) {
+                    return this.get("cidr");
+                } else {
+                    return "Private network";
+                }
+            }
+            if (this.get("state") == "ACTIVE" && this.is_public()) {
+                  return "Public network";
             }
 
             return models.Network.STATES[this.get("state")];
@@ -755,21 +628,25 @@
                     this.call(action, with_params, success, error);
                 }, this));
             }, this));
+            this.get("actions").reset();
         }
     });
     
     models.Network.STATES = {
-        'NORMAL': 'Private network',
+        'ACTIVE': 'Private network',
         'CONNECTING': 'Connecting...',
         'DISCONNECTING': 'Disconnecting...',
         'FIREWALLING': 'Firewall update...',
-        'DESTROY': 'Destroying...'
+        'DESTROY': 'Destroying...',
+        'PENDING': 'Pending...',
+        'ERROR': 'Error'
     }
 
     models.Network.STATES_TRANSITIONS = {
-        'CONNECTING': ['NORMAL'],
-        'DISCONNECTING': ['NORMAL'],
-        'FIREWALLING': ['NORMAL']
+        'CONNECTING': ['ACTIVE'],
+        'DISCONNECTING': ['ACTIVE'],
+        'PENDING': ['ACTIVE'],
+        'FIREWALLING': ['ACTIVE']
     }
 
     // Virtualmachine model
@@ -778,7 +655,6 @@
         path: 'servers',
         has_status: true,
         initialize: function(params) {
-            this.networks = new VMNetworksList();
             
             this.pending_firewalls = {};
             
@@ -807,28 +683,16 @@
             this.bind("change:progress", _.bind(this.update_building_progress, this));
             this.update_building_progress();
 
-            this.bind("change:firewalls", _.bind(this.handle_firewall_change, this));
-            
             // default values
-            this.set({linked_to_nets:this.get("linked_to_nets") || []});
-            this.set({firewalls:this.get("firewalls") || []});
-
-            this.bind("change:state", _.bind(function(){if (this.state() == "DESTROY") { this.handle_destroy() }}, this))
-        },
-
-        handle_firewall_change: function() {
+            this.bind("change:state", _.bind(function(){
+                if (this.state() == "DESTROY") { 
+                    this.handle_destroy() 
+                }
+            }, this));
 
-        },
-        
-        set_linked_to_nets: function(data) {
-            this.set({"linked_to":_.map(data, function(n){ return n.id})});
-            return data;
+            this.bind("change:nics", _.bind(synnefo.storage.nics.update_vm_nics, synnefo.storage.nics));
         },
 
-        is_connected_to: function(net) {
-            return _.filter(this.networks.list(), function(n){return n.id == net.id}).length > 0;
-        },
-        
         status: function(st) {
             if (!st) { return this.get("status")}
             return this.set({status:st});
@@ -1103,23 +967,6 @@
             return models.VM.CONNECT_STATES.indexOf(this.state()) > -1;
         },
         
-        set_firewalls: function(data) {
-            _.each(data, _.bind(function(val, key){
-                if (this.pending_firewalls && this.pending_firewalls[key] && this.pending_firewalls[key] == val) {
-                        this.require_reboot();
-                        this.remove_pending_firewall(key, val);
-                }
-            }, this));
-            return data;
-        },
-
-        remove_pending_firewall: function(net_id, value) {
-            if (this.pending_firewalls[net_id] == value) {
-                delete this.pending_firewalls[net_id];
-                storage.networks.get(net_id).update_state();
-            }
-        },
-            
         remove_meta: function(key, complete, error) {
             var url = this.api_path() + "/meta/" + key;
             this.api_call(url, "delete", undefined, complete, error);
@@ -1139,27 +986,7 @@
             this.api_call(url, "update", payload, complete, error);
         },
 
-        set_firewall: function(net_id, value, callback, error, options) {
-            if (this.get("firewalls") && this.get("firewalls")[net_id] == value) { return }
-
-            this.pending_firewalls[net_id] = value;
-            this.trigger("change", this, this);
-            var payload = {"firewallProfile":{"profile":value}};
-            payload._options = _.extend({critical: false}, options);
-            
-            // reset firewall state on error
-            var error_cb = _.bind(function() {
-                thi
-            }, this);
-
-            this.api_call(this.api_path() + "/action", "create", payload, callback, error);
-            storage.networks.get(net_id).update_state();
-        },
 
-        firewall_pending: function(net_id) {
-            return this.pending_firewalls[net_id] != undefined;
-        },
-        
         // update/get the state of the machine
         state: function() {
             var args = slice.call(arguments);
@@ -1241,43 +1068,59 @@
         get_gui: function() {
             return this.get_meta('GUI');
         },
+        
+        connected_to: function(net) {
+            return this.get('linked_to').indexOf(net.id) > -1;
+        },
 
-        // get public ip addresses
-        // TODO: public network is always the 0 index ???
-        get_addresses: function(net_id) {
-            var net_id = net_id || "public";
-            
-            var info = this.get_network_info(net_id);
-            if (!info) { return {} };
-            addrs = {};
-            _.each(info.values, function(addr) {
-                addrs["ip" + addr.version] = addr.addr;
-            });
-            return addrs
+        connected_with_nic_id: function(nic_id) {
+            return _.keys(this.get('nics')).indexOf(nic_id) > -1;
         },
 
-        get_network_info: function(net_id) {
-            var net_id = net_id || "public";
-            
-            if (!this.networks.network_ids.length) { return {} };
+        get_nics: function(filter) {
+            ret = synnefo.storage.nics.filter(function(nic) {
+                return parseInt(nic.get('vm_id')) == this.id;
+            }, this);
 
-            var addresses = this.networks.get();
-            try {
-                return _.select(addresses, function(net, key){return key == net_id })[0];
-            } catch (err) {
-                //this.log.debug("Cannot find network {0}".format(net_id))
+            if (filter) {
+                return _.filter(ret, filter);
             }
+
+            return ret;
+        },
+
+        get_net_nics: function(net_id) {
+            return this.get_nics(function(n){return n.get('network_id') == net_id});
         },
 
-        firewall_profile: function(net_id) {
-            var net_id = net_id || "public";
-            var firewalls = this.get("firewalls");
-            return firewalls[net_id];
+        get_public_nic: function() {
+            return this.get_nics(function(n){ return n.get('network_id') == 'public'})[0];
         },
 
-        has_firewall: function(net_id) {
-            var net_id = net_id || "public";
-            return ["ENABLED","PROTECTED"].indexOf(this.firewall_profile()) > -1;
+        get_nic: function(net_id) {
+        },
+
+        has_firewall: function() {
+            var nic = this.get_public_nic();
+            if (nic) {
+                var profile = nic.get('firewallProfile'); 
+                return ['ENABLED', 'PROTECTED'].indexOf(profile) > -1;
+            }
+            return false;
+        },
+
+        get_firewall_profile: function() {
+            var nic = this.get_public_nic();
+            if (nic) {
+                return nic.get('firewallProfile');
+            }
+            return null;
+        },
+
+        get_addresses: function() {
+            var pnic = this.get_public_nic();
+            if (!pnic) { return {'ip4': undefined, 'ip6': undefined }};
+            return {'ip4': pnic.get('ipv4'), 'ip6': pnic.get('ipv6')};
         },
     
         // get actions that the user can execute
@@ -1540,8 +1383,8 @@
         path: 'networks',
         details: true,
         //noUpdate: true,
-        defaults: {'linked_to':[]},
-
+        defaults: {'nics':[],'linked_to':[]},
+        
         parse: function (resp, xhr) {
             // FIXME: depricated global var
             if (!resp) { return []};
@@ -1550,10 +1393,18 @@
             return data;
         },
 
+        add: function() {
+            ret = models.Networks.__super__.add.apply(this, arguments);
+            // update nics after each network addition
+            ret.each(function(r){
+                synnefo.storage.nics.update_net_nics(r);
+            });
+        },
+
         reset_pending_actions: function() {
             this.each(function(net) {
                 net.get("actions").reset();
-            })
+            });
         },
 
         do_all_pending_actions: function() {
@@ -1563,14 +1414,56 @@
         },
 
         parse_net_api_data: function(data) {
-            if (data.servers && data.servers.values) {
-                data['linked_to'] = data.servers.values;
+            // append nic metadata
+            // net.get('nics') contains a list of vm/index objects 
+            // e.g. {'vm_id':12231, 'index':1}
+            // net.get('linked_to') contains a list of vms the network is 
+            // connected to e.g. [1001, 1002]
+            if (data.attachments && data.attachments.values) {
+                data['nics'] = {};
+                data['linked_to'] = [];
+                _.each(data.attachments.values, function(nic_id){
+                  
+                  var vm_id = NIC_REGEX.exec(nic_id)[1];
+                  var nic_index = parseInt(NIC_REGEX.exec(nic_id)[2]);
+
+                  if (vm_id !== undefined && nic_index !== undefined) {
+                      data['nics'][nic_id] = {
+                          'vm_id': vm_id, 
+                          'index': nic_index, 
+                          'id': nic_id
+                      };
+                      if (data['linked_to'].indexOf(vm_id) == -1) {
+                        data['linked_to'].push(vm_id);
+                      }
+                  }
+                });
             }
             return data;
         },
 
-        create: function (name, callback) {
-            return this.api_call(this.path, "create", {network:{name:name}}, callback);
+        create: function (name, type, cidr, dhcp, callback) {
+            var params = {
+                network:{
+                    name:name
+                }
+            };
+
+            if (type) {
+                params.network.type = type;
+            }
+            if (cidr) {
+                params.network.cidr = cidr;
+            }
+            if (dhcp) {
+                params.network.dhcp = dhcp;
+            }
+
+            if (dhcp === false) {
+                params.network.dhcp = false;
+            }
+            
+            return this.api_call(this.path, "create", params, callback);
         }
     })
 
@@ -1588,7 +1481,11 @@
         // api url
         update_unknown_id: function(id, callback) {
             var url = getUrl.call(this) + "/" + id;
-            this.api_call(this.path + "/" + id, this.read_method, {_options:{async:true, skip_api_error:true}}, undefined, 
+            this.api_call(this.path + "/" + id, this.read_method, {
+              _options:{
+                async:true, 
+                skip_api_error:true}
+              }, undefined, 
             _.bind(function() {
                 if (!this.get(id)) {
 		            if (this.fallback_service) {
@@ -1783,7 +1680,7 @@
         path: 'servers',
         details: true,
         copy_image_meta: true,
-        
+
         parse: function (resp, xhr) {
             // FIXME: depricated after refactoring
             var data = resp;
@@ -1791,6 +1688,13 @@
             data = _.filter(_.map(resp.servers.values, _.bind(this.parse_vm_api_data, this)), function(v){return v});
             return data;
         },
+
+        add: function() {
+            ret = models.VMS.__super__.add.apply(this, arguments);
+            ret.each(function(r){
+                synnefo.storage.nics.update_vm_nics(r);
+            });
+        },
         
         get_reboot_required: function() {
             return this.filter(function(vm){return vm.get("reboot_required") == true})
@@ -1860,14 +1764,23 @@
                 data['OS'] = data.metadata.values.OS || "okeanos";
             }
             
+
+            // network metadata
             data['firewalls'] = {};
-            if (data['addresses'] && data['addresses'].values) {
-                data['linked_to_nets'] = data['addresses'].values;
-                _.each(data['addresses'].values, function(f){
-                    if (f['firewallProfile']) {
-                        data['firewalls'][f['id']] = f['firewallProfile']
+            data['nics'] = {};
+            data['linked_to'] = [];
+
+            if (data['attachments'] && data['attachments'].values) {
+                var nics = data['attachments'].values;
+                _.each(nics, function(nic) {
+                    var net_id = nic.network_id;
+                    var index = parseInt(NIC_REGEX.exec(nic.id)[2]);
+                    if (data['linked_to'].indexOf(net_id) == -1) {
+                        data['linked_to'].push(net_id);
                     }
-                });
+
+                    data['nics'][nic.id] = nic;
+                })
             }
             
             // if vm has no metadata, no metadata object
@@ -1894,6 +1807,145 @@
         }
 
     })
+    
+    models.NIC = models.Model.extend({
+        
+        initialize: function() {
+            models.NIC.__super__.initialize.apply(this, arguments);
+            this.pending_for_firewall = false;
+            this.bind("change:firewallProfile", _.bind(this.check_firewall, this));
+            this.bind("change:pending_firewall", function(nic) {
+                nic.get_network().update_state();
+            });
+            this.get_vm().bind("remove", function(){
+                this.collection.remove(this);
+            }, this);
+            this.get_network().bind("remove", function(){
+                try {
+                    this.collection.remove(this);
+                } catch (err) {};
+            }, this);
+        },
+
+        get_vm: function() {
+            return synnefo.storage.vms.get(parseInt(this.get('vm_id')));
+        },
+
+        get_network: function() {
+            return synnefo.storage.networks.get(this.get('network_id'));
+        },
+
+        get_v6_address: function() {
+            return this.get("ipv6");
+        },
+
+        get_v4_address: function() {
+            return this.get("ipv4");
+        },
+
+        set_firewall: function(value, callback, error, options) {
+            var net_id = this.get('network_id');
+            var self = this;
+
+            // api call data
+            var payload = {"firewallProfile":{"profile":value}};
+            payload._options = _.extend({critical: false}, options);
+            
+            this.set({'pending_firewall': value});
+            this.set({'pending_firewall_sending': true});
+
+            var success_cb = function() {
+                if (callback) {
+                    callback();
+                }
+                self.set({'pending_firewall_sending': false});
+            };
+
+            var error_cb = function() {
+                self.reset_pending_firewall();
+            }
+            
+            this.get_vm().api_call(this.get_vm().api_path() + "/action", "create", payload, success_cb, error_cb);
+        },
+
+        reset_pending_firewall: function() {
+            this.set({'pending_firewall': false});
+        },
+
+        check_firewall: function() {
+            var firewall = this.get('firewallProfile');
+            var pending = this.get('pending_firewall');
+            this.reset_pending_firewall();
+        }
+        
+    });
+
+    models.NICs = models.Collection.extend({
+        model: models.NIC,
+        
+        add_or_update: function(nic_id, data, vm) {
+            var params = _.clone(data);
+            params.attachment_id = params.id;
+            params.id = params.id + '-' + params.network_id;
+            params.vm_id = parseInt(NIC_REGEX.exec(nic_id)[1]);
+
+            if (!this.get(params.id)) {
+                this.add(params);
+                var nic = this.get(params.id);
+                nic.get_network().decrease_connecting();
+                nic.bind("remove", function() {
+                    nic.set({"removing": 0});
+                    if (this.get_network()) {
+                        // network might got removed before nic
+                        nic.get_network().update_state();
+                    }
+                })
+            } else {
+                this.get(params.id).set(params);
+            }
+        },
+        
+        reset_nics: function(nics, filter_attr, filter_val) {
+            var nics_to_check = this.filter(function(nic) {
+                return nic.get(filter_attr) == filter_val;
+            });
+            
+            _.each(nics_to_check, function(nic) {
+                if (nics.indexOf(nic.get('id')) == -1) {
+                    this.remove(nic);
+                } else {
+                }
+            }, this);
+        },
+
+        update_vm_nics: function(vm) {
+            var nics = vm.get('nics');
+            this.reset_nics(_.map(nics, function(nic, key){
+                return key + "-" + nic.network_id;
+            }), 'vm_id', vm.id);
+
+            _.each(nics, function(val, key) {
+                var net = synnefo.storage.networks.get(val.network_id);
+                if (net && net.connected_with_nic_id(key) && vm.connected_with_nic_id(key)) {
+                    this.add_or_update(key, vm.get('nics')[key], vm);
+                }
+            }, this);
+        },
+
+        update_net_nics: function(net) {
+            var nics = net.get('nics');
+            this.reset_nics(_.map(nics, function(nic, key){
+                return key + "-" + net.get('id');
+            }), 'network_id', net.id);
+
+            _.each(nics, function(val, key) {
+                var vm = synnefo.storage.vms.get(val.vm_id);
+                if (vm && net.connected_with_nic_id(key) && vm.connected_with_nic_id(key)) {
+                    this.add_or_update(key, vm.get('nics')[key], vm);
+                }
+            }, this);
+        }
+    });
 
     models.PublicKey = models.Model.extend({
         path: 'keys',
@@ -1967,6 +2019,7 @@
     snf.storage.networks = new models.Networks();
     snf.storage.vms = new models.VMS();
     snf.storage.keys = new models.PublicKeys();
+    snf.storage.nics = new models.NICs();
 
     //snf.storage.vms.fetch({update:true});
     //snf.storage.images.fetch({update:true});
diff --git a/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_networks_view.js b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_networks_view.js
index 954f8d3..29fc349 100644
--- a/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_networks_view.js
+++ b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_networks_view.js
@@ -109,6 +109,7 @@
             }
 
             _.each(this.vms, _.bind(function(vm){
+                
                 var html = '<li class="vm option options-object vm-{0}">' +
                            '<div class="options-object-cont">' +
                            '{2}' + 
@@ -120,7 +121,7 @@
                                        snf.ui.helpers.vm_icon_tag(vm, "small", {'class':'os'}),
                                        _.escape(vm.get_os())
                                       ))
-                el.data({vm:vm,vm_id:vm.id})
+                el.data({vm:vm, vm_id:vm.id})
                 this.list.append(el);
 
                 vm.bind("remove", function(){ el.remove()})
@@ -142,6 +143,12 @@
             this.reset();
             this.set_subtitle(network.escape("name"));
             this.vms = vms;
+            if (!synnefo.config.network_allow_duplicate_vm_nics) {
+                this.vms = _.filter(this.vms, function(vm) {
+                    return !vm.connected_to(this.network);
+                }, this);
+            }
+
             this.selected = selected;
             this.cb = callback;
             this.show();
@@ -194,10 +201,61 @@
             this.create_button = this.$("form .form-action.create");
             this.text = this.$(".network-create-name");
             this.form = this.$("form");
+
+            this.dhcp_select = this.$("#network-create-dhcp");
+            this.type_select = this.$("#network-create-type");
+            this.subnet_select = this.$("#network-create-subnet");
+            this.subnet_custom = this.$("#network-create-subnet-custom");
+                
+            this.dhcp_form = this.$("#network-create-dhcp-fields");
+            
+            this.subnet_select.find(".subnet").remove();
+            _.each(synnefo.config.network_suggested_subnets, function(subnet){
+                this.subnet_select.append($('<option value='+subnet+' class="subnet">'+subnet+'</option>'));
+            }, this);
+
+            this.type_select.find(".subnet").remove();
+            _.each(synnefo.config.network_available_types, function(name, value){
+                this.type_select.append($('<option value='+value+' class="subnet">'+name+'</option>'));
+            }, this);
+            
+            this.disable_network_type = false;
+            if (_.keys(synnefo.config.network_available_types).length <= 1) {
+                this.disable_network_type = true;
+                this.type_select.closest(".form-field").hide();
+            }
+
+            this.check_dhcp_form();
             this.init_handlers();
         },
 
+        check_dhcp_form: function() {
+            if (this.dhcp_select.is(":checked")) {
+                this.dhcp_form.show();
+            } else {
+                this.dhcp_form.hide();
+            }
+            
+            if (this.subnet_select.val() == "custom") {
+                this.subnet_custom.show();
+            } else {
+                this.subnet_custom.hide();
+            }
+        },
+
         init_handlers: function() {
+
+            this.dhcp_select.click(_.bind(function(e){
+                this.check_dhcp_form();
+            }, this));
+
+            this.subnet_select.change(_.bind(function(e){
+                this.check_dhcp_form();
+                if (this.subnet_custom.is(":visible")) {
+                    this.subnet_custom.focus();
+                }
+            }, this));
+
             this.create_button.click(_.bind(function(e){
                 this.submit();
             }, this));
@@ -230,13 +288,49 @@
                 this.text.focus();
                 return false;
             } else {
-                return true;
+                this.text.closest(".form-field").removeClass("error");
             }
+            
+            if (this.dhcp_select.is(":checked")) {
+                if (this.subnet_select.val() == "custom") {
+                    var sub = this.subnet_custom.val();
+                    sub = sub.replace(/^\s+|\s+$/g,"");
+                    this.subnet_custom.val(sub);
+                        
+                    if (!synnefo.util.IP_REGEX.exec(this.subnet_custom.val())) {
+                        this.subnet_custom.closest(".form-field").prev().addClass("error");
+                        return false;
+                    } else {
+                        this.subnet_custom.closest(".form-field").prev().removeClass("error");
+                    }
+                };
+            }
+
+            return true;
         },
 
         create: function() {
             this.create_button.addClass("in-progress");
-            snf.storage.networks.create(this.text.val(), _.bind(function(){
+
+            var name = this.text.val();
+            var dhcp = this.dhcp_select.is(":checked");
+            var subnet = null;
+            var type = this.type_select.val();
+
+            if (this.disable_network_type) { type = null };
+
+            if (dhcp) {
+                if (this.subnet_select.val() == "custom") {
+                    subnet = this.subnet_custom.val();
+                } else if (this.subnet_select.val() == "auto") {
+                    subnet = null;
+                } else {
+                    subnet = this.subnet_select.val();
+                }
+                
+            }
+
+            snf.storage.networks.create(name, type, subnet, dhcp, _.bind(function(){
                 this.hide();
             }, this));
         },
@@ -247,6 +341,11 @@
             this.text.val("");
             this.text.show();
             this.text.focus();
+            this.subnet_custom.val("");
+            this.subnet_select.val("auto");
+            this.dhcp_select.attr("checked", true);
+            this.type_select.val(_.keys(synnefo.config.network_available_types)[0]);
+            this.check_dhcp_form();
         },
 
         onOpen: function() {
@@ -254,15 +353,16 @@
         }
     });
 
-    views.NetworkVMView = views.View.extend({
+    views.NetworkNICView = views.View.extend({
 
-        initialize: function(vm, parent, firewall_controls, el) {
+        initialize: function(nic, parent, firewall_controls, el) {
             this.firewall_controls = firewall_controls || false;
-            this.vm = vm;
+            this.nic = nic;
+            this.vm = nic.get_vm();
             // parent view di
             this.parent = parent;
             // TODO make it better
-            this.el = el || this.parent.vm(vm);
+            this.el = el || this.parent.get_nic_view(nic);
 
             this.init_layout();
             this.update_layout();
@@ -279,25 +379,37 @@
             
             this.firewall_view = undefined;
             if (this.firewall_controls) {
-                this.firewall_view = new views.FirewallEditView(this.vm, this.parent.network, this);
+                this.firewall_view = new views.FirewallEditView(this.nic, this.parent.network, this);
             }
 
         },
         
+        reset_all_net_actions: function(act_types) {
+            synnefo.storage.networks.each(function(n){
+                var actions = n.get('actions');
+                _.each(act_types, function(type){
+                    actions.remove_all(type);
+                })
+            })
+        },
+
         init_handlers: function() {
             if (!this.parent.network.is_public()) {
                 this.disconnect.click(_.bind(function(e){
                     e.preventDefault();
-                    this.parent.network.get("actions").add("disconnect", this.vm.id);
+                    this.reset_all_net_actions(['destroy','disconnect']);
+                    this.parent.network.get("actions").remove_all("disconnect");
+                    this.parent.network.get("actions").add("disconnect", this.nic.id);
                     this.parent.network.get("actions").remove("destroy");
                 }, this));
                 this.cancel.click(_.bind(function(e){
-                    this.parent.network.get("actions").remove("disconnect", this.vm.id);
+                    this.parent.network.get("actions").remove("disconnect", this.nic.id);
                     e.preventDefault()
                 }, this));
+
                 this.confirm.click(_.bind(function(e){
                     e.preventDefault()
-                    this.disconnect_vm();
+                    this.disconnect_nic();
                     this.confirm_el.hide();
                     this.disconnect.removeClass("selected");
                 }, this));
@@ -309,7 +421,9 @@
                 }, this));
 
                 this.$(".remove-icon").click(_.bind(function(){
-                    this.parent.network.get("actions").add("disconnect", this.vm.id);
+                    this.reset_all_net_actions(['destroy','disconnect']);
+                    this.parent.network.get("actions").remove_all("disconnect");
+                    this.parent.network.get("actions").add("disconnect", this.nic.id);
                     this.parent.network.get("actions").remove("destroy");
                 }, this));
 
@@ -318,7 +432,7 @@
                 }, this));
                 
                 this.parent.network.bind("change:actions", _.bind(function(model, action){
-                    if (this.parent.network.get("actions").contains("disconnect", this.vm.id)) {
+                    if (this.parent.network.get("actions").contains("disconnect", this.nic.id)) {
                         this.confirm_disconnect();
                     } else {
                         this.cancel_disconnect();
@@ -327,7 +441,8 @@
             }
             
             var vm = this.vm;
-            this.details.click(function(){
+            this.details.click(function(e){
+                e.preventDefault();
                 snf.ui.main.show_vm_details(vm);
             });
 
@@ -354,24 +469,37 @@
             this.$("img.logo").attr("src", ui.helpers.vm_icon_path(this.vm, "medium"));
 
             if (this.firewall_view) {
-                this.$(".ipv4-text").text(this.vm.get_addresses().ip4);
-                this.$(".ipv6-text").text(this.vm.get_addresses().ip6);
+                this.$(".ipv4-text").text(this.nic.get_v4_address());
+                this.$(".ipv6-text").text(this.nic.get_v6_address());
             }
 
             if (this.firewall_view) {
                 this.firewall_view.update_layout();
             }
+
+            if (!this.firewall_view) {
+                this.$(".ip4-container").hide();
+                this.$(".ip6-container").hide();
+                
+                if (this.nic.get("ipv4")) {
+                    this.$(".ipv4-text").text(this.nic.get_v4_address());
+                    this.$(".ip4-container").show();
+                    this.$(".machine-connect .content").hide();
+                } else if (this.nic.get("ipv6")) {
+                    this.$(".ipv6-text").text(this.nic.get_v6_address());
+                    this.$(".ip6-container").show();
+                    this.$(".machine-connect .content").hide();
+                } else {
+                    this.$(".machine-connect .content").show();
+                }
+            } else {
+            }
         },
 
-        disconnect_vm: function() {
+        disconnect_nic: function() {
             this.$("a.selected").removeClass("selected");
-            this.parent.network.remove_vm(this.vm);
+            this.parent.network.remove_nic(this.nic);
         },
-
-        update_firewall_layout: function() {
-        }
-
-
     })
 
     views.NetworkModelRenameView = views.View.extend({
@@ -448,9 +576,10 @@
     })
 
     views.FirewallEditView = views.View.extend({
-        initialize: function(vm, network, parent) {
+        initialize: function(nic, network, parent) {
             this.parent = parent;
-            this.vm = vm;
+            this.vm = nic.get_vm();
+            this.nic = nic;
             this.network = network;
             this.el = this.parent.el;
 
@@ -467,11 +596,29 @@
 
             this.$(".firewall-content").hide();
             this.$(".firewall-content input[type=radio]").attr("name", "firewall-opt-for-{0}".format(this.vm.id))
-            var mode = this.vm.firewall_profile(this.network.id);
+            var mode = this.vm.get_firewall_profile();
             this.$(".firewall-content input[value={0}]".format(mode)).attr("checked", true);
 
             this.init_handlers();
             this.update_layout();
+
+            var self = this;
+            this.nic.bind("change:pending_firewall_sending", function(nic, value) {
+                if (value) {
+                    self.apply.addClass("in-progress");       
+                    self.progress.show();
+                } else {
+                    self.apply.removeClass("in-progress");       
+                    self.progress.hide();
+                    self.toggler.click();
+                }
+            });
+
+            this.nic.bind("change:firewallProfile", function(nic){
+                self.update_layout();
+                self.reset_value();
+            })
+
         },
         
         _get_selected: function() {
@@ -485,7 +632,8 @@
         },
 
         reset_value: function() {
-            this.inputs.filter("[value={0}]".format(this.vm.firewall_profile(this.network.id))).attr("checked");
+            this.inputs.filter("[value={0}]".format(
+              this.nic.get('firewallProfile'))).attr("checked", true);
         },
 
         init_handlers: function() {
@@ -502,20 +650,7 @@
             }, this))
             
             this.apply.click(_.bind(function(){
-                this.apply.addClass("in-progress");
-                
-                // make the api call
-                this.vm.set_firewall(this.network.id, this.value(), 
-                // complete
-                _.bind(function() {
-                    // complete callback
-                    this.apply.removeClass("in-progress");
-                }, this), 
-                // error
-                _.bind(function(){
-                    this.vm.remove_pending_firewall(this.network.id, this.value());
-                }, this));
-                this.hide_firewall();
+                this.nic.set_firewall(this.value());
             }, this))
 
             this.inputs.change(_.bind(function(){
@@ -549,13 +684,13 @@
         },
 
         update_layout: function() {
-            if (this.value() == this.vm.firewall_profile(this.network.id)) {
+            if (this.value() == this.vm.get_firewall_profile()) {
                 this.apply.hide();
             } else {
                 this.apply.show();
             }
 
-            profile = this.vm.firewall_profile(this.network.id);
+            var profile = this.vm.get_firewall_profile();
             if (this.vm.has_firewall(this.network.id)) {
                 this.$(".firewall-toggle .label span").text("On");
                 this.$(".firewall-toggle .label span").removeClass("firewall-off");
@@ -567,18 +702,8 @@
             }
             
             this.$("span.checkbox-legends").removeClass("current");
-            this.inputs.filter("[value={0}]".format(this.vm.firewall_profile(this.network.id))).next().addClass("current");
-
-            var firewalling = this.vm.firewall_pending(this.network.id);
-            var el = this.el;
+            this.inputs.filter("[value={0}]".format(profile)).next().addClass("current");
             
-            if (firewalling) {
-                el.find("button").addClass("in-progress").show();
-                el.find(".network-progress-indicator").show();
-            } else {
-                el.find("button").removeClass("in-progress");
-                el.find(".network-progress-indicator").hide();
-            }
         }
     })
 
@@ -591,93 +716,77 @@
             this.network = network;
             this.is_public = network.is_public();
 
-            this.init_vm_handlers();
+            this.init_nics_handlers();
 
             this.view_id = "networks_view_" + network.id;
             views.NetworkModelView.__super__.initialize.call(this);
 
-            this.vm_views = {};
+            this.nics_views = {};
 
             this.el = this.create_el();
 
             // element helpers
-            this.vms_list = this.$(".machines-list");
-            this.vms_list_toggler = this.$(".list-toggle");
+            this.nics_list = this.$(".machines-list");
+            this.nics_list_toggler = this.$(".list-toggle");
             
             this.init_handlers();
-            this.update_vms();
+            this.update_nics();
             this.update_layout();
 
-            this.hide_vm_list();
-            this.vms_list.hide();
+            this.hide_nics_list();
+            this.nics_list.hide();
 
             this.rename_view = undefined;
             if (!this.network.is_public()) {
+                // allow network rename for non public networks only
                 this.rename_view = new views.NetworkModelRenameView(this, network);
             }
+            
+            var self = this;
+            this.network.bind('change:status', function() {
+                self.update_layout();
+            })
+        },
+
+        init_nics_handlers: function() {
+            storage.nics.bind("add", _.bind(this.nic_added_handler, this, "add"));
+            storage.nics.bind("change", _.bind(this.nic_changed_handler, this, "change"));
+            storage.nics.bind("reset", _.bind(this.nic_changed_handler, this, "reset"));
+            storage.nics.bind("remove", _.bind(this.nic_removed_handler, this, "remove"));
         },
 
-        show_vm_list: function() {
-            if (this.vms_empty()) { return }
-            this.vms_list_toggler.addClass("open");
-            this.vms_list.slideDown(function(){
+
+        show_nics_list: function() {
+            if (this.nics_empty()) { return }
+            this.nics_list_toggler.addClass("open");
+            this.nics_list.slideDown(function(){
                 $(window).trigger("resize");
             }).closest(".network").addClass("expand");
             this.$(".empty-network-slot").show();
-            this.vms_visible = true;
+            this.nics_visible = true;
         },
 
-        hide_vm_list: function() {
-            this.vms_list_toggler.removeClass("open");
-            this.vms_list.slideUp(function(){
+        hide_nics_list: function() {
+            this.nics_list_toggler.removeClass("open");
+            this.nics_list.slideUp(function(){
                 $(window).trigger("resize");
             }).closest(".network").removeClass("expand");
             this.$(".empty-network-slot").hide();
-            this.vms_visible = false;
+            this.nics_visible = false;
         },
         
-        // fix left border position
-        fix_left_border: function() {
-            if (!this.vms_visible) { return };
-            
-            var imgheight = 2783;
-            var opened_vm_height = 133 + 20;
-            var closed_vm_height = 61 + 20;
-            var additional_height = 25;
-
-            if (!this.is_public) { 
-                imgheight = 2700;
-                additional_height = 65;
-            };
-            
-            var contents = this.$(".network-contents");
-            var last_vm = this.$(".network-machine:last .cont-toggler.open").length;
-            var last_vm_height = closed_vm_height;
-            if (last_vm > 0){
-                last_vm_height = opened_vm_height;
-            }
-
-            var vms_opened = this.$(".network-machine .cont-toggler.open").length;
-            var vms_closed = this.$(".network-machine").length - vms_opened;
-
-            var calc_height = (vms_opened * opened_vm_height) + (vms_closed * closed_vm_height) + additional_height; 
-            var bgpos = imgheight - calc_height + last_vm_height - 30;
-            this.$(".network-contents").css({'background-position':'33px ' + (-bgpos) + 'px'});
-        },
-
-
         init_handlers: function() {
             var self = this;
 
-            this.vms_list_toggler.click(_.bind(function(){
-                if (this.vms_list.is(":visible")) {
-                    this.hide_vm_list();
+            this.nics_list_toggler.click(_.bind(function(){
+                if (this.nics_list.is(":visible")) {
+                    this.hide_nics_list();
                 } else {
                     this.fix_left_border();
-                    this.show_vm_list();
+                    this.show_nics_list();
                 }
 
-                this.check_empty_vms();
+                this.check_empty_nics();
             }, this));
 
             this.$(".action-add").click(_.bind(function(e){
@@ -693,6 +802,12 @@
 
             this.$(".net-actions .destroy a").click(_.bind(function(e){
                 e.preventDefault();
+                synnefo.storage.networks.each(function(n) {
+                    n.get('actions').remove_all("disconnect");
+                    if (!synnefo.config.network_allow_multiple_destory) {
+                        n.get('actions').remove_all("destroy");
+                    }
+                });
                 self.network.get("actions").add("destroy");
                 self.network.get("actions").remove_all("disconnect");
             }, this));
@@ -762,117 +877,110 @@
         },
 
         create_el: function() {
-            var el = this.$(this.tpl).clone().attr("id", "network-" + this.network.id)
+            var el = this.$(this.tpl).clone().attr("id", "network-" + this.network.id);
             return el;
         },
 
-        init_vm_handlers: function() {
-            storage.vms.bind("add", _.bind(this.vm_added_handler, this, "add"));
-            storage.vms.bind("network:connect", _.bind(this.vm_changed_handler, this, "connect"));
-            storage.vms.bind("change", _.bind(this.vm_changed_handler, this, "change"));
-            storage.vms.bind("reset", _.bind(this.vm_changed_handler, this, "reset"));
-            storage.vms.bind("remove", _.bind(this.vm_removed_handler, this, "remove"));
-            storage.vms.bind("network:disconnect", _.bind(this.vm_removed_handler, this, "disconnect"));
+        get_nic_id: function(nic) {
+            return this.nic_id_tpl.format(nic.id);
         },
 
-        get_vm_id: function(vm) {
-            return this.vm_id_tpl.format(this.network.id, vm.id)
+        get_nic_view: function(nic) {
+            return $(this.get_nic_id(nic));
         },
 
-        vm: function(vm) {
-            return $(this.get_vm_id(vm))
-        },
-
-        vm_added_handler: function(action, vm) {
-            if (!this.network.contains_vm(vm)) { return }
-            this.add_or_update_vm(vm);
+        nic_added_handler: function(action, nic) {
+            if (nic.get_network().id != this.network.id) { return };
+            this.add_or_update_nic(nic);
             this.update_layout();
             this.fix_left_border();
         },
 
-        vm_changed_handler: function(action, vms, model, changes) {
-            var vms = vms || [];
-            // reset or change
+        nic_changed_handler: function(action, nics, model, changes) {
+            var nics = nics || [];
+
+            // reset or update
             if (action == "reset") {
-                vms = vms;
+                nics = nics;
             } else {
-                if (!_.isArray(vms)) {
-                    vms = [vms]
+                if (!_.isArray(nics)) {
+                    nics = [nics]
                 }
             }
-
-            if (action == "connect") {
-                vms = [model];
-            }
             
-            _.each(vms, _.bind(function(vm) {
-                if (!this.network.contains_vm(vm)) { return }
-                this.add_or_update_vm(vm);
+            _.each(nics, _.bind(function(nic) {
+                if (nic.get_network().id != this.network.id) { return };
+                this.add_or_update_nic(nic);
             }, this));
+
             this.update_layout();
         },
 
-        vm_removed_handler: function(action, vm, model) {
-            if (action == "disconnect") { vm = model };
+        nic_removed_handler: function(action, nic, model) {
+            if (nic.get_network().id != this.network.id) { return };
             this.fix_left_border();
-            this.remove_vm(vm);
+            this.remove_nic(nic);
             this.update_layout();
         },
 
-        remove_vm: function(vm) {
-            if (this.vm(vm).length) {
-                this.vm(vm).remove();
+        remove_nic: function(nic) {
+            var nic_view = this.get_nic_view(nic);
+            if (nic_view.length) {
+                nic_view.remove();
                 try {
-                    delete this.vm_views[vm.id]
+                    delete this.nics_views[nic.id]
                 } catch (err) {
                 }
             }
         },
         
-        create_vm: function(vm) {
-            vm_el = $(this.vm_tpl).clone().attr({id:this.get_vm_id(vm).replace("#","")});
-            this.vms_list.append(vm_el);
-            this.post_vm_add(vm);
+        create_nic_view: function(nic) {
+            var nic_el = $(this.nic_tpl).clone().attr({
+                id: this.get_nic_id(nic).replace("#","")
+            });
+            this.nics_list.append(nic_el);
+            this.post_nic_add(nic);
 
-            if (!this.vm_views[vm.id]) {
-                vm_view = this.vm_views[vm.id] = new views.NetworkVMView(vm, this, this.firewall, vm_el);
+            if (!this.nics_views[nic.id]) {
+                var nic_view = this.nics_views[nic.id] = new views.NetworkNICView(nic, this, this.firewall, nic_el);
             }
         },
 
-        add_or_update_vm: function(vm) {
-            if (!vm || !this.network.contains_vm(vm)) { return };
-
-            var vm_el = this.vm(vm);
-            var vm_view = this.vm_views[vm.id];
+        add_or_update_nic: function(nic) {
+            if (!nic) { return };
+                
+            var nic_el = this.get_nic_view(nic);
+            var nic_view = this.nics_views[nic.id];
 
-            if (vm_el.length == 0) {
-                this.create_vm(vm);
+            if (nic_el.length == 0) {
+                nic_view = this.create_nic_view(nic);
             }
             
-            if (vm_view) { vm_view.update_layout() };
+            if (nic_view) { nic_view.update_layout() };
 
-            this.update_vm(vm);
-            this.post_vm_update(vm);
+            this.update_nic(nic);
+            this.post_nic_update(nic);
         },
-        update_vm: function(vm){},
-        post_vm_add: function(vm){},
-        post_vm_update: function(vm){},
 
-        update_vms: function(vms) {
-            if (!vms) { vms = this.network.vms.list() };
-            _.each(vms, _.bind(function(vm){
-                this.add_or_update_vm(vm);
+        update_nic: function(vm){},
+        post_nic_add: function(vm){},
+        post_nic_update: function(vm){},
+
+        update_nics: function(nics) {
+            if (!nics) { nics = this.network.get_nics() };
+            _.each(nics, _.bind(function(nic){
+                this.add_or_update_nic(nic);
             }, this));
         },
 
-        check_empty_vms: function() {
-            if (this.network.vms.get().length == 0) {
-                this.hide_vm_list();
+        check_empty_nics: function() {
+            if (this.network.get_nics() == 0) {
+                this.hide_nics_list();
             }
         },
 
-        vms_empty: function() {
-            return this.network.vms.get().length == 0;
+        nics_empty: function() {
+            return this.network.get_nics().length == 0;
         },
 
         remove: function() {
@@ -881,13 +989,13 @@
 
         update_layout: function() {
             // has vms ???
-            this.check_empty_vms(this.network.vms.list());
+            this.check_empty_nics(this.network.get_nics());
 
             // is expanded ???
             //
             // whats the network status ???
             //
-            this.$(".machines-count").text(this.network.vms.get().length);
+            this.$(".machines-count").text(this.network.get_nics().length);
 
             var net_name = this.network.get("name");
             if (net_name == "public") { net_name = "Internet" }
@@ -906,19 +1014,71 @@
                 this.$(".spinner").hide();
                 this.$(".network-indicator").removeClass("in-progress");
             }
+                
+            if (this.network.get('state') == 'PENDING') {
+                this.el.addClass("pending");
+            } else {
+                this.el.removeClass("pending");
+            }
+
+            if (this.network.get('state') == 'ERROR') {
+                this.el.addClass("in-error");
+                this.$(".network-indicator").addClass("error-state");
+            } else {
+                this.el.removeClass("in-error");
+                this.$(".network-indicator").removeClass("error-state");
+            }
+
+            if (synnefo.config.network_strict_destroy) {
+                if (this.network.get_nics().length == 0) {
+                    this.el.removeClass("disable-destroy");
+                } else {
+                    this.el.addClass("disable-destroy");
+                }
+            }
 
             if (this.network.get("state") == "DESTROY") {
                 this.$(".spinner").show();
                 this.$(".state").addClass("destroying-state");
+                this.$(".actions").hide();
+            }
+        },
+
+        // fix left border position
+        fix_left_border: function() {
+            if (!this.nics_visible) { return };
+            
+            var imgheight = 2783;
+            var opened_vm_height = 133 + 20;
+            var closed_vm_height = 61 + 20;
+            var additional_height = 25;
+
+            if (!this.is_public) { 
+                imgheight = 2700;
+                additional_height = 65;
+            };
+            
+            var contents = this.$(".network-contents");
+            var last_vm = this.$(".network-machine:last .cont-toggler.open").length;
+            var last_vm_height = closed_vm_height;
+            if (last_vm > 0){
+                last_vm_height = opened_vm_height;
             }
+
+            var nics_opened = this.$(".network-machine .cont-toggler.open").length;
+            var nics_closed = this.$(".network-machine").length - nics_opened;
+
+            var calc_height = (nics_opened * opened_vm_height) + (nics_closed * closed_vm_height) + additional_height; 
+            var bgpos = imgheight - calc_height + last_vm_height - 30;
+            this.$(".network-contents").css({'background-position':'33px ' + (-bgpos) + 'px'});
         }
     })
 
     views.PublicNetworkView = views.NetworkModelView.extend({
         firewall: true,
         tpl: "#public-template",
-        vm_tpl: "#public-machine-template",
-        vm_id_tpl: "#network-{0}-vm-{1}",
+        nic_tpl: "#public-nic-template",
+        nic_id_tpl: "#nic-{0}",
 
         update_vm: function(vm) {
         }
@@ -926,8 +1086,8 @@
     
     views.PrivateNetworkView = views.NetworkModelView.extend({
         tpl: "#private-template",
-        vm_tpl: "#private-machine-template",
-        vm_id_tpl: "#network-{0}-vm-{1}"
+        nic_tpl: "#private-nic-template",
+        nic_id_tpl: "#nic-{0}"
     })
 
     views.NetworksView = views.View.extend({
@@ -942,15 +1102,12 @@
             this.container = this.$("#networks-container");
             this.public_list = this.$(".public-networks");
             this.private_list = this.$(".private-networks");
-
             views.NetworksView.__super__.initialize.call(this);
             this.init_handlers();
             this.network_views = {};
             this.update_networks(storage.networks.models);
-
             this.create_view = new views.NetworkCreateView();
             this.connect_machines_view = new views.NetworkConnectVMsOverlay();
-
         },
         
         exists: function(net) {
@@ -960,7 +1117,7 @@
         add_or_update: function(net) {
             var nv = this.exists(net);
             if (!nv) {
-                nv = this.create_network_view(net)
+                nv = this.create_network_view(net);
                 this.network_views[net.id] = nv;
                 
                 if (net.is_public()) {
@@ -977,7 +1134,7 @@
             // get updated after vm addition and
             // vm_added_handler fails to append the
             // vm to the list
-            nv.update_vms();
+            nv.update_nics();
             nv.update_layout();
         },
         
@@ -1003,7 +1160,7 @@
 
         update_networks: function(nets) {
             _.each(nets, _.bind(function(net){
-                if (net.get("status") == "DELETED") { return }
+                if (net.get("status") == "DELETED") { return };
                 view = this.add_or_update(net);
             }, this));
         },
diff --git a/snf-cyclades-app/synnefo/ui/static/snf/js/utils.js b/snf-cyclades-app/synnefo/ui/static/snf/js/utils.js
index abff914..0905eb0 100644
--- a/snf-cyclades-app/synnefo/ui/static/snf/js/utils.js
+++ b/snf-cyclades-app/synnefo/ui/static/snf/js/utils.js
@@ -224,6 +224,9 @@
         return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
     }
     
+
+    synnefo.util.IP_REGEX = /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/([0-9]|[1-2][0-9]|3[0-2]?)$/
+
     synnefo.i18n.API_ERROR_MESSAGES = {
         'timeout': {
             'message': 'TIMEOUT', 
diff --git a/snf-cyclades-app/synnefo/ui/templates/home.html b/snf-cyclades-app/synnefo/ui/templates/home.html
index c981bee..01a9403 100644
--- a/snf-cyclades-app/synnefo/ui/templates/home.html
+++ b/snf-cyclades-app/synnefo/ui/templates/home.html
@@ -593,6 +593,12 @@ We welcome any suggestions, questions and bug reports you may have.{% endblocktr
             synnefo.config.logout_redirect = '{{ logout_redirect }}';
             synnefo.config.login_redirect = '{{ login_redirect }}';
             synnefo.config.auth_cookie_name = '{{ auth_cookie_name }}';
+
+            synnefo.config.network_suggested_subnets = {{ network_suggested_subnets|safe }};
+            synnefo.config.network_available_types = {{ network_available_types|safe }};
+            synnefo.config.network_allow_duplicate_vm_nics = {{ network_allow_duplicate_vm_nics }};
+            synnefo.config.network_strict_destroy = {{ network_strict_destroy }};
+            synnefo.config.network_allow_multiple_destory = {{ network_allow_multiple_destroy }};
             
             synnefo.auth_client = new synnefo.auth.AstakosClient({
                 login_url: synnefo.config.login_redirect,
@@ -615,7 +621,6 @@ We welcome any suggestions, questions and bug reports you may have.{% endblocktr
             synnefo.ui.init();
             synnefo.ui.main.bind("ready", function(){
             });
-
         })
     </script>
 </body>
diff --git a/snf-cyclades-app/synnefo/ui/templates/partials/networks.html b/snf-cyclades-app/synnefo/ui/templates/partials/networks.html
index f9b1cf1..535791a 100644
--- a/snf-cyclades-app/synnefo/ui/templates/partials/networks.html
+++ b/snf-cyclades-app/synnefo/ui/templates/partials/networks.html
@@ -27,11 +27,42 @@
     <div class="create-form">
         <p class="info"></p>
         <form>
+          <div class="col-fields bordered clearfix">
             <div class="form-field">
-                <label for="">Network name:</label>
-                <input type="text" class="network-create-name" />
+                <label for="network-create-name">Network name:</label>
+                <input type="text" class="network-create-name" name="network-create-name" id="network-create-name" />
             </div>
-
+            <div class="form-field right-field">
+                <label for="network-create-type">Network type:</label>
+              <select id="network-create-type">
+              </select>
+            </div>
+          </div>
+          <div class="col-fields clearfix bordered">
+            <div class="form-field fixpos">
+                <label for="network-create-dhcp">Automatically assign ips:</label>
+                <input type="checkbox" class="use-dhcp" name="network-create-dhcp"
+                  id="network-create-dhcp" checked />
+                  <p class="description noborder fields-desc">
+                  {% blocktrans %}If you enable DHCP on the private network,
+                  connected virtual machines will automatically be assigned with 
+                  an IP address based on the given subnet. You may disable this option
+                  if you want to manually assign IP addresses on the connected
+                  virtual machines. {% endblocktrans %}
+            </p>
+            </div>
+            <div class="form-field fixpos" id="network-create-dhcp-fields">
+              <label for="network-type">Network subnet:</label>
+              <select id="network-create-subnet">
+                <option selected class="auto" value="auto">Auto</option>
+                <option value="custom" class="manual">Manual...</option>
+              </select>
+            </div>
+            <div class="form-field">
+              <label class="hidden" for="network-custom-subnet">Custom subnet:</label>
+              <input type="text" id="network-create-subnet-custom"/>
+            </div>
+          </div>
             <div class="form-actions plain clearfix">
                 <span class="form-action create">{% trans "create network" %}</span>
             </div>
@@ -45,7 +76,7 @@
 <div id="networks-container" class="clearfix">
     <!-- spinner while loading list -->
     
-    <div class="templates">
+    <div class="templates hidden">
     <!-- public network template -->
     <div class="network" id="public-template">
         <img class="network-logos" src="{{ SYNNEFO_IMAGES_URL }}internet.png" />
@@ -102,7 +133,7 @@
     </div>
 
     <!-- template for machines in public network -->
-    <div class="network-machine" id="public-machine-template">
+    <div class="network-machine" id="public-nic-template">
         <div class="machine-actions vm-actions">
             <a href="#" class="action-details">{% trans "Details" %}</a>
         </div>
@@ -200,7 +231,9 @@
                         <span class="cancel"></span>
                     </div>
                 </h5>
-            </div>
+              </div>
+              <div class="cidr">
+              </div>
             <div class="network-machines">
                 <div class="cont-toggler list-toggle">
                     <span class="label machines-label">{% trans "machines" %}
@@ -222,7 +255,7 @@
     </div>
 
     <!-- template for machines in private network -->
-    <div class="network-machine" id="private-machine-template">
+    <div class="network-machine" id="private-nic-template">
         <div class="machine-actions vm-actions net-vm-actions">
             <div class="action-destroy">
                 <a href="#" class="action-disconnect">{% trans "Disconnect" %}</a>
@@ -244,10 +277,22 @@
         <div href="#" class="machine-name-div">
             <h5 class="namecontainer editable">
                 <span class="name vm-name">my desktop1</span>
-            </h5>
+              </h5>
+        </div>
+        <div class="ips">
+            <div class="ip4-container status">
+                {% trans "IPv4" %}:
+                <span class="ip4 ipv4-text">192.94.73.15</span>
+            </div>
+            <div class="ip6-container status">
+                {% trans "IPv6" %}:
+                <span class="ip6 ipv6-text"></span>
+            </div>
         </div>
         <h5 class='machine-connect'>
+          <span class="content">
             <span>{% trans "Connect" %}</span> {% trans "to manage private IPs" %}
+          </span>
         </h5>
     </div>
     </div>
diff --git a/snf-cyclades-app/synnefo/ui/views.py b/snf-cyclades-app/synnefo/ui/views.py
index c0082c5..64947f3 100644
--- a/snf-cyclades-app/synnefo/ui/views.py
+++ b/snf-cyclades-app/synnefo/ui/views.py
@@ -120,6 +120,20 @@ GLANCE_API_URL = getattr(settings, 'UI_GLANCE_API_URL', '/glance')
 FEEDBACK_CONTACTS = getattr(settings, "FEEDBACK_CONTACTS", [])
 FEEDBACK_EMAIL_FROM = settings.FEEDBACK_EMAIL_FROM
 
+# network settings
+DEFAULT_NETWORK_TYPES = {'PRIVATE_FILTERED': 'mac-filtering'}
+NETWORK_TYPES = getattr(settings,
+                    'UI_NETWORK_AVAILABLE_NETWORK_TYPES', DEFAULT_NETWORK_TYPES)
+DEFAULT_NETWORK_SUBNETS = ['10.0.0.0/24', '192.168.1.1/24']
+NETWORK_SUBNETS = getattr(settings,
+                    'UI_NETWORK_AVAILABLE_SUBNETS', DEFAULT_NETWORK_SUBNETS)
+NETWORK_DUPLICATE_NICS = getattr(settings,
+                    'UI_NETWORK_ALLOW_DUPLICATE_VM_NICS', False)
+NETWORK_STRICT_DESTROY = getattr(settings,
+                    'UI_NETWORK_STRICT_DESTROY', False)
+NETWORK_ALLOW_MULTIPLE_DESTROY = getattr(settings,
+                    'UI_NETWORK_ALLOW_MULTIPLE_DESTROY', False)
+
 def template(name, request, context):
     template_path = os.path.join(os.path.dirname(__file__), "templates/")
     current_template = template_path + name + '.html'
@@ -172,7 +186,12 @@ def home(request):
                'system_images_owners': json.dumps(SYSTEM_IMAGES_OWNERS),
                'image_deleted_title': json.dumps(IMAGE_DELETED_TITLE),
                'image_deleted_size_title': json.dumps(IMAGE_DELETED_SIZE_TITLE),
-               }
+               'network_suggested_subnets': json.dumps(NETWORK_SUBNETS),
+               'network_available_types': json.dumps(NETWORK_TYPES),
+               'network_allow_duplicate_vm_nics': json.dumps(NETWORK_DUPLICATE_NICS),
+               'network_strict_destroy': json.dumps(NETWORK_STRICT_DESTROY),
+               'network_allow_multiple_destroy': json.dumps(NETWORK_ALLOW_MULTIPLE_DESTROY)
+    }
     return template('home', request, context)
 
 def machines_console(request):
