(function(cloudStack, $, testData) {
  var actionFilters = {
    ipAddress: function(args) {
      var allowedActions = args.context.actions;
      var disallowedActions = [];
      var item = args.context.item;
      var status = item.state;
      if (status == 'Destroyed' ||
          status == 'Releasing' ||
          status == 'Released' ||
          status == 'Creating' ||
          status == 'Allocating' ||
          item.account == 'system') {
        disallowedActions = allowedActions;
      }
      if (item.isstaticnat) {
        disallowedActions.push('enableStaticNAT');
      } else {
        disallowedActions.push('disableStaticNAT');
      }
      if (item.vpnenabled) {
        disallowedActions.push('enableVPN');
      } else {
        disallowedActions.push('disableVPN');
      }
      if (item.issourcenat){
        disallowedActions.push('enableStaticNAT');
        disallowedActions.push('disableStaticNAT');
        disallowedActions.push('destroy');
      } else {
        disallowedActions.push('enableVPN');
        disallowedActions.push('disableVPN');
      }
      allowedActions = $.grep(allowedActions, function(item) {
        return $.inArray(item, disallowedActions) == -1;
      });
      return allowedActions;
    }
  };
  cloudStack.sections.network = {
    title: 'Network',
    id: 'network',
    sectionSelect: {
      preFilter: function(args) {
        if (g_directAttachSecurityGroupsEnabled == "true") {
          return args.context.sections;
        }
        else {
          return ['networks', 'ipAddresses'];
        }
      },
      label: 'Select view'
    },
    sections: {
      networks: {
        type: 'select',
        title: 'Networks',
        listView: {
          filters: {
            all: { label: 'All' },
            mine: { label: 'My network' }
          },
          fields: {
            name: { label: 'Name' },
            traffictype: { label: 'Traffic Type' },
            gateway: { label: 'Gateway' },
            vlan: { label: 'VLAN' }
          },
          dataProvider: function(args) {
            $.ajax({
              url: createURL('listNetworks'),
              data: {
                trafficType: 'Guest'
              },
              dataType: 'json',
              async: true,
              success: function(data) {
                args.response.success({
                  data: data.listnetworksresponse.network
                });
              }
            });
          },
          detailView: {
            name: 'Network details',
            viewAll: { path: 'network.ipAddresses', label: 'IP Addresses' },
            tabs: {
              details: {
                title: 'Details',
                fields: [
                  {
                    name: { label: 'Name' }
                  },
                  {
                    name: { label: 'Short name' },
                    displaytext: { label: 'Description' },
                    traffictype: { label: 'Traffic Type' },
                    gateway: { label: 'Gateway' },
                    vlan: { label: 'VLAN' }
                  },
                  {
                    startip: { label: 'Start IP' },
                    endip: { label: 'End IP' }
                  }
                ],
                dataProvider: function(args) {
                  args.response.success({ data: args.context.networks[0] });
                }
              }
            }
          }
        }
      },
      ipAddresses: {
        type: 'select',
        title: 'IP Addresses',
        listView: {
          id: 'ipAddresses',
          label: 'IPs',
          filters: {
            allocated: { label: 'Allocated ' },
            mine: { label: 'My network' }
          },
          fields: {
            ipaddress: {
              label: 'IP',
              converter: function(text, item) {
                if (item.issourcenat) {
                  return text + ' [Source NAT]';
                }
                return text;
              }
            },
            zonename: { label: 'Zone' },
            vlanname: { label: 'VLAN' },
            account: { label: 'Account' },
            state: { label: 'State', indicator: { 'Allocated': 'on' } }
          },
          actions: {
            add: {
              label: 'Acquire new IP',
              action: function(args) {
                $.ajax({
                  url: createURL('associateIpAddress'),
                  data: {
                    zoneid: args.data.availabilityZone
                  },
                  dataType: 'json',
                  async: true,
                  success: function(data) {
                    args.response.success({
                      _custom: {
                        jobId: data.associateipaddressresponse.jobid,
                        getUpdatedItem: function(data) {
                          var newIP = data.queryasyncjobresultresponse.jobresult.ipaddress;
                          return $.extend(newIP, {
                            state: 'Allocated'
                          });
                        },
                        getActionFilter: function() {
                          return actionFilters.ipAddress;
                        }
                      }
                    });
                  }
                });
              },
              messages: {
                confirm: function(args) {
                  return 'Are you sure you want to add this new IP?';
                },
                notification: function(args) {
                  return 'Allocated IP';
                }
              },
              createForm: {
                title: 'Acquire new IP',
                desc: 'Please select a zone from which you want to acquire your new IP from.',
                fields: {
                  availabilityZone: {
                    label: 'Zone',
                    select: function(args) {
                      $.ajax({
                        url: createURL('listZones'),
                        dataType: 'json',
                        async: true,
                        success: function(data) {
                          args.response.success({
                            data: $.map(data.listzonesresponse.zone, function(zone) {
                              return {
                                id: zone.id,
                                description: zone.name
                              };
                            })
                          });
                        }
                      });
                    }
                  }
                }
              },
              notification: {
                poll: pollAsyncJobResult
              }
            },
            enableStaticNAT: {
              label: 'Enable static NAT',
              action: {
                noAdd: true,
                custom: cloudStack.uiCustom.enableStaticNAT({
                  listView: cloudStack.sections.instances,
                  action: function(args) {
                    $.ajax({
                      url: createURL('enableStaticNat'),
                      data: {
                        ipaddressid: args.context.ipAddresses[0].id,
                        virtualmachineid: args.context.instances[0].id
                      },
                      dataType: 'json',
                      async: true,
                      success: function(data) {
                        args.response.success();
                      }
                    });
                  }
                })
              },
              messages: {
                confirm: function(args) {
                  return 'Are you sure you want to enable static NAT?';
                },
                notification: function(args) {
                  return 'Enabled Static NAT';
                }
              },
              notification: {
                poll: function(args) {
                  args.complete({
                    data: {
                      isstaticnat: true
                    }
                  });
                }
              }
            },
            disableStaticNAT: {
              label: 'Disable static NAT',
              action: function(args) {
                $.ajax({
                  url: createURL('disableStaticNat'),
                  data: {
                    ipaddressid: args.context.ipAddresses[0].id
                  },
                  dataType: 'json',
                  async: true,
                  success: function(data) {
                    args.response.success({
                      _custom: {
                        jobId: data.disablestaticnatresponse.jobid,
                        getUpdatedItem: function(json) {
                          return $.extend(args.context.ipAddresses[0], {
                            isstaticnat: false
                          });
                        },
                        getActionFilter: function() {
                          return actionFilters.ipAddresses;
                        }
                      }
                    });
                  }
                });
              },
              messages: {
                confirm: function(args) {
                  return 'Are you sure you want to disable static NAT?';
                },
                notification: function(args) {
                  return 'Disable Static NAT';
                }
              },
              notification: {
                poll: pollAsyncJobResult
              }
            },
            destroy: {
              label: 'Release IP',
              action: function(args) {
                $.ajax({
                  url: createURL('disassociateIpAddress'),
                  data: {
                    id: args.context.ipAddresses[0].id
                  },
                  dataType: 'json',
                  async: true,
                  success: function(data) {
                    args.response.success({
                      _custom: {
                        jobId: data.disassociateipaddressresponse.jobid,
                        getActionFilter: function() {
                          return function(args) {
                            var allowedActions = ['enableStaticNAT'];
                            return allowedActions;
                          };
                        },
                        getUpdatedItem: function(args) {
                          return {
                            state: 'Released'
                          };
                        }
                      }
                    });
                  }
                });
              },
              messages: {
                confirm: function(args) {
                  return 'Are you sure you want to release this IP?';
                },
                notification: function(args) {
                  return 'Release IP';
                }
              },
              notification: { poll: pollAsyncJobResult }
            }
          },
          dataProvider: function(args) {
            var data = {
              page: args.page,
              pageSize: pageSize
            };
            if (g_supportELB == "guest") // IPs are allocated on guest network
              $.extend(data, {
                forvirtualnetwork: false,
                forloadbalancing: true
              });
            else if(g_supportELB == "public") // IPs are allocated on public network
              $.extend(data, {
                forvirtualnetwork: true,
                forloadbalancing: true
              });
            if (args.context.networks) {
              $.extend(data, { associatedNetworkId: args.context.networks[0].id });
            }
            $.ajax({
              url: createURL('listPublicIpAddresses'),
              data: data,
              dataType: "json",
              async: true,
              success: function(json) {
                var items = json.listpublicipaddressesresponse.publicipaddress;
                var processedItems = 0;
                if (!items) {
                  args.response.success({
                    data: []
                  });
                  return;
                }
                args.response.success({
                  actionFilter: actionFilters.ipAddress,
                  data: items
                });
              }
            });
          },
          // Detail view
          detailView: {
            name: 'IP address detail',
            tabFilter: function(args) {
              var item = args.context.ipAddresses[0];
              
              // Get VPN data
              $.ajax({
                url: createURL('listRemoteAccessVpns'),
                data: {
                  publicipid: item.id
                },
                dataType: 'json',
                async: false,
                success: function(vpnResponse) {
                  var isVPNEnabled = vpnResponse.listremoteaccessvpnsresponse.count;
                  if (isVPNEnabled) {
                    item.vpnenabled = true;
                    item.remoteaccessvpn = vpnResponse.listremoteaccessvpnsresponse.remoteaccessvpn[0];
                  };
                }
              });
              
              var disabledTabs = [];
              var ipAddress = args.context.ipAddresses[0];
              if (!ipAddress.issourcenat ||
                  (ipAddress.issourcenat && !ipAddress.vpnenabled)) {
                disabledTabs.push('vpn');
              }
              return disabledTabs;
            },
            actions: {
              enableVPN: {
                label: 'Enable VPN',
                action: function(args) {
                  $.ajax({
                    url: createURL('createRemoteAccessVpn'),
                    data: {
                      publicipid: args.context.ipAddresses[0].id,
                      domainid: args.context.ipAddresses[0].domainid
                    },
                    dataType: 'json',
                    async: true,
                    success: function(data) {
                      args.response.success({
                        _custom: {
                          getUpdatedItem: function(json) {
                            return {
                              vpn: json.queryasyncjobresultresponse.jobresult.remoteaccessvpn,
                              vpnenabled: true
                            };
                          },
                          getActionFilter: function() {
                            return actionFilters.ipAddress;
                          },
                          jobId: data.createremoteaccessvpnresponse.jobid
                        }
                      });
                    }
                  });
                },
                messages: {
                  confirm: function(args) {
                    return 'Please confirm that you want VPN enabled for this IP address.';
                  },
                  notification: function(args) {
                    return 'Enabled VPN';
                  },
                  complete: function(args) {
                    return 'VPN is now enabled for IP ' + args.vpn.publicip + '.'
                      + '
Your IPsec pre-shared key is:
' + args.vpn.presharedkey;
                  }
                },
                notification: {
                  poll: pollAsyncJobResult
                }
              },
              disableVPN: {
                label: 'Disable VPN',
                action: function(args) {
                  $.ajax({
                    url: createURL('deleteRemoteAccessVpn'),
                    data: {
                      publicipid: args.context.ipAddresses[0].id,
                      domainid: args.context.ipAddresses[0].domainid
                    },
                    dataType: 'json',
                    async: true,
                    success: function(data) {
                      args.response.success({
                        _custom: {
                          getUpdatedItem: function(data) {
                            return {
                              vpnenabled: false
                            };
                          },
                          getActionFilter: function() { return actionFilters.ipAddress; },
                          jobId: data.deleteremoteaccessvpnresponse.jobid
                        }
                      });
                    }
                  });
                },
                messages: {
                  confirm: function(args) {
                    return 'Are you sure you want to disable VPN?';
                  },
                  notification: function(args) {
                    return 'Disabled VPN';
                  }
                },
                notification: {
                  poll: pollAsyncJobResult
                }
              },
              enableStaticNAT: {
                label: 'Enable static NAT',
                action: {
                  noAdd: true,
                  custom: cloudStack.uiCustom.enableStaticNAT({
                    listView: cloudStack.sections.instances,
                    action: function(args) {
                      $.ajax({
                        url: createURL('enableStaticNat'),
                        data: {
                          ipaddressid: args.context.ipAddresses[0].id,
                          virtualmachineid: args.context.instances[0].id
                        },
                        dataType: 'json',
                        async: true,
                        success: function(data) {
                          args.response.success();
                        }
                      });
                    }
                  })
                },
                messages: {
                  notification: function(args) {
                    return 'Enabled Static NAT';
                  }
                },
                notification: {
                  poll: function(args) {
                    args.complete({
                      data: {
                        isstaticnat: true
                      }
                    });
                  }
                }
              },
              disableStaticNAT: {
                label: 'Disable static NAT',
                action: function(args) {
                  $.ajax({
                    url: createURL('disableStaticNat'),
                    data: {
                      ipaddressid: args.context.ipAddresses[0].id
                    },
                    dataType: 'json',
                    async: true,
                    success: function(data) {
                      args.response.success({
                        _custom: {
                          jobId: data.disablestaticnatresponse.jobid,
                          getUpdatedItem: function() {
                            return {
                              isstaticnat: false
                            };
                          },
                          getActionFilter: function() {
                            return function(args) {
                              return ['enableStaticNAT'];
                            };
                          }
                        }
                      });
                    }
                  });
                },
                messages: {
                  confirm: function(args) {
                    return 'Are you sure you want to disable static NAT?';
                  },
                  notification: function(args) {
                    return 'Disable Static NAT';
                  }
                },
                notification: {
                  poll: pollAsyncJobResult
                }
              },
              destroy: {
                label: 'Release IP',
                action: function(args) {
                  $.ajax({
                    url: createURL('disassociateIpAddress'),
                    data: {
                      id: args.context.ipAddresses[0].id
                    },
                    dataType: 'json',
                    async: true,
                    success: function(data) {
                      args.response.success({
                        _custom: {
                          jobId: data.disassociateipaddressresponse.jobid,
                          getActionFilter: function() {
                            return function(args) {
                              var allowedActions = ['enableStaticNAT'];
                              return allowedActions;
                            };
                          },
                          getUpdatedItem: function(args) {
                            return {
                              state: 'Released'
                            };
                          }
                        }
                      });
                    }
                  });
                },
                messages: {
                  confirm: function(args) {
                    return 'Are you sure you want to release this IP?';
                  },
                  notification: function(args) {
                    return 'Release IP';
                  }
                },
                notification: { poll: pollAsyncJobResult }
              }
            },
            tabs: {
              details: {
                title: 'Details',
                fields: [
                  {
                    ipaddress: { label: 'IP' }
                  },
                  {
                    state: { label: 'State' },
                    zonename: { label: 'Zone' },
                    vlanname: { label: 'VLAN' },
                    issourcenat: { label: 'Source NAT' }
                  }
                ],
                //dataProvider: testData.dataProvider.detailView('network')
                dataProvider: function(args) {
                  var items = args.context.ipAddresses;
                  // Get network data
                  $.ajax({
                    url: createURL("listPublicIpAddresses&id="+args.id),
                    dataType: "json",
                    async: true,
                    success: function(json) {
                      var item = items[0];
                      $.ajax({
                        url: createURL('listNetworks'),
                        data: {
                          networkid: this.networkid
                        },
                        dataType: 'json',
                        async: true,
                        success: function(data) {
                          // Get VPN data
                          $.ajax({
                            url: createURL('listRemoteAccessVpns'),
                            data: {
                              publicipid: item.id
                            },
                            dataType: 'json',
                            async: true,
                            success: function(vpnResponse) {
                              var isVPNEnabled = vpnResponse.listremoteaccessvpnsresponse.count;
                              if (isVPNEnabled) {
                                item.vpnenabled = true;
                                item.remoteaccessvpn = vpnResponse.listremoteaccessvpnsresponse.remoteaccessvpn[0];
                              };
                              // Check if data retrieval complete
                              item.network = data.listnetworksresponse.network[0];
                              args.response.success({
                                actionFilter: actionFilters.ipAddress,
                                data: item
                              });
                            }
                          });
                        }
                      });
                    }
                  });
                }
              },
              ipRules: {
                title: 'Configuration',
                custom: cloudStack.ipRules({
                  preFilter: function(args) {
                    if (args.context.ipAddresses[0].isstaticnat) {
                      return args.items; // All items filtered means static NAT
                    }
                    return [];
                  },
                  // Firewall rules
                  firewall: {
                    noSelect: true,
                    fields: {
                      'cidrlist': { edit: true, label: 'Source CIDR' },
                      'protocol': {
                        label: 'Protocol',
                        select: function(args) {
                          args.$select.change(function() {
                            var $inputs = args.$form.find('input');
                            var $icmpFields = $inputs.filter(function() {
                              var name = $(this).attr('name');
                              return $.inArray(name, [
                                'icmptype',
                                'icmpcode'
                              ]) > -1;
                            });
                            var $otherFields = $inputs.filter(function() {
                              var name = $(this).attr('name');
                              return name != 'icmptype' && name != 'icmpcode' && name != 'cidrlist';
                            });
                            if ($(this).val() == 'icmp') {
                              $icmpFields.attr('disabled', false);
                              $otherFields.attr('disabled', 'disabled');
                            } else {
                              $otherFields.attr('disabled', false);
                              $icmpFields.attr('disabled', 'disabled');
                            }
                          });
                          args.response.success({
                            data: [
                              { name: 'tcp', description: 'TCP' },
                              { name: 'udp', description: 'UDP' },
                              { name: 'icmp', description: 'ICMP' }
                            ]
                          });
                        }
                      },
                      'startport': { edit: true, label: 'Start Port' },
                      'endport': { edit: true, label: 'End Port' },
                      'icmptype': { edit: true, label: 'ICMP Type', isDisabled: true },
                      'icmpcode': { edit: true, label: 'ICMP Code', isDisabled: true },
                      'add-rule': {
                        label: 'Add Rule',
                        addButton: true
                      }
                    },
                    add: {
                      label: 'Add',
                      action: function(args) {
                        $.ajax({
                          url: createURL('createFirewallRule'),
                          data: $.extend(args.data, {
                            ipaddressid: args.context.ipAddresses[0].id
                          }),
                          dataType: 'json',
                          success: function(data) {
                            args.response.success({
                              _custom: {
                                jobId: data.createfirewallruleresponse.jobid
                              },
                              notification: {
                                label: 'Add firewall rule',
                                poll: pollAsyncJobResult
                              }
                            });
                          }
                        });
                      }
                    },
                    actions: {
                      destroy: {
                        label: 'Remove Rule',
                        action: function(args) {
                          $.ajax({
                            url: createURL('deleteFirewallRule'),
                            data: {
                              id: args.context.multiRule[0].id
                            },
                            dataType: 'json',
                            async: true,
                            success: function(data) {
                              var jobID = data.deletefirewallruleresponse.jobid;
                              args.response.success({
                                _custom: {
                                  jobId: jobID
                                },
                                notification: {
                                  label: 'Remove firewall rule ' + args.context.multiRule[0].id,
                                  poll: pollAsyncJobResult
                                }
                              });
                            }
                          });
                        }
                      }
                    },
                    dataProvider: function(args) {
                      $.ajax({
                        url: createURL('listFirewallRules'),
                        data: {
                          ipaddressid: args.context.ipAddresses[0].id
                        },
                        dataType: 'json',
                        async: true,
                        success: function(data) {
                          args.response.success({
                            data: data.listfirewallrulesresponse.firewallrule
                          });
                        }
                      });
                    }
                  },
                  staticNATDataProvider: function(args) {
                    $.ajax({
                      url: createURL('listPublicIpAddresses'),
                      data: {
                        id: args.context.ipAddresses[0].id
                      },
                      dataType: 'json',
                      async: true,
                      success: function(data) {
                        var ipAddress = data.listpublicipaddressesresponse.publicipaddress[0];
                        args.response.success({
                          data: ipAddress
                        });
                      }
                    });
                  },
                  vmDataProvider: function(args) {
                    $.ajax({
                      url: createURL('listVirtualMachines'),
                      data: {
                        id: args.context.ipAddresses[0].virtualmachineid
                      },
                      dataType: 'json',
                      async: true,
                      success: function(data) {
                        args.response.success({
                          data: data.listvirtualmachinesresponse.virtualmachine[0]
                        });
                      }
                    });
                  },
                  vmDetails: cloudStack.sections.instances.listView.detailView,
                  staticNAT: {
                    noSelect: true,
                    fields: {
                      'protocol': {
                        label: 'Protocol',
                        select: function(args) {
                          args.response.success({
                            data: [
                              { name: 'tcp', description: 'TCP' },
                              { name: 'udp', description: 'UDP' }
                            ]
                          });
                        }
                      },
                      'startport': { edit: true, label: 'Start Port' },
                      'endport': { edit: true, label: 'End Port' },
                      'add-rule': {
                        label: 'Add Rule',
                        addButton: true
                      }
                    },
                    add: {
                      label: 'Add',
                      action: function(args) {
                        $.ajax({
                          url: createURL('createIpForwardingRule'),
                          data: $.extend(args.data, {
                            ipaddressid: args.context.ipAddresses[0].id
                          }),
                          dataType: 'json',
                          success: function(data) {
                            args.response.success({
                              _custom: {
                                jobId: data.createipforwardingruleresponse.jobid
                              },
                              notification: {
                                label: 'Added static NAT rule',
                                poll: pollAsyncJobResult
                              }
                            });
                          }
                        });
                      }
                    },
                    actions: {
                      destroy: {
                        label: 'Remove Rule',
                        action: function(args) {
                          $.ajax({
                            url: createURL('deleteIpForwardingRule'),
                            data: {
                              id: args.context.multiRule[0].id
                            },
                            dataType: 'json',
                            async: true,
                            success: function(data) {
                              var jobID = data.deleteipforwardingruleresponse.jobid;
                              args.response.success({
                                _custom: {
                                  jobId: jobID
                                },
                                notification: {
                                  label: 'Removed static NAT rule',
                                  poll: pollAsyncJobResult
                                }
                              });
                            }
                          });
                        }
                      }
                    },
                    dataProvider: function(args) {
                      setTimeout(function() {
                        $.ajax({
                          url: createURL('listIpForwardingRules'),
                          data: {
                            ipaddressid: args.context.ipAddresses[0].id
                          },
                          dataType: 'json',
                          async: true,
                          success: function(data) {
                            args.response.success({
                              data: data.listipforwardingrulesresponse.ipforwardingrule
                            });
                          }
                        });
                      }, 100);
                    }
                  },
                  // Load balancing rules
                  loadBalancing: {
                    listView: $.extend(true, {}, cloudStack.sections.instances, {
                      listView: {
                        dataProvider: function(args) {
                          $.ajax({
                            url: createURL('listVirtualMachines'),
                            data: {
                              physicalnetworkid: args.context.ipAddresses[0].physicalnetworkid
                            },
                            dataType: 'json',
                            async: true,
                            success: function(data) {
                              args.response.success({
                                data: $.grep(
                                  data.listvirtualmachinesresponse.virtualmachine,
                                  function(instance) {
                                    return $.inArray(instance.state, [
                                      'Destroyed'
                                    ]) == -1;
                                  }
                                )
                              });
                            }
                          });
                        } 
                      }
                    }),
                    multipleAdd: true,
                    fields: {
                      'name': { edit: true, label: 'Name' },
                      'publicport': { edit: true, label: 'Public Port' },
                      'privateport': { edit: true, label: 'Private Port' },
                      'algorithm': {
                        label: 'Algorithm',
                        select: function(args) {
                          args.response.success({
                            data: [
                              { name: 'roundrobin', description: 'Round-robin' },
                              { name: 'leastconn', description: 'Least connections' },
                              { name: 'source', description: 'Source' }
                            ]
                          });
                        }
                      },
                      'add-vm': {
                        label: 'Add VMs',
                        addButton: true
                      }
                    },
                    add: {
                      label: 'Add VMs',
                      action: function(args) {
                        $.ajax({
                          url: createURL('createLoadBalancerRule'),
                          data: $.extend(args.data, {
                            publicipid: args.context.ipAddresses[0].id
                          }),
                          dataType: 'json',
                          async: true,
                          success: function(data) {
                            var itemData = args.itemData;
                            $.ajax({
                              url: createURL('assignToLoadBalancerRule'),
                              data: {
                                id: data.createloadbalancerruleresponse.id,
                                virtualmachineids: $.map(itemData, function(elem) {
                                  return elem.id;
                                }).join(',')
                              },
                              dataType: 'json',
                              async: true,
                              success: function(data) {
                              }
                            });
                            args.response.success({
                              _custom: {
                                jobId: data.createloadbalancerruleresponse.jobid
                              },
                              notification: {
                                label: 'Add load balancer rule',
                                poll: pollAsyncJobResult
                              }
                            });
                          }
                        });
                      }
                    },
                    actions: {
                      destroy:  {
                        label: 'Remove load balancer rule',
                        action: function(args) {
                          $.ajax({
                            url: createURL('deleteLoadBalancerRule'),
                            data: {
                              id: args.context.multiRule[0].id
                            },
                            dataType: 'json',
                            async: true,
                            success: function(data) {
                              var jobID = data.deleteloadbalancerruleresponse.jobid;
                              args.response.success({
                                _custom: {
                                  jobId: jobID
                                },
                                notification: {
                                  label: 'Remove load balancer rule ' + args.context.multiRule[0].id,
                                  poll: pollAsyncJobResult
                                }
                              });
                            }
                          });
                        }
                      }
                    },
                    dataProvider: function(args) {
                      $.ajax({
                        url: createURL('listLoadBalancerRules'),
                        data: {
                          publicipid: args.context.ipAddresses[0].id
                        },
                        dataType: 'json',
                        async: true,
                        success: function(data) {
                          var loadBalancerData = data.listloadbalancerrulesresponse.loadbalancerrule;
                          var loadVMTotal = loadBalancerData ? loadBalancerData.length : 0;
                          var loadVMCurrent = 0;
                          $(loadBalancerData).each(function() {
                            var item = this;
                            // Get instances
                            $.ajax({
                              url: createURL('listLoadBalancerRuleInstances'),
                              dataType: 'json',
                              async: true,
                              data: {
                                id: item.id
                              },
                              success: function(data) {
                                loadVMCurrent++;
                                $.extend(item, {
                                  _itemData: data
                                    .listloadbalancerruleinstancesresponse.loadbalancerruleinstance
                                });
                                if (loadVMCurrent == loadVMTotal) {
                                  args.response.success({
                                    data: loadBalancerData
                                  });
                                }
                              }
                            });
                          });
                        }
                      });
                    }
                  },
                  // Port forwarding rules
                  portForwarding: {
                    listView: $.extend(true, {}, cloudStack.sections.instances, {
                      listView: {
                        dataProvider: function(args) {
                          $.ajax({
                            url: createURL('listVirtualMachines'),
                            data: {
                              physicalnetworkid: args.context.ipAddresses[0].physicalnetworkid
                            },
                            dataType: 'json',
                            async: true,
                            success: function(data) {
                              args.response.success({
                                data: $.grep(
                                  data.listvirtualmachinesresponse.virtualmachine,
                                  function(instance) {
                                    return $.inArray(instance.state, [
                                      'Destroyed'
                                    ]) == -1;
                                  }
                                )
                              });
                            }
                          });
                        } 
                      }
                    }),
                    fields: {
                      'private-ports': {
                        edit: true,
                        label: 'Private Ports',
                        range: ['privateport', 'privateendport']
                      },
                      'public-ports': {
                        edit: true,
                        label: 'Public Ports',
                        range: ['publicport', 'publicendport']
                      },
                      'protocol': {
                        label: 'Protocol',
                        select: function(args) {
                          args.response.success({
                            data: [
                              { name: 'tcp', description: 'TCP' },
                              { name: 'udp', description: 'UDP' }
                            ]
                          });
                        }
                      },
                      'add-vm': {
                        label: 'Add VM',
                        addButton: true
                      }
                    },
                    add: {
                      label: 'Add VM',
                      action: function(args) {
                        $.ajax({
                          url: createURL('createPortForwardingRule'),
                          data: $.extend(args.data, {
                            ipaddressid: args.context.ipAddresses[0].id,
                            virtualmachineid: args.itemData[0].id
                          }),
                          dataType: 'json',
                          async: true,
                          success: function(data) {
                            args.response.success({
                              _custom: {
                                jobId: data.createportforwardingruleresponse.jobid
                              },
                              notification: {
                                label: 'Add port forwarding rule',
                                poll: pollAsyncJobResult
                              }
                            });
                          }
                        });
                      }
                    },
                    actions: {
                      destroy: {
                        label: 'Remove port forwarding rule',
                        action: function(args) {
                          $.ajax({
                            url: createURL('deletePortForwardingRule'),
                            data: {
                              id: args.context.multiRule[0].id
                            },
                            dataType: 'json',
                            async: true,
                            success: function(data) {
                              var jobID = data.deleteportforwardingruleresponse.jobid;
                              args.response.success({
                                _custom: {
                                  jobId: jobID
                                },
                                notification: {
                                  label: 'Remove port forwarding rule ' + args.context.multiRule[0].id,
                                  poll: pollAsyncJobResult
                                }
                              });
                            }
                          });
                        }
                      }
                    },
                    dataProvider: function(args) {
                      $.ajax({
                        url: createURL('listPortForwardingRules'),
                        data: {
                          ipaddressid: args.context.ipAddresses[0].id
                        },
                        dataType: 'json',
                        async: true,
                        success: function(data) {
                          // Get instance
                          var portForwardingData = data
                                .listportforwardingrulesresponse.portforwardingrule;
                          var loadTotal = portForwardingData ? portForwardingData.length : 0;
                          var loadCurrent = 0;
                          $(portForwardingData).each(function() {
                            var item = this;
                            $.ajax({
                              url: createURL('listVirtualMachines'),
                              dataType: 'json',
                              async: true,
                              data: {
                                id: item.virtualmachineid
                              },
                              success: function(data) {
                                loadCurrent++;
                                $.extend(item, {
                                  _itemData: data.listvirtualmachinesresponse.virtualmachine,
                                  _context: {
                                    instances: data.listvirtualmachinesresponse.virtualmachine
                                  }
                                });
                                if (loadCurrent == loadTotal) {
                                  args.response.success({
                                    data: portForwardingData
                                  });
                                }
                              }
                            });
                          });
                        }
                      });
                    }
                  }
                })
              },
              vpn: {
                title: 'VPN',
                custom: function(args) {
                  var ipAddress = args.context.ipAddresses[0].ipaddress;
                  var psk = args.context.ipAddresses[0].remoteaccessvpn.presharedkey;
                  return $('