Merge branch 'master' into regions

Conflicts:
	client/tomcatconf/components.xml.in
	server/src/com/cloud/api/ApiResponseHelper.java
diff --git a/INSTALL.md b/INSTALL.md
index 61ebadf..00c724b 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -52,8 +52,9 @@
 used by Apache CloudStack and its entities:
 
     8787: Apache CloudStack (Tomcat) debug socket
-    9090, 8250: Apache CloudStack Management Server, User/Client API
+    9090, 8250, 8080: Apache CloudStack Management Server, User/Client API
     8096: User/Client to CloudStack Management Server (unauthenticated)
+    7080: AWS API Server
     3306: MySQL Server
     3922, 8250, 80/443, 111/2049, 53: Secondary Storage VM
     3922, 8250, 53: Console Proxy VM
diff --git a/api/src/com/cloud/agent/api/routing/DhcpEntryCommand.java b/api/src/com/cloud/agent/api/routing/DhcpEntryCommand.java
index 5c2cd58..f0ce70e 100644
--- a/api/src/com/cloud/agent/api/routing/DhcpEntryCommand.java
+++ b/api/src/com/cloud/agent/api/routing/DhcpEntryCommand.java
@@ -28,7 +28,9 @@
     String defaultRouter;
     String staticRoutes;
     String defaultDns;
-
+    String vmIp6Address;
+    String ip6Gateway;
+    String duid;
 
     protected DhcpEntryCommand() {
 
@@ -39,14 +41,15 @@
         return true;
     }
 
-    public DhcpEntryCommand(String vmMac, String vmIpAddress, String vmName) {
+    public DhcpEntryCommand(String vmMac, String vmIpAddress, String vmName, String vmIp6Address) {
         this.vmMac = vmMac;
         this.vmIpAddress = vmIpAddress;
         this.vmName = vmName;
+        this.vmIp6Address = vmIp6Address;
     }
 
-    public DhcpEntryCommand(String vmMac, String vmIpAddress, String vmName, String dns, String gateway) {
-        this(vmMac, vmIpAddress, vmName);
+    public DhcpEntryCommand(String vmMac, String vmIpAddress, String vmName, String vmIp6Address, String dns, String gateway, String ip6Gateway) {
+        this(vmMac, vmIpAddress, vmName, vmIp6Address);
         this.dns = dns;
         this.gateway = gateway;
     }
@@ -102,4 +105,28 @@
 	public void setDefaultDns(String defaultDns) {
 		this.defaultDns = defaultDns;
 	}
+
+	public String getIp6Gateway() {
+		return ip6Gateway;
+	}
+
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+	public String getDuid() {
+		return duid;
+	}
+
+	public void setDuid(String duid) {
+		this.duid = duid;
+	}
+
+	public String getVmIp6Address() {
+		return vmIp6Address;
+	}
+
+	public void setVmIp6Address(String ip6Address) {
+		this.vmIp6Address = ip6Address;
+	}
 }
diff --git a/api/src/com/cloud/dc/Vlan.java b/api/src/com/cloud/dc/Vlan.java
index 0f629ef..790195f 100644
--- a/api/src/com/cloud/dc/Vlan.java
+++ b/api/src/com/cloud/dc/Vlan.java
@@ -44,4 +44,9 @@
 
     public Long getPhysicalNetworkId();
 
+	public String getIp6Gateway();
+
+	public String getIp6Cidr();
+
+	public String getIp6Range();
 }
diff --git a/api/src/com/cloud/event/EventCategory.java b/api/src/com/cloud/event/EventCategory.java
new file mode 100644
index 0000000..cee6529
--- /dev/null
+++ b/api/src/com/cloud/event/EventCategory.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.cloud.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EventCategory {
+    private static List<EventCategory> eventCategories = new ArrayList<EventCategory>();
+    private String eventCategoryName;
+
+    public  EventCategory(String categoryName) {
+        this.eventCategoryName = categoryName;
+        eventCategories.add(this);
+    }
+
+    public String getName() {
+        return eventCategoryName;
+    }
+
+    public static List<EventCategory> listAllEventCategories() {
+        return eventCategories;
+    }
+
+    public static EventCategory getEventCategory(String categoryName) {
+        for (EventCategory category : eventCategories) {
+            if (category.getName().equalsIgnoreCase(categoryName)) {
+                return category;
+            }
+        }
+        return null;
+    }
+
+    public static final EventCategory ACTION_EVENT = new EventCategory("ActionEvent");
+    public static final EventCategory ALERT_EVENT  = new EventCategory("AlertEvent");
+    public static final EventCategory USAGE_EVENT  = new EventCategory("UsageEvent");
+    public static final EventCategory RESOURCE_STATE_CHANGE_EVENT = new EventCategory("ResourceStateEvent");
+}
diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java
index d666c1e..0dd97cb 100755
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -16,7 +16,41 @@
 // under the License.
 package com.cloud.event;
 
+import com.cloud.configuration.Configuration;
+import com.cloud.dc.DataCenter;
+import com.cloud.dc.Pod;
+import com.cloud.dc.StorageNetworkIpRange;
+import com.cloud.dc.Vlan;
+import com.cloud.domain.Domain;
+import com.cloud.host.Host;
+import com.cloud.network.*;
+import com.cloud.network.as.*;
+import com.cloud.network.router.VirtualRouter;
+import com.cloud.network.rules.LoadBalancer;
+import com.cloud.network.rules.StaticNat;
+import com.cloud.network.security.SecurityGroup;
+import com.cloud.network.vpc.PrivateGateway;
+import com.cloud.network.vpc.StaticRoute;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.offering.DiskOffering;
+import com.cloud.offering.NetworkOffering;
+import com.cloud.offering.ServiceOffering;
+import com.cloud.projects.Project;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.Volume;
+import com.cloud.template.VirtualMachineTemplate;
+import com.cloud.user.Account;
+import com.cloud.user.User;
+import com.cloud.vm.VirtualMachine;
+
+import java.util.HashMap;
+import java.util.Map;
+
 public class EventTypes {
+
+    //map of Event and corresponding entity for which Event is applicable
+    private static Map<String, String> entityEventDetails = null;
+
     // VM Events
     public static final String EVENT_VM_CREATE = "VM.CREATE";
     public static final String EVENT_VM_DESTROY = "VM.DESTROY";
@@ -319,10 +353,323 @@
     public static final String EVENT_AUTOSCALEVMGROUP_UPDATE = "AUTOSCALEVMGROUP.UPDATE";
     public static final String EVENT_AUTOSCALEVMGROUP_ENABLE = "AUTOSCALEVMGROUP.ENABLE";
     public static final String EVENT_AUTOSCALEVMGROUP_DISABLE = "AUTOSCALEVMGROUP.DISABLE";
-    
+
+
     public static final String EVENT_BAREMETAL_DHCP_SERVER_ADD = "PHYSICAL.DHCP.ADD";
     public static final String EVENT_BAREMETAL_DHCP_SERVER_DELETE = "PHYSICAL.DHCP.DELETE";
-    
     public static final String EVENT_BAREMETAL_PXE_SERVER_ADD = "PHYSICAL.PXE.ADD";
     public static final String EVENT_BAREMETAL_PXE_SERVER_DELETE = "PHYSICAL.PXE.DELETE";
+
+    static {
+
+        // TODO: need a way to force author adding event types to declare the entity details as well, with out braking
+        // current ActionEvent annotation semantics
+
+        entityEventDetails = new HashMap<String, String>();
+
+        entityEventDetails.put(EVENT_VM_CREATE, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_DESTROY, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_START, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_STOP, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_REBOOT, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_UPDATE, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_UPGRADE, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_RESETPASSWORD, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_MIGRATE, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_MOVE, VirtualMachine.class.getName());
+        entityEventDetails.put(EVENT_VM_RESTORE, VirtualMachine.class.getName());
+
+        entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class.getName());
+        entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class.getName());
+        entityEventDetails.put(EVENT_ROUTER_START, VirtualRouter.class.getName());
+        entityEventDetails.put(EVENT_ROUTER_STOP, VirtualRouter.class.getName());
+        entityEventDetails.put(EVENT_ROUTER_REBOOT, VirtualRouter.class.getName());
+        entityEventDetails.put(EVENT_ROUTER_HA, VirtualRouter.class.getName());
+        entityEventDetails.put(EVENT_ROUTER_UPGRADE, VirtualRouter.class.getName());
+
+        entityEventDetails.put(EVENT_PROXY_CREATE, "ConsoleProxy");
+        entityEventDetails.put(EVENT_PROXY_DESTROY, "ConsoleProxy");
+        entityEventDetails.put(EVENT_PROXY_START, "ConsoleProxy");
+        entityEventDetails.put(EVENT_PROXY_STOP, "ConsoleProxy");
+        entityEventDetails.put(EVENT_PROXY_REBOOT, "ConsoleProxy");
+        entityEventDetails.put(EVENT_ROUTER_HA, "ConsoleProxy");
+        entityEventDetails.put(EVENT_PROXY_HA, "ConsoleProxy");
+
+        entityEventDetails.put(EVENT_VNC_CONNECT, "VNC");
+        entityEventDetails.put(EVENT_VNC_DISCONNECT, "VNC");
+
+        // Network Events
+        entityEventDetails.put(EVENT_NETWORK_CREATE, Network.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_DELETE, Network.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_UPDATE, Network.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_RESTART, Network.class.getName());
+        entityEventDetails.put(EVENT_NET_IP_ASSIGN, PublicIpAddress.class.getName());
+        entityEventDetails.put(EVENT_NET_IP_RELEASE, PublicIpAddress.class.getName());
+        entityEventDetails.put(EVENT_NET_RULE_ADD, Network.class.getName());
+        entityEventDetails.put(EVENT_NET_RULE_DELETE, Network.class.getName());
+        entityEventDetails.put(EVENT_NET_RULE_MODIFY, Network.class.getName());
+        entityEventDetails.put(EVENT_FIREWALL_OPEN, Network.class.getName());
+        entityEventDetails.put(EVENT_FIREWALL_CLOSE, Network.class.getName());
+
+        // Load Balancers
+        entityEventDetails.put(EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, LoadBalancer.class.getName());
+        entityEventDetails.put(EVENT_REMOVE_FROM_LOAD_BALANCER_RULE, LoadBalancer.class.getName());
+        entityEventDetails.put(EVENT_LOAD_BALANCER_CREATE, LoadBalancer.class.getName());
+        entityEventDetails.put(EVENT_LOAD_BALANCER_DELETE, LoadBalancer.class.getName());
+        entityEventDetails.put(EVENT_LB_STICKINESSPOLICY_CREATE, LoadBalancer.class.getName());
+        entityEventDetails.put(EVENT_LB_STICKINESSPOLICY_DELETE, LoadBalancer.class.getName());
+        entityEventDetails.put(EVENT_LOAD_BALANCER_UPDATE, LoadBalancer.class.getName());
+
+        // Account events
+        entityEventDetails.put(EVENT_ACCOUNT_DISABLE, Account.class.getName());
+        entityEventDetails.put(EVENT_ACCOUNT_CREATE, Account.class.getName());
+        entityEventDetails.put(EVENT_ACCOUNT_DELETE, Account.class.getName());
+        entityEventDetails.put(EVENT_ACCOUNT_MARK_DEFAULT_ZONE, Account.class.getName());
+
+        // UserVO Events
+        entityEventDetails.put(EVENT_USER_LOGIN, User.class.getName());
+        entityEventDetails.put(EVENT_USER_LOGOUT, User.class.getName());
+        entityEventDetails.put(EVENT_USER_CREATE, User.class.getName());
+        entityEventDetails.put(EVENT_USER_DELETE, User.class.getName());
+        entityEventDetails.put(EVENT_USER_DISABLE, User.class.getName());
+        entityEventDetails.put(EVENT_USER_UPDATE, User.class.getName());
+        entityEventDetails.put(EVENT_USER_ENABLE, User.class.getName());
+        entityEventDetails.put(EVENT_USER_LOCK, User.class.getName());
+
+        // Template Events
+        entityEventDetails.put(EVENT_TEMPLATE_CREATE, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_DELETE, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_UPDATE, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_DOWNLOAD_START, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_DOWNLOAD_SUCCESS, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_DOWNLOAD_FAILED, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_COPY, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_EXTRACT, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_UPLOAD, VirtualMachineTemplate.class.getName());
+        entityEventDetails.put(EVENT_TEMPLATE_CLEANUP, VirtualMachineTemplate.class.getName());
+
+        // Volume Events
+        entityEventDetails.put(EVENT_VOLUME_CREATE, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_DELETE, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_ATTACH, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_DETACH, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_EXTRACT, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_UPLOAD, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_MIGRATE, Volume.class.getName());
+        entityEventDetails.put(EVENT_VOLUME_RESIZE, Volume.class.getName());
+
+        // Domains
+        entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class.getName());
+        entityEventDetails.put(EVENT_DOMAIN_DELETE, Domain.class.getName());
+        entityEventDetails.put(EVENT_DOMAIN_UPDATE, Domain.class.getName());
+
+        // Snapshots
+        entityEventDetails.put(EVENT_SNAPSHOT_CREATE, Snapshot.class.getName());
+        entityEventDetails.put(EVENT_SNAPSHOT_DELETE, Snapshot.class.getName());
+        entityEventDetails.put(EVENT_SNAPSHOT_POLICY_CREATE, Snapshot.class.getName());
+        entityEventDetails.put(EVENT_SNAPSHOT_POLICY_UPDATE, Snapshot.class.getName());
+        entityEventDetails.put(EVENT_SNAPSHOT_POLICY_DELETE, Snapshot.class.getName());
+
+        // ISO
+        entityEventDetails.put(EVENT_ISO_CREATE, "Iso");
+        entityEventDetails.put(EVENT_ISO_DELETE, "Iso");
+        entityEventDetails.put(EVENT_ISO_COPY, "Iso");
+        entityEventDetails.put(EVENT_ISO_ATTACH, "Iso");
+        entityEventDetails.put(EVENT_ISO_DETACH, "Iso");
+        entityEventDetails.put(EVENT_ISO_EXTRACT, "Iso");
+        entityEventDetails.put(EVENT_ISO_UPLOAD, "Iso");
+
+        // SSVM
+        entityEventDetails.put(EVENT_SSVM_CREATE, "SecondaryStorageVm");
+        entityEventDetails.put(EVENT_SSVM_DESTROY, "SecondaryStorageVm");
+        entityEventDetails.put(EVENT_SSVM_START, "SecondaryStorageVm");
+        entityEventDetails.put(EVENT_SSVM_STOP, "SecondaryStorageVm");
+        entityEventDetails.put(EVENT_SSVM_REBOOT, "SecondaryStorageVm");
+        entityEventDetails.put(EVENT_SSVM_HA, "SecondaryStorageVm");
+
+        // Service Offerings
+        entityEventDetails.put(EVENT_SERVICE_OFFERING_CREATE, ServiceOffering.class.getName());
+        entityEventDetails.put(EVENT_SERVICE_OFFERING_EDIT, ServiceOffering.class.getName());
+        entityEventDetails.put(EVENT_SERVICE_OFFERING_DELETE, ServiceOffering.class.getName());
+
+        // Disk Offerings
+        entityEventDetails.put(EVENT_DISK_OFFERING_CREATE, DiskOffering.class.getName());
+        entityEventDetails.put(EVENT_DISK_OFFERING_EDIT, DiskOffering.class.getName());
+        entityEventDetails.put(EVENT_DISK_OFFERING_DELETE, DiskOffering.class.getName());
+
+        // Network offerings
+        entityEventDetails.put(EVENT_NETWORK_OFFERING_CREATE, NetworkOffering.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_OFFERING_ASSIGN, NetworkOffering.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_OFFERING_EDIT, NetworkOffering.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_OFFERING_REMOVE, NetworkOffering.class.getName());
+        entityEventDetails.put(EVENT_NETWORK_OFFERING_DELETE, NetworkOffering.class.getName());
+
+        // Pods
+        entityEventDetails.put(EVENT_POD_CREATE, Pod.class.getName());
+        entityEventDetails.put(EVENT_POD_EDIT, Pod.class.getName());
+        entityEventDetails.put(EVENT_POD_DELETE, Pod.class.getName());
+
+        // Zones
+        entityEventDetails.put(EVENT_ZONE_CREATE, DataCenter.class.getName());
+        entityEventDetails.put(EVENT_ZONE_EDIT, DataCenter.class.getName());
+        entityEventDetails.put(EVENT_ZONE_DELETE, DataCenter.class.getName());
+
+        // VLANs/IP ranges
+        entityEventDetails.put(EVENT_VLAN_IP_RANGE_CREATE, Vlan.class.getName());
+        entityEventDetails.put(EVENT_VLAN_IP_RANGE_DELETE,Vlan.class.getName());
+
+        entityEventDetails.put(EVENT_STORAGE_IP_RANGE_CREATE, StorageNetworkIpRange.class.getName());
+        entityEventDetails.put(EVENT_STORAGE_IP_RANGE_DELETE, StorageNetworkIpRange.class.getName());
+        entityEventDetails.put(EVENT_STORAGE_IP_RANGE_UPDATE, StorageNetworkIpRange.class.getName());
+
+        // Configuration Table
+        entityEventDetails.put(EVENT_CONFIGURATION_VALUE_EDIT, Configuration.class.getName());
+
+        // Security Groups
+        entityEventDetails.put(EVENT_SECURITY_GROUP_AUTHORIZE_INGRESS, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_REVOKE_INGRESS, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_AUTHORIZE_EGRESS, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_REVOKE_EGRESS, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_CREATE, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_DELETE, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_ASSIGN, SecurityGroup.class.getName());
+        entityEventDetails.put(EVENT_SECURITY_GROUP_REMOVE, SecurityGroup.class.getName());
+
+        // Host
+        entityEventDetails.put(EVENT_HOST_RECONNECT,  Host.class.getName());
+
+        // Maintenance
+        entityEventDetails.put(EVENT_MAINTENANCE_CANCEL,  Host.class.getName());
+        entityEventDetails.put(EVENT_MAINTENANCE_CANCEL_PRIMARY_STORAGE,  Host.class.getName());
+        entityEventDetails.put(EVENT_MAINTENANCE_PREPARE,  Host.class.getName());
+        entityEventDetails.put(EVENT_MAINTENANCE_PREPARE_PRIMARY_STORAGE,  Host.class.getName());
+
+        // VPN
+        entityEventDetails.put(EVENT_REMOTE_ACCESS_VPN_CREATE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_REMOTE_ACCESS_VPN_DESTROY, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_VPN_USER_ADD, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_VPN_USER_REMOVE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_CREATE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_DELETE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_CREATE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_DELETE, RemoteAccessVpn.class.getName());
+        entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_RESET, RemoteAccessVpn.class.getName());
+
+        // Custom certificates
+        entityEventDetails.put(EVENT_UPLOAD_CUSTOM_CERTIFICATE, "Certificate");
+
+        // OneToOnenat
+        entityEventDetails.put(EVENT_ENABLE_STATIC_NAT, StaticNat.class.getName());
+        entityEventDetails.put(EVENT_DISABLE_STATIC_NAT, StaticNat.class.getName());
+
+        entityEventDetails.put(EVENT_ZONE_VLAN_ASSIGN,Vlan.class.getName());
+        entityEventDetails.put(EVENT_ZONE_VLAN_RELEASE,Vlan.class.getName());
+
+        // Projects
+        entityEventDetails.put(EVENT_PROJECT_CREATE, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_UPDATE, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_DELETE, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_ACTIVATE, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_SUSPEND, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_ACCOUNT_ADD, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_INVITATION_UPDATE, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_INVITATION_REMOVE, Project.class.getName());
+        entityEventDetails.put(EVENT_PROJECT_ACCOUNT_REMOVE, Project.class.getName());
+
+        // Network as a Service
+        entityEventDetails.put(EVENT_NETWORK_ELEMENT_CONFIGURE,Network.class.getName());
+
+        // Physical Network Events
+        entityEventDetails.put(EVENT_PHYSICAL_NETWORK_CREATE, PhysicalNetwork.class.getName());
+        entityEventDetails.put(EVENT_PHYSICAL_NETWORK_DELETE, PhysicalNetwork.class.getName());
+        entityEventDetails.put(EVENT_PHYSICAL_NETWORK_UPDATE, PhysicalNetwork.class.getName());
+
+        // Physical Network Service Provider Events
+        entityEventDetails.put(EVENT_SERVICE_PROVIDER_CREATE, PhysicalNetworkServiceProvider.class.getName());
+        entityEventDetails.put(EVENT_SERVICE_PROVIDER_DELETE, PhysicalNetworkServiceProvider.class.getName());
+        entityEventDetails.put(EVENT_SERVICE_PROVIDER_UPDATE, PhysicalNetworkServiceProvider.class.getName());
+
+        // Physical Network TrafficType Events
+        entityEventDetails.put(EVENT_TRAFFIC_TYPE_CREATE, PhysicalNetworkTrafficType.class.getName());
+        entityEventDetails.put(EVENT_TRAFFIC_TYPE_DELETE, PhysicalNetworkTrafficType.class.getName());
+        entityEventDetails.put(EVENT_TRAFFIC_TYPE_UPDATE, PhysicalNetworkTrafficType.class.getName());
+
+        // external network device events
+        entityEventDetails.put(EVENT_EXTERNAL_LB_DEVICE_ADD, PhysicalNetwork.class.getName());
+        entityEventDetails.put(EVENT_EXTERNAL_LB_DEVICE_DELETE, PhysicalNetwork.class.getName());
+        entityEventDetails.put(EVENT_EXTERNAL_LB_DEVICE_CONFIGURE, PhysicalNetwork.class.getName());
+
+        // external switch management device events (E.g.: Cisco Nexus 1000v Virtual Supervisor Module.
+        entityEventDetails.put(EVENT_EXTERNAL_SWITCH_MGMT_DEVICE_ADD, "Nexus1000v");
+        entityEventDetails.put(EVENT_EXTERNAL_SWITCH_MGMT_DEVICE_DELETE, "Nexus1000v");
+        entityEventDetails.put(EVENT_EXTERNAL_SWITCH_MGMT_DEVICE_CONFIGURE, "Nexus1000v");
+        entityEventDetails.put(EVENT_EXTERNAL_SWITCH_MGMT_DEVICE_ENABLE, "Nexus1000v");
+        entityEventDetails.put(EVENT_EXTERNAL_SWITCH_MGMT_DEVICE_DISABLE, "Nexus1000v");
+
+
+        entityEventDetails.put(EVENT_EXTERNAL_FIREWALL_DEVICE_ADD, PhysicalNetwork.class.getName());
+        entityEventDetails.put(EVENT_EXTERNAL_FIREWALL_DEVICE_DELETE, PhysicalNetwork.class.getName());
+        entityEventDetails.put(EVENT_EXTERNAL_FIREWALL_DEVICE_CONFIGURE, PhysicalNetwork.class.getName());
+
+        // VPC
+        entityEventDetails.put(EVENT_VPC_CREATE, Vpc.class.getName());
+        entityEventDetails.put(EVENT_VPC_UPDATE, Vpc.class.getName());
+        entityEventDetails.put(EVENT_VPC_DELETE, Vpc.class.getName());
+        entityEventDetails.put(EVENT_VPC_RESTART, Vpc.class.getName());
+
+        // VPC offerings
+        entityEventDetails.put(EVENT_VPC_OFFERING_CREATE, Vpc.class.getName());
+        entityEventDetails.put(EVENT_VPC_OFFERING_UPDATE, Vpc.class.getName());
+        entityEventDetails.put(EVENT_VPC_OFFERING_DELETE, Vpc.class.getName());
+
+        // Private gateway
+        entityEventDetails.put(EVENT_PRIVATE_GATEWAY_CREATE, PrivateGateway.class.getName());
+        entityEventDetails.put(EVENT_PRIVATE_GATEWAY_DELETE, PrivateGateway.class.getName());
+
+        // Static routes
+        entityEventDetails.put(EVENT_STATIC_ROUTE_CREATE, StaticRoute.class.getName());
+        entityEventDetails.put(EVENT_STATIC_ROUTE_DELETE, StaticRoute.class.getName());
+
+        // tag related events
+        entityEventDetails.put(EVENT_TAGS_CREATE, "Tag");
+        entityEventDetails.put(EVENT_TAGS_DELETE, "tag");
+
+        // external network device events
+        entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_ADD,  "NvpController");
+        entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_DELETE,  "NvpController");
+        entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_CONFIGURE, "NvpController");
+
+        // AutoScale
+        entityEventDetails.put(EVENT_COUNTER_CREATE, AutoScaleCounter.class.getName());
+        entityEventDetails.put(EVENT_COUNTER_DELETE, AutoScaleCounter.class.getName());
+        entityEventDetails.put(EVENT_CONDITION_CREATE, Condition.class.getName());
+        entityEventDetails.put(EVENT_CONDITION_DELETE, Condition.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEPOLICY_CREATE, AutoScalePolicy.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEPOLICY_UPDATE, AutoScalePolicy.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEPOLICY_DELETE, AutoScalePolicy.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMPROFILE_CREATE, AutoScaleVmProfile.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMPROFILE_DELETE, AutoScaleVmProfile.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMPROFILE_UPDATE, AutoScaleVmProfile.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_CREATE, AutoScaleVmGroup.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_DELETE, AutoScaleVmGroup.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_UPDATE, AutoScaleVmGroup.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_ENABLE, AutoScaleVmGroup.class.getName());
+        entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_DISABLE, AutoScaleVmGroup.class.getName());
+    }
+
+    public static String getEntityForEvent (String eventName) {
+        String entityClassName = entityEventDetails.get(eventName);
+        if (entityClassName == null || entityClassName.isEmpty()) {
+            return null;
+        }
+        int index = entityClassName.lastIndexOf(".");
+        String entityName = entityClassName;
+        if (index != -1) {
+            entityName = entityClassName.substring(index+1);
+        }
+        return entityName;
+    }
 }
diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java
index 413b6d9..a70bf02 100644
--- a/api/src/com/cloud/network/Network.java
+++ b/api/src/com/cloud/network/Network.java
@@ -16,26 +16,25 @@
 // under the License.
 package com.cloud.network;
 
-import org.apache.cloudstack.acl.ControlledEntity;
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.network.Networks.Mode;
 import com.cloud.network.Networks.TrafficType;
-import com.cloud.utils.fsm.FiniteState;
-import com.cloud.utils.fsm.StateMachine;
+import com.cloud.utils.fsm.StateMachine2;
+import com.cloud.utils.fsm.StateObject;
+import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.Identity;
 import org.apache.cloudstack.api.InternalIdentity;
 
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
 /**
  * owned by an account.
  */
-public interface Network extends ControlledEntity, InternalIdentity, Identity {
+public interface Network extends ControlledEntity, StateObject<Network.State>, InternalIdentity, Identity {
 
-    public enum GuestType {
+  public enum GuestType {
         Shared,
         Isolated
     }
@@ -204,7 +203,8 @@
         OperationFailed;
     }
 
-    enum State implements FiniteState<State, Event> {
+    public enum State {
+
         Allocated("Indicates the network configuration is in allocated but not setup"),
         Setup("Indicates the network configuration is setup"),
         Implementing("Indicates the network configuration is being implemented"),
@@ -212,39 +212,8 @@
         Shutdown("Indicates the network configuration is being destroyed"),
         Destroy("Indicates that the network is destroyed");
 
+        protected static final StateMachine2<State, Network.Event, Network> s_fsm = new StateMachine2<State, Network.Event, Network>();
 
-        @Override
-        public StateMachine<State, Event> getStateMachine() {
-            return s_fsm;
-        }
-
-        @Override
-        public State getNextState(Event event) {
-            return s_fsm.getNextState(this, event);
-        }
-
-        @Override
-        public List<State> getFromStates(Event event) {
-            return s_fsm.getFromStates(this, event);
-        }
-
-        @Override
-        public Set<Event> getPossibleEvents() {
-            return s_fsm.getPossibleEvents(this);
-        }
-
-        String _description;
-
-        @Override
-        public String getDescription() {
-            return _description;
-        }
-
-        private State(String description) {
-            _description = description;
-        }
-
-        private static StateMachine<State, Event> s_fsm = new StateMachine<State, Event>();
         static {
             s_fsm.addTransition(State.Allocated, Event.ImplementNetwork, State.Implementing);
             s_fsm.addTransition(State.Implementing, Event.OperationSucceeded, State.Implemented);
@@ -253,8 +222,43 @@
             s_fsm.addTransition(State.Shutdown, Event.OperationSucceeded, State.Allocated);
             s_fsm.addTransition(State.Shutdown, Event.OperationFailed, State.Implemented);
         }
-    }
 
+        public static StateMachine2<State, Network.Event, Network> getStateMachine() {
+            return s_fsm;
+        }
+
+        String _description;
+        private State(String description) {
+            _description = description;
+        }
+    }
+    
+    public class IpAddresses {
+    	private String ip4Address;
+    	private String ip6Address;
+    	
+    	public IpAddresses(String ip4Address, String ip6Address) {
+    		this.setIp4Address(ip4Address);
+    		this.setIp6Address(ip6Address);
+    	}
+
+		public String getIp4Address() {
+			return ip4Address;
+		}
+
+		public void setIp4Address(String ip4Address) {
+			this.ip4Address = ip4Address;
+		}
+
+		public String getIp6Address() {
+			return ip6Address;
+		}
+
+		public void setIp6Address(String ip6Address) {
+			this.ip6Address = ip6Address;
+		}
+    }
+    
     String getName();
 
     Mode getMode();
@@ -266,7 +270,11 @@
     String getGateway();
 
     String getCidr();
-
+    
+    String getIp6Gateway();
+    
+    String getIp6Cidr();
+    
     long getDataCenterId();
 
     long getNetworkOfferingId();
diff --git a/api/src/com/cloud/network/NetworkModel.java b/api/src/com/cloud/network/NetworkModel.java
index 9baa0ec..7e17405 100644
--- a/api/src/com/cloud/network/NetworkModel.java
+++ b/api/src/com/cloud/network/NetworkModel.java
@@ -249,4 +249,5 @@
     
     boolean isNetworkInlineMode(Network network);
 
+	Vlan getVlanForNetwork(long networkId);
 }
\ No newline at end of file
diff --git a/api/src/com/cloud/network/NetworkProfile.java b/api/src/com/cloud/network/NetworkProfile.java
index bb59b04..e3be941 100644
--- a/api/src/com/cloud/network/NetworkProfile.java
+++ b/api/src/com/cloud/network/NetworkProfile.java
@@ -39,6 +39,8 @@
     private TrafficType trafficType;
     private String gateway;
     private String cidr;
+    private String ip6Gateway;
+    private String ip6Cidr;
     private long networkOfferingId;
     private long related;
     private String displayText;
@@ -64,6 +66,8 @@
         this.trafficType = network.getTrafficType();
         this.gateway = network.getGateway();
         this.cidr = network.getCidr();
+        this.ip6Gateway = network.getIp6Gateway();
+        this.ip6Cidr = network.getIp6Cidr();
         this.networkOfferingId = network.getNetworkOfferingId();
         this.related = network.getRelated();
         this.displayText = network.getDisplayText();
@@ -230,4 +234,14 @@
     public void setTrafficType(TrafficType type) {
         this.trafficType = type;
     }
+
+	@Override
+	public String getIp6Gateway() {
+		return ip6Gateway;
+	}
+
+	@Override
+	public String getIp6Cidr() {
+		return ip6Cidr;
+	}
 }
diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java
index b156619..e3d2158 100755
--- a/api/src/com/cloud/network/Networks.java
+++ b/api/src/com/cloud/network/Networks.java
@@ -48,7 +48,7 @@
     public enum AddressFormat {
         Ip4,
         Ip6,
-        Mixed
+        DualStack
     }
 
     /**
diff --git a/api/src/com/cloud/storage/Snapshot.java b/api/src/com/cloud/storage/Snapshot.java
index 99bdee6..2e2965a 100644
--- a/api/src/com/cloud/storage/Snapshot.java
+++ b/api/src/com/cloud/storage/Snapshot.java
@@ -16,14 +16,16 @@
 // under the License.
 package com.cloud.storage;
 
-import java.util.Date;
-
-import org.apache.cloudstack.acl.ControlledEntity;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.utils.fsm.StateMachine2;
+import com.cloud.utils.fsm.StateObject;
+import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.Identity;
 import org.apache.cloudstack.api.InternalIdentity;
 
-public interface Snapshot extends ControlledEntity, Identity, InternalIdentity {
+import java.util.Date;
+
+public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, StateObject<Snapshot.State> {
     public enum Type {
         MANUAL,
         RECURRING,
@@ -51,13 +53,29 @@
         }
     }
 
-    public enum Status {
+    public enum State {
         Creating,
         CreatedOnPrimary,
         BackingUp,
         BackedUp,
         Error;
 
+        private final static StateMachine2<State, Event, Snapshot> s_fsm = new StateMachine2<State, Event, Snapshot>();
+
+        public static StateMachine2<State, Event, Snapshot> getStateMachine() {
+            return s_fsm;
+        }
+
+        static {
+            s_fsm.addTransition(null, Event.CreateRequested, Creating);
+            s_fsm.addTransition(Creating, Event.OperationSucceeded, CreatedOnPrimary);
+            s_fsm.addTransition(Creating, Event.OperationNotPerformed, BackedUp);
+            s_fsm.addTransition(Creating, Event.OperationFailed, Error);
+            s_fsm.addTransition(CreatedOnPrimary, Event.BackupToSecondary, BackingUp);
+            s_fsm.addTransition(BackingUp, Event.OperationSucceeded, BackedUp);
+            s_fsm.addTransition(BackingUp, Event.OperationFailed, Error);
+        }
+
         public String toString() {
             return this.name();
         }
@@ -67,6 +85,15 @@
         }
     }
 
+    enum Event {
+        CreateRequested,
+        OperationNotPerformed,
+        BackupToSecondary,
+        BackedupToSecondary,
+        OperationSucceeded,
+        OperationFailed
+    }
+
     public static final long MANUAL_POLICY_ID = 0L;
 
     long getAccountId();
@@ -81,7 +108,7 @@
 
     Type getType();
 
-    Status getStatus();
+    State getState();
 
     HypervisorType getHypervisorType();
 
diff --git a/api/src/com/cloud/vm/Nic.java b/api/src/com/cloud/vm/Nic.java
index 84c0034..aeae2ae 100644
--- a/api/src/com/cloud/vm/Nic.java
+++ b/api/src/com/cloud/vm/Nic.java
@@ -144,4 +144,10 @@
     VirtualMachine.Type getVmType();
 
     AddressFormat getAddressFormat();
+
+	String getIp6Gateway();
+
+	String getIp6Cidr();
+
+	String getIp6Address();
 }
diff --git a/api/src/com/cloud/vm/NicProfile.java b/api/src/com/cloud/vm/NicProfile.java
index 32e3f21..9a5e781 100644
--- a/api/src/com/cloud/vm/NicProfile.java
+++ b/api/src/com/cloud/vm/NicProfile.java
@@ -37,6 +37,8 @@
     TrafficType trafficType;
     String ip4Address;
     String ip6Address;
+    String ip6Gateway;
+    String ip6Cidr;
     String macAddress;
     URI isolationUri;
     String netmask;
@@ -50,7 +52,8 @@
     Integer networkRate;
     boolean isSecurityGroupEnabled;
     String name;
-    String requestedIp;
+    String requestedIpv4;
+    String requestedIpv6;
 
     public String getDns1() {
         return dns1;
@@ -218,7 +221,7 @@
         this.trafficType = network.getTrafficType();
         this.ip4Address = nic.getIp4Address();
         this.format = nic.getAddressFormat();
-        this.ip6Address = null;
+        this.ip6Address = nic.getIp6Address();
         this.macAddress = nic.getMacAddress();
         this.reservationId = nic.getReservationId();
         this.strategy = nic.getReservationStrategy();
@@ -230,6 +233,8 @@
         this.isSecurityGroupEnabled = isSecurityGroupEnabled;
         this.vmId = nic.getInstanceId();
         this.name = name;
+        this.ip6Cidr = nic.getIp6Cidr();
+        this.ip6Gateway = nic.getIp6Gateway();
 
         if (networkRate != null) {
             this.networkRate = networkRate;
@@ -245,8 +250,9 @@
         this.strategy = strategy;
     }
 
-    public NicProfile(String requestedIp) {
-        this.requestedIp = requestedIp;
+    public NicProfile(String requestedIpv4, String requestedIpv6) {
+        this.requestedIpv4 = requestedIpv4;
+        this.requestedIpv6 = requestedIpv6;
     }
 
     public NicProfile() {
@@ -272,8 +278,8 @@
         this.isSecurityGroupEnabled = enabled;
     }
 
-    public String getRequestedIp() {
-        return requestedIp;
+    public String getRequestedIpv4() {
+        return requestedIpv4;
     }
 
     public void deallocate() {
@@ -301,4 +307,28 @@
                 append(reservationId).append("-").append(ip4Address).append("-").append(broadcastUri).toString();
     }
 
+	public String getIp6Gateway() {
+		return ip6Gateway;
+	}
+
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+	public String getIp6Cidr() {
+		return ip6Cidr;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
+
+	public String getRequestedIpv6() {
+		return requestedIpv6;
+	}
+
+	public void setRequestedIpv6(String requestedIpv6) {
+		this.requestedIpv6 = requestedIpv6;
+	}
+
 }
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index 768ea95..9e7f777 100755
--- a/api/src/com/cloud/vm/UserVmService.java
+++ b/api/src/com/cloud/vm/UserVmService.java
@@ -47,6 +47,7 @@
 import com.cloud.exception.VirtualMachineMigrationException;
 import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.Network.IpAddresses;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.Volume;
@@ -228,7 +229,7 @@
      * @throws InsufficientResourcesException
      */
     UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList, Account owner, String hostName,
-            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps, String defaultIp, String keyboard)
+            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp, String keyboard)
             throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -275,7 +276,7 @@
      *            - name of the ssh key pair used to login to the virtual machine
      * @param requestedIps
      *            TODO
-     * @param defaultIp
+     * @param defaultIps
      *            TODO
      * @param accountName
      *            - an optional account for the virtual machine. Must be used with domainId
@@ -293,8 +294,8 @@
      * @throws InsufficientResourcesException
      */
     UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, List<Long> securityGroupIdList,
-            Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps,
-            String defaultIp, String keyboard)
+            Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+            IpAddresses defaultIps, String keyboard)
             throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -339,8 +340,7 @@
      *            - name of the ssh key pair used to login to the virtual machine
      * @param requestedIps
      *            TODO
-     * @param defaultIp
-     *            TODO
+     * @param defaultIps TODO
      * @param accountName
      *            - an optional account for the virtual machine. Must be used with domainId
      * @param domainId
@@ -357,7 +357,7 @@
      * @throws InsufficientResourcesException
      */
     UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner, String hostName,
-            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps, String defaultIp, String keyboard)
+            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard)
             throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 9d620ab..f704aec 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -38,6 +38,7 @@
     public static final String DOMAIN_SUFFIX = "domainsuffix";
     public static final String DNS_SEARCH_ORDER = "dnssearchorder";
     public static final String CIDR = "cidr";
+    public static final String IP6_CIDR = "ip6cidr";
     public static final String CIDR_LIST = "cidrlist";
     public static final String CLEANUP = "cleanup";
     public static final String CLUSTER_ID = "clusterid";
@@ -64,6 +65,7 @@
     public static final String EMAIL = "email";
     public static final String END_DATE = "enddate";
     public static final String END_IP = "endip";
+    public static final String END_IPV6 = "endipv6";
     public static final String END_PORT = "endport";
     public static final String ENTRY_TIME = "entrytime";
     public static final String FETCH_LATEST = "fetchlatest";
@@ -73,6 +75,7 @@
     public static final String FORMAT = "format";
     public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
     public static final String GATEWAY = "gateway";
+    public static final String IP6_GATEWAY = "ip6gateway";
     public static final String GROUP = "group";
     public static final String GROUP_ID = "groupid";
     public static final String GUEST_CIDR_ADDRESS = "guestcidraddress";
@@ -90,6 +93,7 @@
     public static final String INTERNAL_DNS2 = "internaldns2";
     public static final String INTERVAL_TYPE = "intervaltype";
     public static final String IP_ADDRESS = "ipaddress";
+    public static final String IP6_ADDRESS = "ip6address";
     public static final String IP_ADDRESS_ID = "ipaddressid";
     public static final String IS_ASYNC = "isasync";
     public static final String IP_AVAILABLE = "ipavailable";
@@ -181,6 +185,7 @@
     public static final String SOURCE_ZONE_ID = "sourcezoneid";
     public static final String START_DATE = "startdate";
     public static final String START_IP = "startip";
+    public static final String START_IPV6 = "startipv6";
     public static final String START_PORT = "startport";
     public static final String STATE = "state";
     public static final String STATUS = "status";
diff --git a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
index 870a591..bbd8b5a 100644
--- a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
@@ -113,6 +113,18 @@
             description="the VPC network belongs to")
     private Long vpcId;
 
+    @Parameter(name=ApiConstants.START_IPV6, type=CommandType.STRING, description="the beginning IPv6 address in the IPv6 network range")
+    private String startIpv6;
+
+    @Parameter(name=ApiConstants.END_IPV6, type=CommandType.STRING, description="the ending IPv6 address in the IPv6 network range")
+    private String endIpv6;
+
+    @Parameter(name=ApiConstants.IP6_GATEWAY, type=CommandType.STRING, description="the gateway of the IPv6 network. Required " +
+            "for Shared networks and Isolated networks when it belongs to VPC")
+    private String ip6Gateway;
+    
+    @Parameter(name=ApiConstants.IP6_CIDR, type=CommandType.STRING, description="the CIDR of IPv6 network, must be at least /64")
+    private String ip6Cidr;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -207,7 +219,35 @@
         }
     }
 
-    /////////////////////////////////////////////////////
+    public String getStartIpv6() {
+    	if (startIpv6 == null) {
+    		return null;
+    	}
+        return startIpv6.toLowerCase();
+    }
+
+    public String getEndIpv6() {
+    	if (endIpv6 == null) {
+    		return null;
+    	}
+        return endIpv6.toLowerCase();
+    }
+
+    public String getIp6Gateway() {
+    	if (ip6Gateway == null) {
+    		return null;
+    	}
+        return ip6Gateway.toLowerCase();
+    }
+
+    public String getIp6Cidr() {
+    	if (ip6Cidr == null) {
+    		return null;
+    	}
+        return ip6Cidr.toLowerCase();
+    }
+
+	/////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
     @Override
@@ -228,6 +268,10 @@
     @Override
     // an exception thrown by createNetwork() will be caught by the dispatcher.
     public void execute() throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException{
+        if (getStartIpv6() != null && getStartIp() != null) {
+        	throw new InvalidParameterValueException("Cannot support dualstack at this moment!");
+        }
+        
         Network result = _networkService.createGuestNetwork(this);
         if (result != null) {
             NetworkResponse response = _responseGenerator.createNetworkResponse(result);
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 5300e3c..b21b53c 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -51,6 +51,7 @@
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
+import com.cloud.network.Network.IpAddresses;
 import com.cloud.offering.DiskOffering;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.template.VirtualMachineTemplate;
@@ -148,12 +149,15 @@
 
     @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP,
             description = "ip to network mapping. Can't be specified with networkIds parameter." +
-                    " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid")
+                    " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid")
     private Map ipToNetworkList;
 
     @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="the ip address for default vm's network")
     private String ipAddress;
 
+    @Parameter(name=ApiConstants.IP6_ADDRESS, type=CommandType.STRING, description="the ipv6 address for default vm's network")
+    private String ip6Address;
+
     @Parameter(name=ApiConstants.KEYBOARD, type=CommandType.STRING, description="an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us")
     private String keyboard;
 
@@ -244,7 +248,7 @@
 
     public List<Long> getNetworkIds() {
        if (ipToNetworkList != null) {
-           if (networkIds != null || ipAddress != null) {
+           if (networkIds != null || ipAddress != null || getIp6Address() != null) {
                throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress");
            } else {
                List<Long> networks = new ArrayList<Long>();
@@ -271,13 +275,13 @@
         return startVm == null ? true : startVm;
     }
 
-    private Map<Long, String> getIpToNetworkMap() {
-        if ((networkIds != null || ipAddress != null) && ipToNetworkList != null) {
+    private Map<Long, IpAddresses> getIpToNetworkMap() {
+        if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) {
             throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter");
         }
-        LinkedHashMap<Long, String> ipToNetworkMap = null;
+        LinkedHashMap<Long, IpAddresses> ipToNetworkMap = null;
         if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) {
-            ipToNetworkMap = new LinkedHashMap<Long, String>();
+            ipToNetworkMap = new LinkedHashMap<Long, IpAddresses>();
             Collection ipsCollection = ipToNetworkList.values();
             Iterator iter = ipsCollection.iterator();
             while (iter.hasNext()) {
@@ -294,12 +298,27 @@
                     }
                 }
                 String requestedIp = (String) ips.get("ip");
-                ipToNetworkMap.put(networkId, requestedIp);
+                String requestedIpv6 = (String) ips.get("ipv6");
+                if (requestedIpv6 != null) {
+                	requestedIpv6 = requestedIpv6.toLowerCase();
+                }
+                if (requestedIpv6 != null) {
+                	throw new InvalidParameterValueException("Cannot support specified IPv6 address!");
+                }
+                IpAddresses addrs = new IpAddresses(requestedIp, requestedIpv6);
+                ipToNetworkMap.put(networkId, addrs);
             }
         }
 
         return ipToNetworkMap;
     }
+    
+	public String getIp6Address() {
+		if (ip6Address == null) {
+			return null;
+		}
+		return ip6Address.toLowerCase();
+	}
 
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
@@ -388,6 +407,10 @@
     @Override
     public void create() throws ResourceAllocationException{
         try {
+        	if (getIp6Address() != null) {
+        		throw new InvalidParameterValueException("Cannot support specified IPv6 address!");
+        	}
+        	
             //Verify that all objects exist before passing them to the service
             Account owner = _accountService.getActiveAccountById(getEntityOwnerId());
 
@@ -428,23 +451,24 @@
             if (getHypervisor() == HypervisorType.BareMetal) {
                 vm = _bareMetalVmService.createVirtualMachine(this);
             } else {
+            	IpAddresses addrs = new IpAddresses(ipAddress, getIp6Address());
                 if (zone.getNetworkType() == NetworkType.Basic) {
                     if (getNetworkIds() != null) {
                         throw new InvalidParameterValueException("Can't specify network Ids in Basic zone");
                     } else {
                         vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(), owner, name,
-                                displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), ipAddress, keyboard);
+                                displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard);
                     }
                 } else {
                     if (zone.isSecurityGroupEnabled())  {
                         vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(),
-                                owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), ipAddress, keyboard);
+                                owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard);
                     } else {
                         if (getSecurityGroupIdList() != null && !getSecurityGroupIdList().isEmpty()) {
                             throw new InvalidParameterValueException("Can't create vm with security groups; security group feature is not enabled per zone");
                         }
                         vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, getNetworkIds(), owner, name, displayName,
-                                diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), ipAddress, keyboard);
+                                diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard);
                     }
                 }
             }
@@ -467,4 +491,5 @@
             throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
         }
     }
+
 }
diff --git a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java
index 64cc953..ba8ea32 100644
--- a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java
@@ -144,6 +144,12 @@
     @SerializedName(ApiConstants.TAGS)  @Param(description="the list of resource tags associated with network", responseObject = ResourceTagResponse.class)
     private List<ResourceTagResponse> tags;
 
+    @SerializedName(ApiConstants.IP6_GATEWAY) @Param(description="the gateway of IPv6 network")
+    private String ip6Gateway;
+    
+    @SerializedName(ApiConstants.IP6_CIDR) @Param(description="the cidr of IPv6 network")
+    private String ip6Cidr;
+    
     public void setId(String id) {
         this.id = id;
     }
@@ -298,4 +304,12 @@
     public void setTags(List<ResourceTagResponse> tags) {
         this.tags = tags;
     }
+
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
 }
diff --git a/api/src/org/apache/cloudstack/api/response/NicResponse.java b/api/src/org/apache/cloudstack/api/response/NicResponse.java
index 7e200ae..36cf506 100644
--- a/api/src/org/apache/cloudstack/api/response/NicResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/NicResponse.java
@@ -63,6 +63,15 @@
     @SerializedName("macaddress") @Param(description="true if nic is default, false otherwise")
     private String macAddress;
 
+    @SerializedName(ApiConstants.IP6_GATEWAY) @Param(description="the gateway of IPv6 network")
+    private String ip6Gateway;
+    
+    @SerializedName(ApiConstants.IP6_CIDR) @Param(description="the cidr of IPv6 network")
+    private String ip6Cidr;
+    
+    @SerializedName(ApiConstants.IP6_ADDRESS) @Param(description="the IPv6 address of network")
+    private String ip6Address;
+    
     public String getId() {
         return id;
     }
@@ -115,6 +124,18 @@
     public void setMacAddress(String macAddress) {
         this.macAddress = macAddress;
     }
+    
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
+
+	public void setIp6Address(String ip6Address) {
+		this.ip6Address = ip6Address;
+	}
 
     @Override
     public int hashCode() {
@@ -142,5 +163,4 @@
             return false;
         return true;
     }
-
 }
diff --git a/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java b/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java
index 8ea0d7f..58b7cf1 100644
--- a/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java
@@ -16,16 +16,16 @@
 // under the License.
 package org.apache.cloudstack.api.response;
 
-import java.util.Date;
-import java.util.List;
-
-import org.apache.cloudstack.api.ApiConstants;
 import com.cloud.serializer.Param;
 import com.cloud.storage.Snapshot;
 import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseResponse;
 import org.apache.cloudstack.api.EntityReference;
 
+import java.util.Date;
+import java.util.List;
+
 @EntityReference(value=Snapshot.class)
 @SuppressWarnings("unused")
 public class SnapshotResponse extends BaseResponse implements ControlledEntityResponse {
@@ -81,7 +81,7 @@
 
     @SerializedName(ApiConstants.STATE)
     @Param(description = "the state of the snapshot. BackedUp means that snapshot is ready to be used; Creating - the snapshot is being allocated on the primary storage; BackingUp - the snapshot is being backed up on secondary storage")
-    private Snapshot.Status state;
+    private Snapshot.State state;
 
     @SerializedName(ApiConstants.TAGS)  @Param(description="the list of resource tags associated with snapshot", responseObject = ResourceTagResponse.class)
     private List<ResourceTagResponse> tags;
@@ -149,7 +149,7 @@
         this.intervalType = intervalType;
     }
 
-    public void setState(Snapshot.Status state) {
+    public void setState(Snapshot.State state) {
         this.state = state;
     }
 
diff --git a/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java b/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
index bfde739..e3cac68 100644
--- a/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
@@ -80,6 +80,19 @@
     @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network this belongs to")
     private String physicalNetworkId;
 
+    @SerializedName(ApiConstants.START_IPV6) @Param(description="the start ipv6 of the VLAN IP range")
+    private String startIpv6;
+
+    @SerializedName(ApiConstants.END_IPV6) @Param(description="the end ipv6 of the VLAN IP range")
+    private String endIpv6;
+    
+    @SerializedName(ApiConstants.IP6_GATEWAY) @Param(description="the gateway of IPv6 network")
+    private String ip6Gateway;
+    
+    @SerializedName(ApiConstants.IP6_CIDR) @Param(description="the cidr of IPv6 network")
+    private String ip6Cidr;
+    
+
     public void setId(String id) {
         this.id = id;
     }
@@ -157,4 +170,24 @@
     public String getphysicalNetworkId() {
         return physicalNetworkId;
     }
+
+	public String getStartIpv6() {
+		return startIpv6;
+	}
+
+	public void setStartIpv6(String startIpv6) {
+		this.startIpv6 = startIpv6;
+	}
+
+	public void setEndIpv6(String endIpv6) {
+		this.endIpv6 = endIpv6;
+	}
+
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
 }
diff --git a/awsapi/src/com/cloud/bridge/model/CloudStackUserVO.java b/awsapi/src/com/cloud/bridge/model/CloudStackUserVO.java
new file mode 100644
index 0000000..fe1ec94
--- /dev/null
+++ b/awsapi/src/com/cloud/bridge/model/CloudStackUserVO.java
@@ -0,0 +1,49 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.bridge.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="user")
+public class CloudStackUserVO {
+
+    @Column(name="api_key")
+    private String apiKey;
+
+    @Column(name="secret_key")
+    private String secretKey;
+
+    public String getApiKey() {
+        return apiKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setApiKey(String apiKey) {
+        this.apiKey = apiKey;
+    }
+
+   public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+}
diff --git a/awsapi/src/com/cloud/bridge/persist/dao/CloudStackUserDao.java b/awsapi/src/com/cloud/bridge/persist/dao/CloudStackUserDao.java
new file mode 100644
index 0000000..f5d4aa1
--- /dev/null
+++ b/awsapi/src/com/cloud/bridge/persist/dao/CloudStackUserDao.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.bridge.persist.dao;
+
+import com.cloud.bridge.model.CloudStackUserVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface CloudStackUserDao extends GenericDao<CloudStackUserVO, String> {
+
+    public String getSecretKeyByAccessKey(String acessKey);
+
+}
diff --git a/awsapi/src/com/cloud/bridge/persist/dao/CloudStackUserDaoImpl.java b/awsapi/src/com/cloud/bridge/persist/dao/CloudStackUserDaoImpl.java
new file mode 100644
index 0000000..0613519
--- /dev/null
+++ b/awsapi/src/com/cloud/bridge/persist/dao/CloudStackUserDaoImpl.java
@@ -0,0 +1,66 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.bridge.persist.dao;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
+
+import com.cloud.bridge.model.CloudStackUserVO;
+import com.cloud.bridge.util.EncryptionSecretKeyCheckerUtil;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+
+@Local(value={CloudStackUserDao.class})
+public class CloudStackUserDaoImpl extends GenericDaoBase<CloudStackUserVO, String> implements CloudStackUserDao {
+    public static final Logger logger = Logger.getLogger(CloudStackUserDaoImpl.class);
+
+    public CloudStackUserDaoImpl() {}
+
+    @Override
+    public String getSecretKeyByAccessKey( String accessKey ) {
+        CloudStackUserVO user = null;
+        String cloudSecretKey = null;
+
+        SearchBuilder <CloudStackUserVO> searchByAccessKey = createSearchBuilder();
+        searchByAccessKey.and("apiKey", searchByAccessKey.entity().getApiKey(), SearchCriteria.Op.EQ);
+        searchByAccessKey.done();
+        Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+        try {
+            txn.start();
+            SearchCriteria<CloudStackUserVO> sc = searchByAccessKey.create();
+            sc.setParameters("apiKey", accessKey);
+            user =  findOneBy(sc);
+            if ( user != null && user.getSecretKey() != null) {
+                // if the cloud db is encrypted, decrypt the secret_key returned by cloud db before signature generation
+                if( EncryptionSecretKeyCheckerUtil.useEncryption() ) {
+                    StandardPBEStringEncryptor encryptor = EncryptionSecretKeyCheckerUtil.getEncryptor();
+                    cloudSecretKey = encryptor.decrypt( user.getSecretKey() );
+                } else {
+                    cloudSecretKey = user.getSecretKey();
+                }
+            }
+            return cloudSecretKey;
+        } finally {
+            txn.close();
+        }
+    }
+
+}
diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java
index 4f74873..4260e9f 100644
--- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java
+++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java
@@ -56,6 +56,7 @@
 import org.apache.axis2.databinding.utils.writer.MTOMAwareXMLSerializer;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.log4j.Logger;
+import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
 
 import com.amazon.ec2.AllocateAddressResponse;
 import com.amazon.ec2.AssociateAddressResponse;
@@ -94,7 +95,9 @@
 import com.amazon.ec2.StartInstancesResponse;
 import com.amazon.ec2.StopInstancesResponse;
 import com.amazon.ec2.TerminateInstancesResponse;
+import com.cloud.bridge.model.CloudStackUserVO;
 import com.cloud.bridge.model.UserCredentialsVO;
+import com.cloud.bridge.persist.dao.CloudStackUserDaoImpl;
 import com.cloud.bridge.persist.dao.OfferingDaoImpl;
 import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl;
 import com.cloud.bridge.service.controller.s3.ServiceProvider;
@@ -138,6 +141,7 @@
 import com.cloud.bridge.util.AuthenticationUtils;
 import com.cloud.bridge.util.ConfigurationHelper;
 import com.cloud.bridge.util.EC2RestAuth;
+import com.cloud.bridge.util.EncryptionSecretKeyCheckerUtil;
 import com.cloud.stack.models.CloudStackAccount;
 import com.cloud.utils.component.ComponentLocator;
 import com.cloud.utils.db.Transaction;
@@ -147,6 +151,7 @@
 
 	private static final long serialVersionUID = -6168996266762804888L;
 	protected final UserCredentialsDaoImpl ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class);
+    protected final CloudStackUserDaoImpl userDao = ComponentLocator.inject(CloudStackUserDaoImpl.class);
 	protected final OfferingDaoImpl ofDao = ComponentLocator.inject(OfferingDaoImpl.class);
 	
 	public static final Logger logger = Logger.getLogger(EC2RestServlet.class);
@@ -676,48 +681,65 @@
         String[] groupName = request.getParameterValues( "GroupName" );
 		if ( null != groupName && 0 < groupName.length ) 
 			 EC2request.setName( groupName[0] );
-		else { response.sendError(530, "Missing GroupName parameter" ); return; }
+        else { response.sendError(530, "Missing GroupName parameter" ); return; }
 
-		EC2IpPermission perm = new EC2IpPermission();       	
+        // -> not clear how many parameters there are until we fail to get IpPermissions.n.IpProtocol
+        int nCount = 1, mCount;
+        do  {
+            EC2IpPermission perm = new EC2IpPermission();
 
-        String[] protocol = request.getParameterValues( "IpProtocol" );
-		if ( null != protocol && 0 < protocol.length ) 
-		     perm.setProtocol( protocol[0] );
-		else { response.sendError(530, "Missing IpProtocol parameter" ); return; }
+            String[] protocol = request.getParameterValues( "IpPermissions." + nCount + ".IpProtocol" );
+            if ( null != protocol && 0 < protocol.length )
+                perm.setProtocol( protocol[0]);
+            else break;
 
-        String[] fromPort = request.getParameterValues( "FromPort" );
-	    if ( null != fromPort && 0 < fromPort.length ) 
-	    	 perm.setProtocol( fromPort[0] );
-		else { response.sendError(530, "Missing FromPort parameter" ); return; }
+            String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" );
+            if ( null != fromPort && 0 < fromPort.length)
+                perm.setFromPort( Integer.parseInt( fromPort[0]));
 
-        String[] toPort = request.getParameterValues( "ToPort" );
-		if ( null != toPort && 0 < toPort.length ) 
-			 perm.setProtocol( toPort[0] );
-		else { response.sendError(530, "Missing ToPort parameter" ); return; }
-		    		    
-	    String[] ranges = request.getParameterValues( "CidrIp" );
-		if ( null != ranges && 0 < ranges.length) 
-		 	 perm.addIpRange( ranges[0] );
-		else { response.sendError(530, "Missing CidrIp parameter" ); return; }
-		
-	    String[] user = request.getParameterValues( "SourceSecurityGroupOwnerId" );
-		if ( null == user || 0 == user.length) { 
-		     response.sendError(530, "Missing SourceSecurityGroupOwnerId parameter" ); 
-		     return; 
-		}
-	
-		String[] name = request.getParameterValues( "SourceSecurityGroupName" );
-		if ( null == name || 0 == name.length) {
-		     response.sendError(530, "Missing SourceSecurityGroupName parameter" ); 
-		     return; 		
-		}
+            String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" );
+            if ( null != toPort && 0 < toPort.length)
+                perm.setToPort( Integer.parseInt( toPort[0]));
 
-		EC2SecurityGroup group = new EC2SecurityGroup();
-		group.setAccount( user[0] );
-		group.setName( name[0] );
-		perm.addUser( group );
-	    EC2request.addIpPermission( perm );	
-		
+            // -> list: IpPermissions.n.IpRanges.m.CidrIp
+            mCount = 1;
+            do {
+                String[] ranges = request.getParameterValues( "IpPermissions." + nCount + ".IpRanges." + mCount + ".CidrIp" );
+                if ( null != ranges && 0 < ranges.length)
+                    perm.addIpRange( ranges[0]);
+                else break;
+                mCount++;
+            } while( true );
+
+            // -> list: IpPermissions.n.Groups.m.UserId and IpPermissions.n.Groups.m.GroupName
+            mCount = 1;
+            do {
+                EC2SecurityGroup group = new EC2SecurityGroup();
+
+                String[] user = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".UserId" );
+                if ( null != user && 0 < user.length)
+                    group.setAccount( user[0]);
+                else break;
+
+                String[] name = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".GroupName" );
+                if ( null != name && 0 < name.length)
+                    group.setName( name[0]);
+                else break;
+
+                perm.addUser( group);
+                mCount++;
+            } while( true );
+
+            // -> multiple IP permissions can be specified per group name
+            EC2request.addIpPermission( perm);
+            nCount++;
+        } while( true );
+
+        if (1 == nCount) {
+            response.sendError(530, "At least one IpPermissions required" );
+            return;
+        }
+
 	    // -> execute the request
         RevokeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl.toRevokeSecurityGroupIngressResponse( 
         		ServiceProvider.getInstance().getEC2Engine().revokeSecurityGroup( EC2request ));
@@ -732,7 +754,7 @@
         String[] groupName = request.getParameterValues( "GroupName" );
 		if ( null != groupName && 0 < groupName.length ) 
 			 EC2request.setName( groupName[0] );
-		else { response.sendError(530, "Missing GroupName parameter" ); return; }
+        else { response.sendError(530, "Missing GroupName parameter" ); return; }
 
 		// -> not clear how many parameters there are until we fail to get IpPermissions.n.IpProtocol
 		int nCount = 1;
@@ -745,16 +767,18 @@
 		    else break;
 
             String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" );
-		    if (null != fromPort && 0 < fromPort.length) perm.setProtocol( fromPort[0] );
+            if ( null != fromPort && 0 < fromPort.length)
+                perm.setFromPort( Integer.parseInt( fromPort[0]));
 
             String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" );
-		    if (null != toPort && 0 < toPort.length) perm.setProtocol( toPort[0] );
-		    		    
+            if ( null != toPort && 0 < toPort.length)
+                perm.setToPort( Integer.parseInt( toPort[0]));
+
             // -> list: IpPermissions.n.IpRanges.m.CidrIp
 			int mCount = 1;
 	        do 
 	        {  String[] ranges = request.getParameterValues( "IpPermissions." + nCount + ".IpRanges." + mCount + ".CidrIp" );
-		       if ( null != ranges && 0 < ranges.length) 
+               if ( null != ranges && 0 < ranges.length)
 		    	    perm.addIpRange( ranges[0] );
 		       else break;
 		       mCount++;
@@ -1715,18 +1739,14 @@
              }
 		} 
 		
-		// [B] Use the cloudAccessKey to get the users secret key in the db
-	    UserCredentialsVO cloudKeys = ucDao.getByAccessKey( cloudAccessKey );
+        // [B] Use the access key to get the users secret key from the cloud DB
+        cloudSecretKey = userDao.getSecretKeyByAccessKey( cloudAccessKey );
+        if ( cloudSecretKey == null ) {
+            logger.debug("No Secret key found for Access key '" + cloudAccessKey + "' in the the EC2 service");
+            throw new EC2ServiceException( ClientError.AuthFailure, "No Secret key found for Access key '" + cloudAccessKey +
+                    "' in the the EC2 service" );
+        }
 
-	    if ( null == cloudKeys ) 
-	    {
-	    	 logger.debug( cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" );
-	         response.sendError(404, cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); 
-	         return false; 
-	    }
-		else cloudSecretKey = cloudKeys.getSecretKey(); 
-
-		
 		// [C] Verify the signature
 		//  -> getting the query-string in this way maintains its URL encoding
 	    EC2RestAuth restAuth = new EC2RestAuth();
@@ -1751,8 +1771,8 @@
                     String paramName = (String) params.nextElement();
                     // exclude the signature string obviously. ;)
                     if (paramName.equalsIgnoreCase("Signature")) continue;
-                    if (queryString == null) 
-                        queryString = paramName + "=" + request.getParameter(paramName);
+                    if (queryString == null)
+                        queryString = paramName + "=" + URLEncoder.encode(request.getParameter(paramName), "UTF-8"); 
                     else 
                         queryString = queryString + "&" + paramName + "=" + URLEncoder.encode(request.getParameter(paramName), "UTF-8"); 
                 }
diff --git a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java
index 6ac8c97..b2451c6 100644
--- a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java
+++ b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java
@@ -1379,7 +1379,7 @@
 	        param7.setDnsName( "" );
 	        param7.setReason( "" );
             param7.setKeyName( inst.getKeyPairName());
-	        param7.setAmiLaunchIndex( "" );
+            param7.setAmiLaunchIndex( null );
 	        param7.setInstanceType( inst.getServiceOffering());
 	        
 	        ProductCodesSetType param9 = new ProductCodesSetType();
@@ -1701,7 +1701,7 @@
 	        param7.setDnsName( "" );
 	        param7.setReason( "" );
             param7.setKeyName( inst.getKeyPairName());
-	        param7.setAmiLaunchIndex( "" );
+            param7.setAmiLaunchIndex( null );
 	        
 	        ProductCodesSetType param9 = new ProductCodesSetType();
 	        ProductCodesSetItemType param10 = new ProductCodesSetItemType();
diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AddressFilterSet.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AddressFilterSet.java
index cb21fb2..1823b26 100644
--- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AddressFilterSet.java
+++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AddressFilterSet.java
@@ -63,22 +63,21 @@
 	}
 
 
-	public EC2DescribeAddressesResponse evaluate( List<EC2Address> addresses) throws ParseException	{
+    public EC2DescribeAddressesResponse evaluate( EC2DescribeAddressesResponse response) throws ParseException	{
 		EC2DescribeAddressesResponse resultList = new EC2DescribeAddressesResponse();
 		
 		boolean matched;
 		
+        EC2Address[] addresses = response.getAddressSet();
 		EC2Filter[] filterSet = getFilterSet();
 		for ( EC2Address address : addresses ) {
 			matched = true;
-			if (filterSet != null) {
-				for (EC2Filter filter : filterSet) {
-					if (!filterMatched(address, filter)) {
-						matched = false;
-						break;
-					}
-				}
-			}
+            for (EC2Filter filter : filterSet) {
+                if (!filterMatched(address, filter)) {
+                    matched = false;
+                    break;
+                }
+            }
 			if (matched == true)
 				resultList.addAddress(address);
 
diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java
index eb25249..17752af 100644
--- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java
+++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java
@@ -310,7 +310,7 @@
 				throw new EC2ServiceException(ClientError.InvalidGroup_NotFound, "Cannot find matching ruleid.");
 
 			CloudStackInfoResponse resp = getApi().revokeSecurityGroupIngress(ruleId);
-			if (resp != null && resp.getId() != null) {
+            if (resp != null) {
 				return resp.getSuccess();
 			}
 			return false;
@@ -341,7 +341,7 @@
 					pair.setKeyValue(group.getAccount(), group.getName());
 					secGroupList.add(pair);
 				}
-				CloudStackSecurityGroupIngress resp = null;
+                CloudStackSecurityGroup resp = null;
 				if (ipPerm.getProtocol().equalsIgnoreCase("icmp")) {
 					resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null, null, 
 							ipPerm.getIcmpCode(), ipPerm.getIcmpType(), ipPerm.getProtocol(), null, 
@@ -351,10 +351,13 @@
 							ipPerm.getToPort().longValue(), null, null, ipPerm.getProtocol(), null, request.getName(), 
 							ipPerm.getFromPort().longValue(), secGroupList);
 				}
-				if (resp != null && resp.getRuleId() != null) {
-					return true;
-				}
-				return false;
+                if (resp != null ){
+                    List<CloudStackIngressRule> ingressRules = resp.getIngressRules();
+                    for (CloudStackIngressRule ingressRule : ingressRules)
+                        if (ingressRule.getRuleId() == null) return false;
+                } else {
+                    return false;
+                }
 			}
 		} catch(Exception e) {
 			logger.error( "EC2 AuthorizeSecurityGroupIngress - ", e);
@@ -709,58 +712,27 @@
 			throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
 		}
 	}
+
 	/**
-	 * Lists SSH KeyPairs on the systme
+     * Lists SSH KeyPairs on the system
 	 * 
 	 * @param request
 	 * @return
 	 */
 	public EC2DescribeKeyPairsResponse describeKeyPairs( EC2DescribeKeyPairs request ) {
-		try {
-			EC2KeyPairFilterSet filterSet = request.getKeyFilterSet();
-			String[] keyNames = request.getKeyNames();
-			List<CloudStackKeyPair> keyPairs = getApi().listSSHKeyPairs(null, null, null);
-			List<EC2SSHKeyPair> keyPairsList = new ArrayList<EC2SSHKeyPair>();
-	
-			if (keyPairs != null) {
-				// Let's trim the list of keypairs to only the ones listed in keyNames
-			    List<CloudStackKeyPair> matchedKeyPairs = new ArrayList<CloudStackKeyPair>();
-				if (keyNames != null && keyNames.length > 0) {
-					for (CloudStackKeyPair keyPair : keyPairs) {
-						boolean matched = false;
-						for (String keyName : keyNames) {
-							if (keyPair.getName().equalsIgnoreCase(keyName)) {
-								matched = true;
-								break;
-							}
-						}
-						if (matched) {
-						    matchedKeyPairs.add(keyPair);
-						}
-					}
-	                if (matchedKeyPairs.isEmpty()) {
-	                    throw new EC2ServiceException(ServerError.InternalError, "No matching keypairs found");
-	                }
-				}else{
-				    matchedKeyPairs = keyPairs;
-				}
-	
-	
-				// this should be reworked... converting from CloudStackKeyPairResponse to EC2SSHKeyPair is dumb
-				for (CloudStackKeyPair respKeyPair: matchedKeyPairs) {
-					EC2SSHKeyPair ec2KeyPair = new EC2SSHKeyPair();
-					ec2KeyPair.setFingerprint(respKeyPair.getFingerprint());
-					ec2KeyPair.setKeyName(respKeyPair.getName());
-					ec2KeyPair.setPrivateKey(respKeyPair.getPrivatekey());
-					keyPairsList.add(ec2KeyPair);
-				}
-			}
-			return filterSet.evaluate(keyPairsList);
-		} catch(Exception e) {
-			logger.error("EC2 DescribeKeyPairs - ", e);
-			throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
-		}
-	}
+        try {
+            EC2DescribeKeyPairsResponse response = listKeyPairs(request.getKeyNames());
+            EC2KeyPairFilterSet kfs = request.getKeyFilterSet();
+
+            if (kfs == null)
+                return response;
+            else
+                return kfs.evaluate(response);
+        } catch(Exception e) {
+            logger.error("EC2 DescribeKeyPairs - ", e);
+            throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
+        }
+    }
 
 	/**
 	 * Delete SSHKeyPair
@@ -840,24 +812,13 @@
 	 */
 	public EC2DescribeAddressesResponse describeAddresses( EC2DescribeAddresses request ) {
 		try {
-			List<CloudStackIpAddress> addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null);
+            EC2DescribeAddressesResponse response = listAddresses(request.getPublicIpsSet());
+            EC2AddressFilterSet afs = request.getFilterSet();
 
-			EC2AddressFilterSet filterSet = request.getFilterSet();
-			List<EC2Address> addressList = new ArrayList<EC2Address>();
-			if (addrList != null && addrList.size() > 0) {
-				for (CloudStackIpAddress addr: addrList) {
-					// remember, if no filters are set, request.inPublicIpSet always returns true
-					if (request.inPublicIpSet(addr.getIpAddress())) {
-						EC2Address ec2Address = new EC2Address();
-						ec2Address.setIpAddress(addr.getIpAddress());
-						if (addr.getVirtualMachineId() != null) 
-							ec2Address.setAssociatedInstanceId(addr.getVirtualMachineId().toString());
-						addressList.add(ec2Address);
-					}
-				}
-			}
-
-			return filterSet.evaluate(addressList);
+            if (afs ==null)
+                return response;
+            else
+                return afs.evaluate(response);
 		} catch(Exception e) {
 			logger.error("EC2 DescribeAddresses - ", e);
 			throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
@@ -2075,6 +2036,71 @@
 		}
 			}
 
+    private EC2DescribeKeyPairsResponse listKeyPairs( String[] keyNames ) throws Exception {
+        try {
+            EC2DescribeKeyPairsResponse keyPairSet = new EC2DescribeKeyPairsResponse();
+
+            List<CloudStackKeyPair> keyPairs = getApi().listSSHKeyPairs(null, null, null);
+            if (keyPairs != null && keyPairs.size() > 0) {
+                for (CloudStackKeyPair keyPair : keyPairs) {
+                    boolean matched = false;
+                    if (keyNames.length > 0) {
+                        for (String keyName : keyNames) {
+                            if (keyName.equalsIgnoreCase(keyPair.getName())) {
+                                matched = true;
+                                break;
+                            }
+                        }
+                    } else matched = true;
+                    if (!matched) continue;
+                    EC2SSHKeyPair ec2KeyPair = new EC2SSHKeyPair();
+                    ec2KeyPair.setFingerprint(keyPair.getFingerprint());
+                    ec2KeyPair.setKeyName(keyPair.getName());
+                    ec2KeyPair.setPrivateKey(keyPair.getPrivatekey());
+
+                    keyPairSet.addKeyPair(ec2KeyPair);
+                }
+            }
+            return keyPairSet;
+        } catch(Exception e) {
+            logger.error( "List Keypairs - ", e);
+            throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
+        }
+    }
+
+    private EC2DescribeAddressesResponse listAddresses(String[] addressNames) throws Exception {
+        try {
+            EC2DescribeAddressesResponse addressSet = new EC2DescribeAddressesResponse();
+
+            List<CloudStackIpAddress> addresses = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null);
+            if (addresses != null && addresses.size() > 0) {
+                for (CloudStackIpAddress address : addresses) {
+                    boolean matched = false;
+                    if ( addressNames.length > 0) {
+                        for (String addressName : addressNames) {
+                            if (address.getIpAddress().equalsIgnoreCase(addressName)) {
+                                matched = true;
+                                break;
+                            }
+                        }
+                    } else matched = true;
+
+                    if (!matched) continue ;
+
+                    EC2Address ec2Address = new EC2Address();
+                    ec2Address.setIpAddress(address.getIpAddress());
+                    if (address.getVirtualMachineId() != null)
+                        ec2Address.setAssociatedInstanceId(address.getVirtualMachineId().toString());
+                    addressSet.addAddress(ec2Address);
+                }
+            }
+            return addressSet;
+        } catch(Exception e) {
+            logger.error( "List Addresses - ", e);
+            throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
+        }
+    }
+
 	/**
 	 * Convert ingress rule to EC2IpPermission records
 	 * 
diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2KeyPairFilterSet.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2KeyPairFilterSet.java
index 021487c..2ad005b 100644
--- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2KeyPairFilterSet.java
+++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2KeyPairFilterSet.java
@@ -62,14 +62,14 @@
 	}
 
 
-	public EC2DescribeKeyPairsResponse evaluate( List<EC2SSHKeyPair> sampleList) throws ParseException	{
+    public EC2DescribeKeyPairsResponse evaluate( EC2DescribeKeyPairsResponse response ) throws ParseException {
 		EC2DescribeKeyPairsResponse resultList = new EC2DescribeKeyPairsResponse();
 		
 		boolean matched;
 		
-		EC2SSHKeyPair[] keypairSet = sampleList.toArray(new EC2SSHKeyPair[0]);
+        EC2SSHKeyPair[] keyPairSet = response.getKeyPairSet();
 		EC2Filter[] filterSet = getFilterSet();
-		for (EC2SSHKeyPair keyPair : keypairSet) {
+        for (EC2SSHKeyPair keyPair : keyPairSet) {
 			matched = true;
 			for (EC2Filter filter : filterSet) {
 				if (!filterMatched(keyPair, filter)) {
diff --git a/awsapi/src/com/cloud/stack/CloudStackApi.java b/awsapi/src/com/cloud/stack/CloudStackApi.java
index 52c2459..5a30ef2 100644
--- a/awsapi/src/com/cloud/stack/CloudStackApi.java
+++ b/awsapi/src/com/cloud/stack/CloudStackApi.java
@@ -1115,9 +1115,9 @@
 	 * @return
 	 * @throws Exception
 	 */
-	public CloudStackSecurityGroupIngress authorizeSecurityGroupIngress(String account, String cidrList, String domainId, Long endPort, 
-			String icmpCode, String icmpType, String protocol, String securityGroupId, String securityGroupName, Long startPort, 
-			List<CloudStackKeyValue> userSecurityGroupList) throws Exception {
+    public CloudStackSecurityGroup authorizeSecurityGroupIngress(String account, String cidrList, String domainId, Long endPort,
+            String icmpCode, String icmpType, String protocol, String securityGroupId, String securityGroupName, Long startPort,
+            List<CloudStackKeyValue> userSecurityGroupList) throws Exception {
 		CloudStackCommand cmd = new CloudStackCommand(ApiConstants.AUTHORIZE_SECURITY_GROUP_INGRESS);
 		if (cmd != null) {
 			if (account != null) cmd.setParam(ApiConstants.ACCOUNT, account);
@@ -1139,7 +1139,8 @@
 				}				
 			}
 		}
-		return _client.call(cmd, apiKey, secretKey, true, ApiConstants.AUTHORIZE_SECURITY_GROUP_INGRESS_RESPONSE, ApiConstants.SECURITY_GROUP, CloudStackSecurityGroupIngress.class);
+        return _client.call(cmd, apiKey, secretKey, true, ApiConstants.AUTHORIZE_SECURITY_GROUP_INGRESS_RESPONSE,
+                ApiConstants.SECURITY_GROUP, CloudStackSecurityGroup.class);
 	}
 
 	/**
diff --git a/client/pom.xml b/client/pom.xml
index 7ebe50c..63ec2ef 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -117,6 +117,11 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-mom-rabbitmq</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>${cs.mysql.version}</version>
diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in
index c45d14e..e84be04 100755
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@ -272,6 +272,7 @@
         <dao name="Site2SiteVpnGatewayDao" class="com.cloud.network.dao.Site2SiteVpnGatewayDaoImpl" singleton="false"/>
         <dao name="Site2SiteVpnConnectionDao" class="com.cloud.network.dao.Site2SiteVpnConnectionDaoImpl" singleton="false"/>
         <dao name="RegionDao" class="org.apache.cloudstack.region.dao.RegionDaoImpl" singleton="false"/>
+        <dao name="UserIpv6AddressDao" class="com.cloud.network.dao.UserIpv6AddressDaoImpl" singleton="false"/>
     </configuration-server>
     
     <awsapi-ec2server class="com.cloud.bridge.service.EC2MainServlet">
diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
index 05107e7..1767207 100755
--- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
+++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
@@ -578,7 +578,9 @@
     protected synchronized Answer execute (final DhcpEntryCommand cmd) {
         final Script command  = new Script(_dhcpEntryPath, _timeout, s_logger);
         command.add("-r", cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP));
-        command.add("-v", cmd.getVmIpAddress());
+        if (cmd.getVmIpAddress() != null) {
+        	command.add("-v", cmd.getVmIpAddress());
+        }
         command.add("-m", cmd.getVmMac());
         command.add("-n", cmd.getVmName());
         
@@ -592,6 +594,11 @@
         if (cmd.getDefaultDns() != null) {
         	command.add("-N", cmd.getDefaultDns());
         }
+        
+        if (cmd.getVmIp6Address() != null) {
+        	command.add("-6", cmd.getVmIp6Address());
+        	command.add("-u", cmd.getDuid());
+        }
 
         final String result = command.execute();
         return new Answer(cmd, result==null, result);
diff --git a/core/src/com/cloud/storage/SnapshotVO.java b/core/src/com/cloud/storage/SnapshotVO.java
index e5e3650..c1c5f21 100644
--- a/core/src/com/cloud/storage/SnapshotVO.java
+++ b/core/src/com/cloud/storage/SnapshotVO.java
@@ -16,23 +16,13 @@
 // under the License.
 package com.cloud.storage;
 
-import java.util.Date;
-import java.util.UUID;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.Table;
-
-import org.apache.cloudstack.api.Identity;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.utils.db.GenericDao;
 import com.google.gson.annotations.Expose;
-import org.apache.cloudstack.api.InternalIdentity;
+
+import javax.persistence.*;
+import java.util.Date;
+import java.util.UUID;
 
 @Entity
 @Table(name="snapshots")
@@ -69,7 +59,7 @@
     @Expose
     @Column(name="status", updatable = true, nullable=false)
     @Enumerated(value=EnumType.STRING)
-    private Status status;
+    private State status;
 
     @Column(name="snapshot_type")
     short snapshotType;
@@ -127,7 +117,7 @@
         this.snapshotType = snapshotType;
         this.typeDescription = typeDescription;
         this.size = size;
-        this.status = Status.Creating;
+        this.status = State.Creating;
         this.prevSnapshotId = 0;
         this.hypervisorType = hypervisorType;
         this.version = "2.2";
@@ -252,11 +242,11 @@
     }
 
 	@Override
-    public Status getStatus() {
+    public State getState() {
 		return status;
 	}
 
-	public void setStatus(Status status) {
+	public void setStatus(State status) {
 		this.status = status;
 	}
 
diff --git a/framework/events/pom.xml b/framework/events/pom.xml
new file mode 100644
index 0000000..ef812e5
--- /dev/null
+++ b/framework/events/pom.xml
@@ -0,0 +1,47 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-framework-events</artifactId>
+  <name>Apache CloudStack Event Notification Framework</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack-framework</artifactId>
+    <version>4.1.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+        <artifactId>cloud-utils</artifactId>
+        <version>${project.version}</version>
+     </dependency>
+    <dependency>
+        <groupId>com.google.code.gson</groupId>
+        <artifactId>gson</artifactId>
+        <version>${cs.gson.version}</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <defaultGoal>install</defaultGoal>
+    <sourceDirectory>src</sourceDirectory>
+    <testSourceDirectory>test</testSourceDirectory>
+  </build>
+</project>
diff --git a/framework/events/src/org/apache/cloudstack/framework/events/Event.java b/framework/events/src/org/apache/cloudstack/framework/events/Event.java
new file mode 100644
index 0000000..eb6f48d
--- /dev/null
+++ b/framework/events/src/org/apache/cloudstack/framework/events/Event.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cloudstack.framework.events;
+
+import com.google.gson.Gson;
+
+public class Event {
+
+    String eventCategory;
+    String eventType;
+    String eventSource;
+    String resourceType;
+    String resourceUUID;
+    String description;
+
+    public Event(String eventSource, String eventCategory, String eventType, String resourceType,
+                 String resourceUUID) {
+        this.eventCategory = eventCategory;
+        this.eventType = eventType;
+        this.eventSource = eventSource;
+        this.resourceType = resourceType;
+        this.resourceUUID = resourceUUID;
+    }
+
+    public String getEventCategory() {
+        return eventCategory;
+    }
+
+    public void setEventCategory(String category) {
+        eventCategory = category;
+    }
+
+    public String getEventType() {
+        return eventType;
+    }
+
+    public void setEventType(String type) {
+        eventType = type;
+    }
+
+    public String getEventSource() {
+        return eventSource;
+    }
+
+    void setEventSource(String source) {
+        eventSource = source;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription (Object message) {
+        Gson gson = new Gson();
+        this.description = gson.toJson(message).toString();
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public void setResourceType(String resourceType) {
+        this.resourceType = resourceType;
+    }
+
+    public void setResourceUUID(String uuid) {
+        this.resourceUUID = uuid;
+    }
+
+    public String getResourceUUID () {
+        return resourceUUID;
+    }
+}
\ No newline at end of file
diff --git a/framework/events/src/org/apache/cloudstack/framework/events/EventBus.java b/framework/events/src/org/apache/cloudstack/framework/events/EventBus.java
new file mode 100644
index 0000000..c16ee6f
--- /dev/null
+++ b/framework/events/src/org/apache/cloudstack/framework/events/EventBus.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cloudstack.framework.events;
+
+import com.cloud.utils.component.Adapter;
+
+import java.util.UUID;
+
+/**
+ * Interface to publish and subscribe to CloudStack events
+ *
+ */
+public interface EventBus extends Adapter{
+
+    /**
+     * publish an event on to the event bus
+     *
+     * @param event event that needs to be published on the event bus
+     */
+    void publish(Event event) throws EventBusException;
+
+    /**
+     * subscribe to events that matches specified event topics
+     *
+     * @param topic defines category and type of the events being subscribed to
+     * @param subscriber subscriber that intends to receive event notification
+     * @return UUID returns the subscription ID
+     */
+     UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException;
+
+    /**
+     * unsubscribe to events of a category and a type
+     *
+     * @param subscriber subscriber that intends to unsubscribe from the event notification
+     */
+    void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException;
+
+}
diff --git a/framework/events/src/org/apache/cloudstack/framework/events/EventBusException.java b/framework/events/src/org/apache/cloudstack/framework/events/EventBusException.java
new file mode 100644
index 0000000..5654ba0
--- /dev/null
+++ b/framework/events/src/org/apache/cloudstack/framework/events/EventBusException.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cloudstack.framework.events;
+
+public class EventBusException extends Exception{
+    public EventBusException (String msg) {
+      super(msg);
+    }
+}
diff --git a/framework/events/src/org/apache/cloudstack/framework/events/EventSubscriber.java b/framework/events/src/org/apache/cloudstack/framework/events/EventSubscriber.java
new file mode 100644
index 0000000..b1c30c2
--- /dev/null
+++ b/framework/events/src/org/apache/cloudstack/framework/events/EventSubscriber.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cloudstack.framework.events;
+
+public interface EventSubscriber {
+
+    /**
+     * Callback method. EventBus calls this method on occurrence of subscribed event
+     *
+     * @param event details of the event
+     */
+    void onEvent(Event event);
+}
diff --git a/framework/events/src/org/apache/cloudstack/framework/events/EventTopic.java b/framework/events/src/org/apache/cloudstack/framework/events/EventTopic.java
new file mode 100644
index 0000000..19b727d
--- /dev/null
+++ b/framework/events/src/org/apache/cloudstack/framework/events/EventTopic.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cloudstack.framework.events;
+
+public class EventTopic {
+
+    String eventCategory;
+    String eventType;
+    String resourceType;
+    String resourceUUID;
+    String eventSource;
+
+    public EventTopic(String eventCategory, String eventType, String resourceType, String resourceUUID, String eventSource) {
+        this.eventCategory = eventCategory;
+        this.eventType = eventType;
+        this.resourceType = resourceType;
+        this.resourceUUID = resourceUUID;
+        this.eventSource = eventSource;
+    }
+
+    public String getEventCategory() {
+        return eventCategory;
+    }
+
+    public String getEventType() {
+        return eventType;
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public String getEventSource() {
+        return eventSource;
+    }
+
+    public String getResourceUUID() {
+        return resourceUUID;
+    }
+}
diff --git a/framework/pom.xml b/framework/pom.xml
new file mode 100644
index 0000000..81e0916
--- /dev/null
+++ b/framework/pom.xml
@@ -0,0 +1,35 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloudstack-framework</artifactId>
+  <name>Apache CloudStack framework POM</name>
+  <packaging>pom</packaging>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack</artifactId>
+    <version>4.1.0-SNAPSHOT</version>
+  </parent>
+  <build>
+    <defaultGoal>install</defaultGoal>
+  </build>
+  <modules>
+    <module>events</module>
+  </modules>
+</project>
diff --git a/patches/systemvm/debian/config/etc/dnsmasq.conf b/patches/systemvm/debian/config/etc/dnsmasq.conf
index 8f999a7..7d656cb 100644
--- a/patches/systemvm/debian/config/etc/dnsmasq.conf
+++ b/patches/systemvm/debian/config/etc/dnsmasq.conf
@@ -4,11 +4,16 @@
 # as the long options legal on the command line. See
 # "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
 
+# Listen on this specific port instead of the standard DNS port
+# (53). Setting this to zero completely disables DNS function,
+# leaving only DHCP and/or TFTP.
+#port=5353
+
 # The following two options make you a better netizen, since they
 # tell dnsmasq to filter out queries which the public DNS cannot
 # answer, and which load the servers (especially the root servers)
-# uneccessarily. If you have a dial-on-demand link they also stop
-# these requests from bringing up the link uneccessarily.
+# unnecessarily. If you have a dial-on-demand link they also stop
+# these requests from bringing up the link unnecessarily.
 
 # Never forward plain names (without a dot or domain part)
 domain-needed
@@ -19,10 +24,10 @@
 # Uncomment this to filter useless windows-originated DNS requests
 # which can trigger dial-on-demand links needlessly.
 # Note that (amongst other things) this blocks all SRV requests,
-# so don't use it if you use eg Kerberos.
+# so don't use it if you use eg Kerberos, SIP, XMMP or Google-talk.
 # This option only affects forwarding, SRV records originating for
 # dnsmasq (via srv-host= lines) are not suppressed by it.
-#filterwin2k
+filterwin2k
 
 # Change this line if you want dns to get its upstream servers from
 # somewhere other that /etc/resolv.conf
@@ -48,7 +53,7 @@
 # non-public domains.
 #server=/localnet/192.168.0.1
 
-# Example of routing PTR queries to nameservers: this will send all 
+# Example of routing PTR queries to nameservers: this will send all
 # address->name queries for 192.168.3/24 to nameserver 10.1.2.3
 #server=/3.168.192.in-addr.arpa/10.1.2.3
 
@@ -57,9 +62,21 @@
 local=/2.vmops-test.vmops.com/
 
 # Add domains which you want to force to an IP address here.
-# The example below send any host in doubleclick.net to a local
-# webserver.
-#address=/doubleclick.net/127.0.0.1
+# The example below send any host in double-click.net to a local
+# web-server.
+#address=/double-click.net/127.0.0.1
+
+# --address (and --server) work with IPv6 addresses too.
+#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
+
+# You can control how dnsmasq talks to a server: this forces
+# queries to 10.1.2.3 to be routed via eth1
+# server=10.1.2.3@eth1
+
+# and this sets the source (ie local) address used to talk to
+# 10.1.2.3 to 192.168.1.1 port 55 (there must be a interface with that
+# IP on the machine, obviously).
+# server=10.1.2.3@192.168.1.1#55
 
 # If you want dnsmasq to change uid and gid to something other
 # than the default, edit the following lines.
@@ -80,7 +97,7 @@
 #listen-address=
 # If you want dnsmasq to provide only DNS service on an interface,
 # configure it as shown above, and then use the following line to
-# disable DHCP on it.
+# disable DHCP and TFTP on it.
 no-dhcp-interface=eth1
 no-dhcp-interface=eth2
 
@@ -113,13 +130,19 @@
 # 3) Provides the domain part for "expand-hosts"
 domain=2.vmops-test.vmops.com
 
+# Set a different domain for a particular subnet
+#domain=wireless.thekelleys.org.uk,192.168.2.0/24
+
+# Same idea, but range rather then subnet
+#domain=reserved.thekelleys.org.uk,192.68.3.100,192.168.3.200
+
 # Uncomment this to enable the integrated DHCP server, you need
 # to supply the range of addresses available for lease and optionally
 # a lease time. If you have more than one network, you will need to
 # repeat this for each network on which you want to supply DHCP
 # service.
-dhcp-range=10.1.1.1,static
-#dhcp-range=10.0.0.1,10.255.255.255
+dhcp-range_ip4=10.1.1.1,static
+dhcp-range_ip6=::1,static
 dhcp-hostsfile=/etc/dhcphosts.txt
 
 # This is an example of a DHCP range where the netmask is given. This
@@ -128,17 +151,68 @@
 # don't need to worry about this.
 #dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
 
-# This is an example of a DHCP range with a network-id, so that
+# This is an example of a DHCP range which sets a tag, so that
 # some DHCP options may be set only for this network.
-#dhcp-range=red,192.168.0.50,192.168.0.150
+#dhcp-range=set:red,192.168.0.50,192.168.0.150
+
+# Use this DHCP range only when the tag "green" is set.
+#dhcp-range=tag:green,192.168.0.50,192.168.0.150,12h
+
+# Specify a subnet which can't be used for dynamic address allocation,
+# is available for hosts with matching --dhcp-host lines. Note that
+# dhcp-host declarations will be ignored unless there is a dhcp-range
+# of some type for the subnet in question.
+# In this case the netmask is implied (it comes from the network
+# configuration on the machine running dnsmasq) it is possible to give
+# an explicit netmask instead.
+#dhcp-range=192.168.0.0,static
+
+# Enable DHCPv6. Note that the prefix-length does not need to be specified
+# and defaults to 64 if missing/
+#dhcp-range=1234::2, 1234::500, 64, 12h
+
+# Do Router Advertisements, BUT NOT DHCP for this subnet.
+#dhcp-range=1234::, ra-only 
+
+# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
+# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack 
+# hosts. Use the DHCPv4 lease to derive the name, network segment and 
+# MAC address and assume that the host will also have an
+# IPv6 address calculated using the SLAAC alogrithm.
+#dhcp-range=1234::, ra-names
+
+# Do Router Advertisements, BUT NOT DHCP for this subnet.
+# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
+#dhcp-range=1234::, ra-only, 48h
+
+# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
+# so that clients can use SLAAC addresses as well as DHCP ones.
+#dhcp-range=1234::2, 1234::500, slaac
+
+# Do Router Advertisements and stateless DHCP for this subnet. Clients will
+# not get addresses from DHCP, but they will get other configuration information.
+# They will use SLAAC for addresses.
+#dhcp-range=1234::, ra-stateless
+
+# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
+# from DHCPv4 leases.
+#dhcp-range=1234::, ra-stateless, ra-names
+
+# Do router advertisements for all subnets where we're doing DHCPv6
+# Unless overriden by ra-stateless, ra-names, et al, the router 
+# advertisements will have the M and O bits set, so that the clients
+# get addresses and configuration from DHCPv6, and the A bit reset, so the 
+# clients don't use SLAAC addresses.
+#enable-ra
 
 # Supply parameters for specified hosts using DHCP. There are lots
 # of valid alternatives, so we will give examples of each. Note that
 # IP addresses DO NOT have to be in the range given above, they just
 # need to be on the same network. The order of the parameters in these
-# do not matter, it's permissble to give name,adddress and MAC in any order
+# do not matter, it's permissible to give name, address and MAC in any
+# order.
 
-# Always allocate the host with ethernet address 11:22:33:44:55:66
+# Always allocate the host with Ethernet address 11:22:33:44:55:66
 # The IP address 192.168.0.60
 #dhcp-host=11:22:33:44:55:66,192.168.0.60
 
@@ -146,11 +220,19 @@
 # 11:22:33:44:55:66 to be "fred"
 #dhcp-host=11:22:33:44:55:66,fred
 
-# Always give the host with ethernet address 11:22:33:44:55:66
+# Always give the host with Ethernet address 11:22:33:44:55:66
 # the name fred and IP address 192.168.0.60 and lease time 45 minutes
 #dhcp-host=11:22:33:44:55:66,fred,192.168.0.60,45m
 
-# Give the machine which says it's name is "bert" IP address
+# Give a host with Ethernet address 11:22:33:44:55:66 or
+# 12:34:56:78:90:12 the IP address 192.168.0.60. Dnsmasq will assume
+# that these two Ethernet interfaces will never be in use at the same
+# time, and give the IP address to the second, even if it is already
+# in use by the first. Useful for laptops with wired and wireless
+# addresses.
+#dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.60
+
+# Give the machine which says its name is "bert" IP address
 # 192.168.0.70 and an infinite lease
 #dhcp-host=bert,192.168.0.70,infinite
 
@@ -167,41 +249,47 @@
 # it asks for a DHCP lease.
 #dhcp-host=judge
 
-# Never offer DHCP service to a machine whose ethernet
+# Never offer DHCP service to a machine whose Ethernet
 # address is 11:22:33:44:55:66
 #dhcp-host=11:22:33:44:55:66,ignore
 
-# Ignore any client-id presented by the machine with ethernet
+# Ignore any client-id presented by the machine with Ethernet
 # address 11:22:33:44:55:66. This is useful to prevent a machine
 # being treated differently when running under different OS's or
 # between PXE boot and OS boot.
 #dhcp-host=11:22:33:44:55:66,id:*
 
 # Send extra options which are tagged as "red" to
-# the machine with ethernet address 11:22:33:44:55:66
-#dhcp-host=11:22:33:44:55:66,net:red
+# the machine with Ethernet address 11:22:33:44:55:66
+#dhcp-host=11:22:33:44:55:66,set:red
 
 # Send extra options which are tagged as "red" to
-# any machine with ethernet address starting 11:22:33:
-#dhcp-host=11:22:33:*:*:*,net:red
+# any machine with Ethernet address starting 11:22:33:
+#dhcp-host=11:22:33:*:*:*,set:red
 
-# Ignore any clients which are specified in dhcp-host lines
-# or /etc/ethers. Equivalent to ISC "deny unkown-clients".
-# This relies on the special "known" tag which is set when 
+# Give a fixed IPv6 address and name to client with 
+# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
+# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
+# Note also the they [] around the IPv6 address are obilgatory.
+#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5] 
+
+# Ignore any clients which are not specified in dhcp-host lines
+# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
+# This relies on the special "known" tag which is set when
 # a host is matched.
-#dhcp-ignore=#known
+#dhcp-ignore=tag:!known
 
 # Send extra options which are tagged as "red" to any machine whose
 # DHCP vendorclass string includes the substring "Linux"
-#dhcp-vendorclass=red,Linux
+#dhcp-vendorclass=set:red,Linux
 
 # Send extra options which are tagged as "red" to any machine one
 # of whose DHCP userclass strings includes the substring "accounts"
-#dhcp-userclass=red,accounts
+#dhcp-userclass=set:red,accounts
 
 # Send extra options which are tagged as "red" to any machine whose
 # MAC address matches the pattern.
-#dhcp-mac=red,00:60:8C:*:*:*
+#dhcp-mac=set:red,00:60:8C:*:*:*
 
 # If this line is uncommented, dnsmasq will read /etc/ethers and act
 # on the ethernet-address/IP pairs found there just as if they had
@@ -211,11 +299,11 @@
 
 # Send options to hosts which ask for a DHCP lease.
 # See RFC 2132 for details of available options.
-# Common options can be given to dnsmasq by name: 
+# Common options can be given to dnsmasq by name:
 # run "dnsmasq --help dhcp" to get a list.
 # Note that all the common settings, such as netmask and
 # broadcast address, DNS server and default route, are given
-# sane defaults by dnsmasq. You very likely will not need 
+# sane defaults by dnsmasq. You very likely will not need
 # any dhcp-options. If you use Windows clients and Samba, there
 # are some options which are recommended, they are detailed at the
 # end of this section.
@@ -229,13 +317,20 @@
 
 # Override the default route supplied by dnsmasq and send no default
 # route at all. Note that this only works for the options sent by
-# default (1, 3, 6, 12, 28) the same line will send a zero-length option 
+# default (1, 3, 6, 12, 28) the same line will send a zero-length option
 # for all other option numbers.
 #dhcp-option=3
 
 # Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
 #dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
 
+# Send DHCPv6 option. Note [] around IPv6 addresses.
+#dhcp-option=option6:dns-server,[1234::77],[1234::88]
+
+# Send DHCPv6 option for namservers as the machine running 
+# dnsmasq and another.
+#dhcp-option=option6:dns-server,[::],[1234::88]
+
 # Set the NTP time server address to be the same machine as
 # is running dnsmasq
 #dhcp-option=42,0.0.0.0
@@ -258,20 +353,23 @@
 
 # Specify an option which will only be sent to the "red" network
 # (see dhcp-range for the declaration of the "red" network)
-# Note that the net: part must precede the option: part.
-#dhcp-option = net:red, option:ntp-server, 192.168.1.1
+# Note that the tag: part must precede the option: part.
+#dhcp-option = tag:red, option:ntp-server, 192.168.1.1
 
 # The following DHCP options set up dnsmasq in the same way as is specified
 # for the ISC dhcpcd in
 # http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
 # adapted for a typical dnsmasq installation where the host running
 # dnsmasq is also the host running samba.
-# you may want to uncomment them if you use Windows clients and Samba.
+# you may want to uncomment some or all of them if you use
+# Windows clients and Samba.
 #dhcp-option=19,0           # option ip-forwarding off
 #dhcp-option=44,0.0.0.0     # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
 #dhcp-option=45,0.0.0.0     # netbios datagram distribution server
 #dhcp-option=46,8           # netbios node type
-#dhcp-option=47             # empty netbios scope.
+
+# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
+#dhcp-option=252,"\n"
 
 # Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
 # probably doesn't support this......
@@ -280,10 +378,10 @@
 # Send RFC-3442 classless static routes (note the netmask encoding)
 #dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
 
-# Send vendor-class specific options encapsulated in DHCP option 43. 
+# Send vendor-class specific options encapsulated in DHCP option 43.
 # The meaning of the options is defined by the vendor-class so
 # options are sent only when the client supplied vendor class
-# matches the class given here. (A substring match is OK, so "MSFT" 
+# matches the class given here. (A substring match is OK, so "MSFT"
 # matches "MSFT" and "MSFT 5.0"). This example sets the
 # mtftp address to 0.0.0.0 for PXEClients.
 #dhcp-option=vendor:PXEClient,1,0.0.0.0
@@ -300,7 +398,7 @@
 
 # Send options to PXELinux. Note that we need to send the options even
 # though they don't appear in the parameter request list, so we need
-# to use dhcp-option-force here. 
+# to use dhcp-option-force here.
 # See http://syslinux.zytor.com/pxe.php#special for details.
 # Magic number - needed before anything else is recognised
 #dhcp-option-force=208,f1:00:74:7e
@@ -311,29 +409,97 @@
 # Reboot time. (Note 'i' to send 32-bit value)
 #dhcp-option-force=211,30i
 
-# Set the boot filename for BOOTP. You will only need 
+# Set the boot filename for netboot/PXE. You will only need
 # this is you want to boot machines over the network and you will need
 # a TFTP server; either dnsmasq's built in TFTP server or an
 # external one. (See below for how to enable the TFTP server.)
 #dhcp-boot=pxelinux.0
 
+# The same as above, but use custom tftp-server instead machine running dnsmasq
+#dhcp-boot=pxelinux,server.name,192.168.1.100
+
+# Boot for Etherboot gPXE. The idea is to send two different
+# filenames, the first loads gPXE, and the second tells gPXE what to
+# load. The dhcp-match sets the gpxe tag for requests from gPXE.
+#dhcp-match=set:gpxe,175 # gPXE sends a 175 option.
+#dhcp-boot=tag:!gpxe,undionly.kpxe
+#dhcp-boot=mybootimage
+
+# Encapsulated options for Etherboot gPXE. All the options are
+# encapsulated within option 175
+#dhcp-option=encap:175, 1, 5b         # priority code
+#dhcp-option=encap:175, 176, 1b       # no-proxydhcp
+#dhcp-option=encap:175, 177, string   # bus-id
+#dhcp-option=encap:175, 189, 1b       # BIOS drive code
+#dhcp-option=encap:175, 190, user     # iSCSI username
+#dhcp-option=encap:175, 191, pass     # iSCSI password
+
+# Test for the architecture of a netboot client. PXE clients are
+# supposed to send their architecture as option 93. (See RFC 4578)
+#dhcp-match=peecees, option:client-arch, 0 #x86-32
+#dhcp-match=itanics, option:client-arch, 2 #IA64
+#dhcp-match=hammers, option:client-arch, 6 #x86-64
+#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
+
+# Do real PXE, rather than just booting a single file, this is an
+# alternative to dhcp-boot.
+#pxe-prompt="What system shall I netboot?"
+# or with timeout before first available action is taken:
+#pxe-prompt="Press F8 for menu.", 60
+
+# Available boot services. for PXE.
+#pxe-service=x86PC, "Boot from local disk"
+
+# Loads <tftp-root>/pxelinux.0 from dnsmasq TFTP server.
+#pxe-service=x86PC, "Install Linux", pxelinux
+
+# Loads <tftp-root>/pxelinux.0 from TFTP server at 1.2.3.4.
+# Beware this fails on old PXE ROMS.
+#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
+
+# Use bootserver on network, found my multicast or broadcast.
+#pxe-service=x86PC, "Install windows from RIS server", 1
+
+# Use bootserver at a known IP address.
+#pxe-service=x86PC, "Install windows from RIS server", 1, 1.2.3.4
+
+# If you have multicast-FTP available,
+# information for that can be passed in a similar way using options 1
+# to 5. See page 19 of
+# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
+
+
 # Enable dnsmasq's built-in TFTP server
 #enable-tftp
 
-# Set the root directory for files availble via FTP.
+# Set the root directory for files available via FTP.
 #tftp-root=/var/ftpd
 
 # Make the TFTP server more secure: with this set, only files owned by
 # the user dnsmasq is running as will be send over the net.
 #tftp-secure
 
+# This option stops dnsmasq from negotiating a larger blocksize for TFTP
+# transfers. It will slow things down, but may rescue some broken TFTP
+# clients.
+#tftp-no-blocksize
+
 # Set the boot file name only when the "red" tag is set.
 #dhcp-boot=net:red,pxelinux.red-net
 
-# An example of dhcp-boot with an external server: the name and IP
+# An example of dhcp-boot with an external TFTP server: the name and IP
 # address of the server are given after the filename.
+# Can fail with old PXE ROMS. Overridden by --pxe-service.
 #dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
 
+# If there are multiple external tftp servers having a same name
+# (using /etc/hosts) then that name can be specified as the
+# tftp_servername (the third option to dhcp-boot) and in that
+# case dnsmasq resolves this name and returns the resultant IP
+# addresses in round robin fasion. This facility can be used to
+# load balance the tftp load among a set of servers.
+#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
+
 # Set the limit on DHCP leases, the default is 150
 #dhcp-lease-max=150
 
@@ -347,16 +513,16 @@
 # and take over the lease for any client which broadcasts on the network,
 # whether it has a record of the lease or not. This avoids long timeouts
 # when a machine wakes up on a new network. DO NOT enable this if there's
-# the slighest chance that you might end up accidentally configuring a DHCP
+# the slightest chance that you might end up accidentally configuring a DHCP
 # server for your campus/company accidentally. The ISC server uses
 # the same option, and this URL provides more information:
-# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
+# http://www.isc.org/files/auth.html
 #dhcp-authoritative
 
 # Run an executable when a DHCP lease is created or destroyed.
-# The arguments sent to the script are "add" or "del", 
+# The arguments sent to the script are "add" or "del",
 # then the MAC address, the IP address and finally the hostname
-# if there is one. 
+# if there is one.
 #dhcp-script=/bin/echo
 
 # Set the cachesize here.
@@ -385,7 +551,8 @@
 #alias=1.2.3.4,5.6.7.8
 # and this maps 1.2.3.x to 5.6.7.x
 #alias=1.2.3.0,5.6.7.0,255.255.255.0
-
+# and this maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
+#alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
 
 # Change these lines if you want dnsmasq to serve MX records.
 
@@ -415,12 +582,12 @@
 # set for this to work.)
 
 # A SRV record sending LDAP for the example.com domain to
-# ldapserver.example.com port 289
+# ldapserver.example.com port 389
 #srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
 
 # A SRV record sending LDAP for the example.com domain to
-# ldapserver.example.com port 289 (using domain=)
-###domain=example.com
+# ldapserver.example.com port 389 (using domain=)
+#domain=example.com
 #srv-host=_ldap._tcp,ldapserver.example.com,389
 
 # Two SRV records for LDAP, each with different priorities
@@ -448,6 +615,10 @@
 #Example zeroconf
 #txt-record=_http._tcp.example.com,name=value,paper=A4
 
+# Provide an alias for a "local" DNS name. Note that this _only_ works
+# for targets which are names from DHCP or /etc/hosts. Give host
+# "bert" another name, bertrand
+#cname=bertand,bert
 
 # For debugging purposes, log each DNS query as it passes through
 # dnsmasq.
@@ -461,6 +632,3 @@
 # Include a another lot of configuration options.
 #conf-file=/etc/dnsmasq.more.conf
 conf-dir=/etc/dnsmasq.d
-
-# Don't reply Windows's periodical DNS request
-filterwin2k
diff --git a/patches/systemvm/debian/config/etc/init.d/cloud-early-config b/patches/systemvm/debian/config/etc/init.d/cloud-early-config
index 285a92f..db2fcc2 100755
--- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config
+++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config
@@ -229,6 +229,23 @@
   fi
 }
 
+setup_interface_ipv6() {
+  sysctl net.ipv6.conf.all.disable_ipv6=0
+  sysctl net.ipv6.conf.all.accept_ra=1
+  
+  local intfnum=$1
+  local ipv6="$2"
+  local prelen="$3"
+  local intf=eth${intfnum}
+  
+  echo "iface $intf inet6 static" >> /etc/network/interfaces
+  echo "  address $ipv6 " >> /etc/network/interfaces
+  echo "  netmask $prelen" >> /etc/network/interfaces
+  echo "  accept_ra 1" >> /etc/network/interfaces
+  ifdown $intf
+  ifup $intf
+}
+
 enable_fwding() {
   local enabled=$1
   log_it "cloud: enable_fwding = $1"
@@ -303,7 +320,14 @@
 
 setup_common() {
   init_interfaces $1 $2 $3
-  setup_interface "0" $ETH0_IP $ETH0_MASK $GW
+  if [ -n "$ETH0_IP" ]
+  then
+    setup_interface "0" $ETH0_IP $ETH0_MASK $GW
+  fi
+  if [ -n "$ETH0_IP6" ]
+  then
+	  setup_interface_ipv6 "0" $ETH0_IP6 $ETH0_IP6_PRELEN
+  fi
   setup_interface "1" $ETH1_IP $ETH1_MASK $GW
   if [ -n "$ETH2_IP" ]
   then
@@ -374,7 +398,8 @@
 
 setup_dnsmasq() {
   log_it "Setting up dnsmasq"
-  [ -z $DHCP_RANGE ] && DHCP_RANGE=$ETH0_IP
+  [ -z $DHCP_RANGE ] && [ $ETH0_IP ] && DHCP_RANGE=$ETH0_IP
+  [ $ETH0_IP6 ] && DHCP_RANGE_IP6=$ETH0_IP6
   [ -z $DOMAIN ] && DOMAIN="cloudnine.internal"
   
   if [ -n "$DOMAIN" ]
@@ -398,8 +423,20 @@
       sed -i s/[#]*dhcp-option=15.*$/dhcp-option=15,\""$DNS_SEARCH_ORDER"\"/ /etc/dnsmasq.conf
   fi
   
-  sed -i -e "s/^dhcp-range=.*$/dhcp-range=$DHCP_RANGE,static/" /etc/dnsmasq.conf
-  sed -i -e "s/^[#]*listen-address=.*$/listen-address=$ETH0_IP/" /etc/dnsmasq.conf
+  if [ $DHCP_RANGE ]
+  then
+    sed -i -e "s/^dhcp-range_ip4=.*$/dhcp-range=$DHCP_RANGE,static/" /etc/dnsmasq.conf
+  else
+    sed -i -e "s/^dhcp-range_ip4=.*$//" /etc/dnsmasq.conf
+  fi
+  if [ $DHCP_RANGE_IP6 ]
+  then
+    sed -i -e "s/^dhcp-range_ip6=.*$/dhcp-range=$DHCP_RANGE_IP6,static/" /etc/dnsmasq.conf
+  else
+    sed -i -e "s/^dhcp-range_ip6=.*$//" /etc/dnsmasq.conf
+  fi
+
+  sed -i -e "s/^[#]*listen-address=.*$/listen-address=$LOCAL_ADDRS/" /etc/dnsmasq.conf
 
   if [ "$RROUTER" == "1" ]
   then
@@ -683,14 +720,15 @@
   if [ "$DEFAULTROUTE" != "false" ]
   then
     sed -i -e "/^[#]*dhcp-option=option:router.*$/d" /etc/dnsmasq.conf
-    echo "dhcp-option=option:router,$GW" >> /etc/dnsmasq.conf
+    [ $GW ] && echo "dhcp-option=option:router,$GW" >> /etc/dnsmasq.conf
     #for now set up ourself as the dns server as well
     sed -i -e "/^[#]*dhcp-option=6.*$/d" /etc/dnsmasq.conf
     if [ "$USE_EXTERNAL_DNS" == "true" ]
     then
         echo "dhcp-option=6,$NS" >> /etc/dnsmasq.conf
     else
-        echo "dhcp-option=6,$ETH0_IP,$NS" >> /etc/dnsmasq.conf
+        [ $ETH0_IP ] && echo "dhcp-option=6,$ETH0_IP,$NS" >> /etc/dnsmasq.conf
+        [ $ETH0_IP6 ] && echo "dhcp-option=option6:dns-server,[::]" >> /etc/dnsmasq.conf
     fi
   else
     sed -i -e "/^[#]*dhcp-option=option:router.*$/d" /etc/dnsmasq.conf
@@ -700,7 +738,8 @@
   fi
 
   sed -i  /gateway/d /etc/hosts
-  echo "$ETH0_IP $NAME" >> /etc/hosts
+  [ $ETH0_IP ] && echo "$ETH0_IP $NAME" >> /etc/hosts
+  [ $ETH0_IP6 ] && echo "$ETH0_IP6 $NAME" >> /etc/hosts
 
   enable_svc dnsmasq 1
   enable_svc haproxy 0
@@ -903,6 +942,9 @@
       gateway)
           GW=$VALUE
           ;;
+      ip6gateway)
+          IP6GW=$VALUE
+          ;;
       eth0mask)
           ETH0_MASK=$VALUE
           ;;
@@ -912,6 +954,12 @@
       eth2mask)
           ETH2_MASK=$VALUE
           ;;
+      eth0ip6)
+          ETH0_IP6=$VALUE
+          ;;
+      eth0ip6prelen)
+          ETH0_IP6_PRELEN=$VALUE
+          ;;
       internaldns1)
           internalNS1=$VALUE
           ;;
@@ -1001,8 +1049,11 @@
         ;;
     esac
 done
-}
 
+[ $ETH0_IP ] && LOCAL_ADDRS=$ETH0_IP
+[ $ETH0_IP6 ] && LOCAL_ADDRS=$ETH0_IP6
+[ $ETH0_IP ] && [ $ETH0_IP6 ] && LOCAL_ADDRS="$ETH0_IP,$ETH0_IP6"
+}
 
 case "$1" in
 start)
diff --git a/patches/systemvm/debian/config/etc/sysctl.conf b/patches/systemvm/debian/config/etc/sysctl.conf
index 961d471..586d5bd 100644
--- a/patches/systemvm/debian/config/etc/sysctl.conf
+++ b/patches/systemvm/debian/config/etc/sysctl.conf
@@ -42,8 +42,8 @@
 net.core.somaxconn=1000000
 
 # Disable IPv6
-net.ipv6.conf.all.disable_ipv6 = 1
-net.ipv6.conf.all.forwarding = 0
-net.ipv6.conf.all.accept_ra = 0
+net.ipv6.conf.all.disable_ipv6 = 0
+net.ipv6.conf.all.forwarding = 1
+net.ipv6.conf.all.accept_ra = 1
 net.ipv6.conf.all.accept_redirects = 0
 net.ipv6.conf.all.autoconf = 0
diff --git a/patches/systemvm/debian/config/root/edithosts.sh b/patches/systemvm/debian/config/root/edithosts.sh
index 37c75a5..3cf2742 100755
--- a/patches/systemvm/debian/config/root/edithosts.sh
+++ b/patches/systemvm/debian/config/root/edithosts.sh
@@ -26,12 +26,43 @@
 # $5 : nameserver on default nic
 # $6 : comma separated static routes
 
-mac=$1
-ip=$2
-host=$3
-dflt=$4
-dns=$5
-routes=$6
+usage() {
+  printf "Usage: %s: -m <MAC address> -4 <IPv4 address> -6 <IPv6 address> -h <hostname> -d <default router> -n <name server address> -s <Routes> -u <DUID>\n" $(basename $0) >&2
+}
+
+mac=
+ipv4=
+ipv6=
+host=
+dflt=
+dns=
+routes=
+duid=
+
+while getopts 'm:4:h:d:n:s:6:u:' OPTION
+do
+  case $OPTION in
+  m)    mac="$OPTARG"
+        ;;
+  4)    ipv4="$OPTARG"
+        ;;
+  6)    ipv6="$OPTARG"
+        ;;
+  u)    duid="$OPTARG"
+        ;;
+  h)    host="$OPTARG"
+        ;;
+  d)    dflt="$OPTARG"
+        ;;
+  n)    dns="$OPTARG"
+        ;;
+  s)    routes="$OPTARG"
+        ;;
+  ?)    usage
+        exit 2
+        ;;
+  esac
+done
 
 DHCP_HOSTS=/etc/dhcphosts.txt
 DHCP_OPTS=/etc/dhcpopts.txt
@@ -70,26 +101,69 @@
 [ ! -f $DHCP_LEASES ] && touch $DHCP_LEASES
 
 #delete any previous entries from the dhcp hosts file
-sed -i  /$mac/d $DHCP_HOSTS 
-sed -i  /$ip,/d $DHCP_HOSTS 
-sed -i  /$host,/d $DHCP_HOSTS 
+sed -i  /$mac/d $DHCP_HOSTS
+if [ $ipv4 ]
+then
+  sed -i  /$ipv4,/d $DHCP_HOSTS
+fi
+if [ $ipv6 ]
+then
+  sed -i  /$ipv6,/d $DHCP_HOSTS
+fi
+sed -i  /$host,/d $DHCP_HOSTS
 
 
 #put in the new entry
-echo "$mac,$ip,$host,infinite" >>$DHCP_HOSTS
+if [ $ipv4 ]
+then
+  echo "$mac,$ipv4,$host,infinite" >>$DHCP_HOSTS
+fi
+if [ $ipv6 ]
+then
+  echo "id:$duid,[$ipv6],$host,infinite" >>$DHCP_HOSTS
+fi
 
 #delete leases to supplied mac and ip addresses
-sed -i  /$mac/d $DHCP_LEASES 
-sed -i  /"$ip "/d $DHCP_LEASES 
+if [ $ipv4 ]
+then
+  sed -i  /$mac/d $DHCP_LEASES 
+  sed -i  /"$ipv4 "/d $DHCP_LEASES 
+fi
+if [ $ipv6 ]
+then
+  sed -i  /$duid/d $DHCP_LEASES 
+  sed -i  /"$ipv6 "/d $DHCP_LEASES 
+fi
 sed -i  /"$host "/d $DHCP_LEASES 
 
 #put in the new entry
-echo "0 $mac $ip $host *" >> $DHCP_LEASES
+if [ $ipv4 ]
+then
+  echo "0 $mac $ipv4 $host *" >> $DHCP_LEASES
+fi
+if [ $ipv6 ]
+then
+  echo "0 $duid $ipv6 $host *" >> $DHCP_LEASES
+fi
 
 #edit hosts file as well
-sed -i  /"$ip "/d $HOSTS
+if [ $ipv4 ]
+then
+  sed -i  /"$ipv4 "/d $HOSTS
+fi
+if [ $ipv6 ]
+then
+  sed -i  /"$ipv6 "/d $HOSTS
+fi
 sed -i  /" $host$"/d $HOSTS
-echo "$ip $host" >> $HOSTS
+if [ $ipv4 ]
+then
+  echo "$ipv4 $host" >> $HOSTS
+fi
+if [ $ipv6 ]
+then
+  echo "$ipv6 $host" >> $HOSTS
+fi
 
 if [ "$dflt" != "" ]
 then
@@ -97,26 +171,26 @@
   sed -i /dhcp-optsfile/d /etc/dnsmasq.conf
   echo "dhcp-optsfile=$DHCP_OPTS" >> /etc/dnsmasq.conf
 
-  tag=$(echo $ip | tr '.' '_')
+  tag=$(echo $ipv4 | tr '.' '_')
   sed -i /$tag/d $DHCP_OPTS
   if [ "$dflt" != "0.0.0.0" ]
   then
-      logger -t cloud "$0: setting default router for $ip to $dflt"
+      logger -t cloud "$0: setting default router for $ipv4 to $dflt"
       echo "$tag,3,$dflt" >> $DHCP_OPTS
   else
-      logger -t cloud "$0: unset default router for $ip"
+      logger -t cloud "$0: unset default router for $ipv4"
       echo "$tag,3," >> $DHCP_OPTS
   fi
   if [ "$dns" != "" ] 
   then
-    logger -t cloud "$0: setting dns server for $ip to $dns"
+    logger -t cloud "$0: setting dns server for $ipv4 to $dns"
     echo "$tag,6,$dns" >> $DHCP_OPTS
   fi
   [ "$routes" != "" ] && echo "$tag,121,$routes" >> $DHCP_OPTS
   #delete entry we just put in because we need a tag
-  sed -i  /$mac/d $DHCP_HOSTS 
+  sed -i  /$ipv4/d $DHCP_HOSTS 
   #put it back with a tag
-  echo "$mac,set:$tag,$ip,$host,infinite" >>$DHCP_HOSTS
+  echo "$mac,set:$tag,$ipv4,$host,infinite" >>$DHCP_HOSTS
 fi
 
 # make dnsmasq re-read files
diff --git a/plugins/event-bus/rabbitmq/pom.xml b/plugins/event-bus/rabbitmq/pom.xml
new file mode 100644
index 0000000..6a47983
--- /dev/null
+++ b/plugins/event-bus/rabbitmq/pom.xml
@@ -0,0 +1,46 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-mom-rabbitmq</artifactId>
+  <name>Apache CloudStack Plugin - RabbitMQ Event Bus</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack-plugins</artifactId>
+    <version>4.1.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+  <dependencies>
+    <dependency>
+    <groupId>com.rabbitmq</groupId>
+      <artifactId>amqp-client</artifactId>
+        <version>2.8.7</version>
+    </dependency>
+    <dependency>
+    <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-framework-events</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <defaultGoal>install</defaultGoal>
+    <sourceDirectory>src</sourceDirectory>
+  </build>
+</project>
diff --git a/plugins/event-bus/rabbitmq/src/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java b/plugins/event-bus/rabbitmq/src/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java
new file mode 100644
index 0000000..3a06c42
--- /dev/null
+++ b/plugins/event-bus/rabbitmq/src/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java
@@ -0,0 +1,555 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cloudstack.mom.rabbitmq;
+
+import com.rabbitmq.client.*;
+import org.apache.cloudstack.framework.events.*;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.Ternary;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@Local(value=EventBus.class)
+public class RabbitMQEventBus implements EventBus {
+
+    // details of AMQP server
+    private static String _amqpHost;
+    private static Integer _port;
+    private static String _username;
+    private static String _password;
+
+    // AMQP exchange name where all CloudStack events will be published
+    private static String _amqpExchangeName;
+
+    // hashmap to book keep the registered subscribers
+    private static ConcurrentHashMap<String, Ternary<String, Channel, EventSubscriber>> _subscribers;
+
+    // connection to AMQP server,
+    private static Connection _connection=null;
+
+    // AMQP server should consider messages acknowledged once delivered if _autoAck is true
+    private static boolean _autoAck = true;
+
+    private ExecutorService executorService;
+    private String _name;
+    private static DisconnectHandler disconnectHandler;
+    private static Integer _retryInterval;
+    private static final Logger s_logger = Logger.getLogger(RabbitMQEventBus.class);
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+
+        _amqpHost = (String) params.get("server");
+        if (_amqpHost == null || _amqpHost.isEmpty()) {
+            throw new ConfigurationException("Unable to get the AMQP server details");
+        }
+
+        _username = (String) params.get("username");
+        if (_username == null || _username.isEmpty()) {
+            throw new ConfigurationException("Unable to get the username details");
+        }
+
+        _password = (String) params.get("password");
+        if (_password == null || _password.isEmpty()) {
+            throw new ConfigurationException("Unable to get the password details");
+        }
+
+        _amqpExchangeName = (String) params.get("exchangename");
+        if (_amqpExchangeName == null || _amqpExchangeName.isEmpty()) {
+            throw new ConfigurationException("Unable to get the _exchange details on the AMQP server");
+        }
+
+        try {
+            String portStr =  (String) params.get("port");
+            if (portStr == null || portStr.isEmpty()) {
+                throw new ConfigurationException("Unable to get the port details of AMQP server");
+            }
+            _port = Integer.parseInt(portStr);
+
+            String retryIntervalStr = (String) params.get("retryinterval");
+            if (retryIntervalStr == null || retryIntervalStr.isEmpty()) {
+                // default to 10s to try out reconnect
+                retryIntervalStr = "10000";
+            }
+            _retryInterval = Integer.parseInt(retryIntervalStr);
+        } catch (NumberFormatException e) {
+            throw new ConfigurationException("Invalid port number/retry interval");
+        }
+
+        _subscribers = new ConcurrentHashMap<String, Ternary<String, Channel, EventSubscriber>>();
+
+        executorService = Executors.newCachedThreadPool();
+        disconnectHandler = new DisconnectHandler();
+        _name = name;
+        return true;
+    }
+
+    /** Call to subscribe to interested set of events
+     *
+     * @param topic defines category and type of the events being subscribed to
+     * @param subscriber subscriber that intends to receive event notification
+     * @return UUID that represents the subscription with event bus
+     * @throws EventBusException
+     */
+    @Override
+    public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
+
+        if (subscriber == null || topic == null) {
+            throw new EventBusException("Invalid EventSubscriber/EventTopic object passed.");
+        }
+
+        // create a UUID, that will be used for managing subscriptions and also used as queue name
+        // for on the queue used for the subscriber on the AMQP broker
+        UUID queueId = UUID.randomUUID();
+        String queueName = queueId.toString();
+
+        try {
+            String bindingKey = createBindingKey(topic);
+
+            // store the subscriber details before creating channel
+            _subscribers.put(queueName, new Ternary(bindingKey, null, subscriber));
+
+            // create a channel dedicated for this subscription
+            Connection connection = getConnection();
+            Channel channel = createChannel(connection);
+
+            // create a queue and bind it to the exchange with binding key formed from event topic
+            createExchange(channel, _amqpExchangeName);
+            channel.queueDeclare(queueName, false, false, false, null);
+            channel.queueBind(queueName, _amqpExchangeName, bindingKey);
+
+            // register a callback handler to receive the events that a subscriber subscribed to
+            channel.basicConsume(queueName, _autoAck, queueName,
+                    new DefaultConsumer(channel) {
+                        @Override
+                        public void handleDelivery(String queueName,
+                                                   Envelope envelope,
+                                                   AMQP.BasicProperties properties,
+                                                   byte[] body)
+                            throws IOException {
+                            Ternary<String, Channel, EventSubscriber> queueDetails = _subscribers.get(queueName);
+                            if (queueDetails != null) {
+                                EventSubscriber subscriber = queueDetails.third();
+                                String routingKey =  envelope.getRoutingKey();
+                                String eventSource = getEventSourceFromRoutingKey(routingKey);
+                                String eventCategory = getEventCategoryFromRoutingKey(routingKey);
+                                String eventType = getEventTypeFromRoutingKey(routingKey);
+                                String resourceType = getResourceTypeFromRoutingKey(routingKey);
+                                String resourceUUID = getResourceUUIDFromRoutingKey(routingKey);
+                                Event event = new Event(eventSource, eventCategory, eventType,
+                                        resourceType, resourceUUID);
+                                event.setDescription(new String(body));
+
+                                // deliver the event to call back object provided by subscriber
+                                subscriber.onEvent(event);
+                            }
+                        }
+                    }
+            );
+
+            // update the channel details for the subscription
+            Ternary<String, Channel, EventSubscriber> queueDetails = _subscribers.get(queueName);
+            queueDetails.second(channel);
+            _subscribers.put(queueName, queueDetails);
+
+        } catch (AlreadyClosedException closedException) {
+            s_logger.warn("Connection to AMQP service is lost. Subscription:" + queueName +
+                    " will be active after reconnection");
+        } catch (ConnectException connectException) {
+            s_logger.warn("Connection to AMQP service is lost. Subscription:" + queueName +
+                    " will be active after reconnection");
+        } catch (Exception e) {
+            throw new EventBusException("Failed to subscribe to event due to " + e.getMessage());
+        }
+
+        return queueId;
+    }
+
+    @Override
+    public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
+        try {
+            String classname =  subscriber.getClass().getName();
+            String queueName = UUID.nameUUIDFromBytes(classname.getBytes()).toString();
+            Ternary<String, Channel, EventSubscriber> queueDetails = _subscribers.get(queueName);
+            Channel channel = queueDetails.second();
+            channel.basicCancel(queueName);
+            _subscribers.remove(queueName, queueDetails);
+        } catch (Exception e) {
+            throw new EventBusException("Failed to unsubscribe from event bus due to " + e.getMessage());
+        }
+    }
+
+    // publish event on to the exchange created on AMQP server
+    @Override
+    public void publish(Event event) throws EventBusException {
+
+        String routingKey = createRoutingKey(event);
+        String eventDescription = event.getDescription();
+
+        try {
+            Connection connection = getConnection();
+            Channel channel = createChannel(connection);
+            createExchange(channel, _amqpExchangeName);
+            publishEventToExchange(channel, _amqpExchangeName, routingKey, eventDescription);
+            channel.close();
+        } catch (AlreadyClosedException e) {
+            closeConnection();
+            throw new EventBusException("Failed to publish event to message broker as connection to AMQP broker in lost");
+        } catch (Exception e) {
+            throw new EventBusException("Failed to publish event to message broker due to " + e.getMessage());
+        }
+    }
+
+    /** creates a routing key from the event details.
+     *  created routing key will be used while publishing the message to exchange on AMQP server
+     */
+    private String createRoutingKey(Event event) {
+
+        StringBuilder routingKey = new StringBuilder();
+
+        String eventSource =  replaceNullWithWildcard(event.getEventSource());
+        eventSource = eventSource.replace(".", "-");
+
+        String eventCategory = replaceNullWithWildcard(event.getEventCategory());
+        eventCategory = eventCategory.replace(".", "-");
+
+        String eventType = replaceNullWithWildcard(event.getEventType());
+        eventType = eventType.replace(".", "-");
+
+        String resourceType = replaceNullWithWildcard(event.getResourceType());
+        resourceType = resourceType.replace(".", "-");
+
+        String resourceUuid = replaceNullWithWildcard(event.getResourceUUID());
+        resourceUuid = resourceUuid.replace(".", "-");
+
+        // routing key will be of format: eventSource.eventCategory.eventType.resourceType.resourceUuid
+        routingKey.append(eventSource);
+        routingKey.append(".");
+        routingKey.append(eventCategory);
+        routingKey.append(".");
+        routingKey.append(eventType);
+        routingKey.append(".");
+        routingKey.append(resourceType);
+        routingKey.append(".");
+        routingKey.append(resourceUuid);
+
+        return routingKey.toString();
+    }
+
+    /** creates a binding key from the event topic that subscriber specified
+     *  binding key will be used to bind the queue created for subscriber to exchange on AMQP server
+     */
+    private String createBindingKey(EventTopic topic) {
+
+        StringBuilder bindingKey = new StringBuilder();
+
+        String eventSource =  replaceNullWithWildcard(topic.getEventSource());
+        eventSource = eventSource.replace(".", "-");
+
+        String eventCategory = replaceNullWithWildcard(topic.getEventCategory());
+        eventCategory = eventCategory.replace(".", "-");
+
+        String eventType = replaceNullWithWildcard(topic.getEventType());
+        eventType = eventType.replace(".", "-");
+
+        String resourceType = replaceNullWithWildcard(topic.getResourceType());
+        resourceType = resourceType.replace(".", "-");
+
+        String resourceUuid = replaceNullWithWildcard(topic.getResourceUUID());
+        resourceUuid = resourceUuid.replace(".", "-");
+
+        // binding key will be of format: eventSource.eventCategory.eventType.resourceType.resourceUuid
+        bindingKey.append(eventSource);
+        bindingKey.append(".");
+        bindingKey.append(eventCategory);
+        bindingKey.append(".");
+        bindingKey.append(eventType);
+        bindingKey.append(".");
+        bindingKey.append(resourceType);
+        bindingKey.append(".");
+        bindingKey.append(resourceUuid);
+
+        return bindingKey.toString();
+    }
+
+    private synchronized Connection getConnection() throws Exception {
+        if (_connection == null) {
+            try {
+                return createConnection();
+            } catch (Exception e) {
+                s_logger.error("Failed to create a connection to AMQP server due to " + e.getMessage());
+                throw e;
+            }
+        } else {
+            return _connection;
+        }
+    }
+
+    private synchronized Connection createConnection() throws Exception {
+        try {
+            ConnectionFactory factory = new ConnectionFactory();
+            factory.setUsername(_username);
+            factory.setPassword(_password);
+            factory.setVirtualHost("/");
+            factory.setHost(_amqpHost);
+            factory.setPort(_port);
+            Connection connection = factory.newConnection();
+            connection.addShutdownListener(disconnectHandler);
+            _connection = connection;
+            return _connection;
+        } catch (Exception e) {
+            throw e;
+        }
+    }
+
+    private synchronized void closeConnection() {
+        try {
+            if (_connection != null) {
+                _connection.close();
+            }
+        } catch (Exception e) {
+            s_logger.warn("Failed to close connection to AMQP server due to " + e.getMessage());
+        }
+        _connection = null;
+    }
+
+    private synchronized void abortConnection () {
+        if (_connection == null)
+            return;
+
+        try {
+            _connection.abort();
+        } catch (Exception e) {
+            s_logger.warn("Failed to abort connection due to " + e.getMessage());
+        }
+        _connection = null;
+    }
+
+    private String replaceNullWithWildcard(String key) {
+        if (key == null || key.isEmpty()) {
+            return "*";
+        } else {
+            return key;
+        }
+    }
+
+    private Channel createChannel(Connection connection) throws Exception {
+        try {
+            return connection.createChannel();
+        } catch (java.io.IOException exception) {
+            s_logger.warn("Failed to create a channel due to " + exception.getMessage());
+            throw exception;
+        }
+    }
+
+    private void createExchange(Channel channel, String exchangeName) throws Exception {
+        try {
+            channel.exchangeDeclare(exchangeName, "topic", true);
+        } catch (java.io.IOException exception) {
+            s_logger.error("Failed to create exchange" + exchangeName + " on RabbitMQ server");
+            throw exception;
+        }
+    }
+
+    private void publishEventToExchange(Channel channel, String exchangeName,
+                                        String routingKey, String eventDescription) throws Exception {
+        try {
+            byte[] messageBodyBytes = eventDescription.getBytes();
+            channel.basicPublish(exchangeName, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, messageBodyBytes);
+        } catch (Exception e) {
+            s_logger.error("Failed to publish event " + routingKey + " on exchange " + exchangeName +
+                    "  of message broker due to " + e.getMessage());
+            throw e;
+        }
+    }
+
+    private String getEventCategoryFromRoutingKey(String routingKey) {
+        String[] keyParts =  routingKey.split("\\.");
+        return keyParts[1];
+    }
+
+    private String getEventTypeFromRoutingKey(String routingKey) {
+        String[] keyParts =  routingKey.split("\\.");
+        return keyParts[2];
+    }
+
+    private String getEventSourceFromRoutingKey(String routingKey) {
+        String[] keyParts =  routingKey.split("\\.");
+        return keyParts[0];
+    }
+
+    private String getResourceTypeFromRoutingKey(String routingKey) {
+        String[] keyParts =  routingKey.split("\\.");
+        return keyParts[3];
+    }
+
+    private String getResourceUUIDFromRoutingKey(String routingKey) {
+        String[] keyParts =  routingKey.split("\\.");
+        return keyParts[4];
+    }
+
+    @Override
+    public String getName() {
+        return _name;
+    }
+
+    @Override
+    public boolean start() {
+        ReconnectionTask reconnect = new ReconnectionTask(); // initiate connection to AMQP server
+        executorService.submit(reconnect);
+        return true;
+    }
+
+    @Override
+    public boolean stop() {
+
+        if (_connection.isOpen()) {
+            for (String subscriberId : _subscribers.keySet()) {
+                Ternary<String, Channel, EventSubscriber> subscriberDetails = _subscribers.get(subscriberId);
+                Channel channel =  subscriberDetails.second();
+                String queueName = subscriberId;
+                try {
+                    channel.queueDelete(queueName);
+                    channel.abort();
+                } catch (IOException ioe) {
+                    s_logger.warn("Failed to delete queue: " + queueName + " on AMQP server due to " + ioe.getMessage() );
+                }
+            }
+        }
+
+        closeConnection();
+        return true;
+    }
+
+    // logic to deal with loss of connection to AMQP server
+    private class DisconnectHandler implements ShutdownListener {
+
+        @Override
+        public void shutdownCompleted(ShutdownSignalException shutdownSignalException) {
+            if (!shutdownSignalException.isInitiatedByApplication()) {
+
+                for (String subscriberId : _subscribers.keySet()) {
+                    Ternary<String, Channel, EventSubscriber> subscriberDetails = _subscribers.get(subscriberId);
+                    subscriberDetails.second(null);
+                    _subscribers.put(subscriberId, subscriberDetails);
+                }
+
+                abortConnection(); // disconnected to AMQP server, so abort the connection and channels
+                s_logger.warn("Connection has been shutdown by AMQP server. Attempting to reconnect.");
+
+                // initiate re-connect process
+                ReconnectionTask reconnect = new ReconnectionTask();
+                executorService.submit(reconnect);
+            }
+        }
+    }
+
+    // retry logic to connect back to AMQP server after loss of connection
+    private class ReconnectionTask implements Runnable {
+
+        boolean connected = false;
+        Connection connection = null;
+
+        public void run() {
+
+            while (!connected) {
+                try {
+                    Thread.sleep(_retryInterval);
+                } catch (InterruptedException ie) {
+                    // ignore timer interrupts
+                }
+
+                try {
+                    try {
+                        connection = createConnection();
+                        connected = true;
+                    } catch (IOException ie) {
+                        continue; // can't establish connection to AMQP server yet, so continue
+                    }
+
+                    // prepare consumer on AMQP server for each of subscriber
+                    for (String subscriberId : _subscribers.keySet()) {
+                        Ternary<String, Channel, EventSubscriber> subscriberDetails = _subscribers.get(subscriberId);
+                        String bindingKey = subscriberDetails.first();
+                        EventSubscriber subscriber = subscriberDetails.third();
+
+                        /** create a queue with subscriber ID as queue name and bind it to the exchange
+                         *  with binding key formed from event topic
+                         */
+                        Channel channel = createChannel(connection);
+                        createExchange(channel, _amqpExchangeName);
+                        channel.queueDeclare(subscriberId, false, false, false, null);
+                        channel.queueBind(subscriberId, _amqpExchangeName, bindingKey);
+
+                        // register a callback handler to receive the events that a subscriber subscribed to
+                        channel.basicConsume(subscriberId, _autoAck, subscriberId,
+                                new DefaultConsumer(channel) {
+                                    @Override
+                                    public void handleDelivery(String queueName,
+                                                               Envelope envelope,
+                                                               AMQP.BasicProperties properties,
+                                                               byte[] body)
+                                            throws IOException {
+
+                                        Ternary<String, Channel, EventSubscriber> subscriberDetails
+                                                = _subscribers.get(queueName); // queue name == subscriber ID
+
+                                        if (subscriberDetails != null) {
+                                            EventSubscriber subscriber = subscriberDetails.third();
+                                            String routingKey =  envelope.getRoutingKey();
+                                            String eventSource = getEventSourceFromRoutingKey(routingKey);
+                                            String eventCategory = getEventCategoryFromRoutingKey(routingKey);
+                                            String eventType = getEventTypeFromRoutingKey(routingKey);
+                                            String resourceType = getResourceTypeFromRoutingKey(routingKey);
+                                            String resourceUUID = getResourceUUIDFromRoutingKey(routingKey);
+
+                                            // create event object from the message details obtained from AMQP server
+                                            Event event = new Event(eventSource, eventCategory, eventType,
+                                                    resourceType, resourceUUID);
+                                            event.setDescription(new String(body));
+
+                                            // deliver the event to call back object provided by subscriber
+                                            subscriber.onEvent(event);
+                                        }
+                                    }
+                                }
+                        );
+
+                        // update the channel details for the subscription
+                        subscriberDetails.second(channel);
+                        _subscribers.put(subscriberId, subscriberDetails);
+                    }
+                } catch (Exception e) {
+                    s_logger.warn("Failed to recreate queues and binding for the subscribers due to " + e.getMessage());
+                }
+            }
+            return;
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java
index ae8482c..e20dd0d 100755
--- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java
+++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java
@@ -145,8 +145,8 @@
         if (dns == null) {
             dns = nic.getDns2();
         }
-        DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName(), dns,
-                nic.getGateway());
+        DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName(), null, dns,
+                nic.getGateway(), null);
         String errMsg = String.format("Set dhcp entry on external DHCP %1$s failed(ip=%2$s, mac=%3$s, vmname=%4$s)", h.getPrivateIpAddress(),
                 nic.getIp4Address(), nic.getMacAddress(), profile.getVirtualMachine().getHostName());
         // prepareBareMetalDhcpEntry(nic, dhcpCommand);
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 96c4348..52358b3 100755
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -1652,20 +1652,27 @@
         }
 
         // ssh -p 3922 -o StrictHostKeyChecking=no -i $cert root@$domr "/root/edithosts.sh $mac $ip $vm $dfltrt $ns $staticrt" >/dev/null
-        String args = " " + cmd.getVmMac();
-        args += " " + cmd.getVmIpAddress();
-        args += " " + cmd.getVmName();
+        String args = " -m " + cmd.getVmMac();
+        if (cmd.getVmIpAddress() != null) {
+        	args += " -4 " + cmd.getVmIpAddress();
+        }
+        args += " -h " + cmd.getVmName();
         
         if (cmd.getDefaultRouter() != null) {
-            args += " " + cmd.getDefaultRouter();
+            args += " -d " + cmd.getDefaultRouter();
         }
         
         if (cmd.getDefaultDns() != null) {
-            args += " " + cmd.getDefaultDns();
+            args += " -n " + cmd.getDefaultDns();
         }
 
         if (cmd.getStaticRoutes() != null) {
-            args +=  " " + cmd.getStaticRoutes();
+            args += " -s " + cmd.getStaticRoutes();
+        }
+        
+        if (cmd.getVmIp6Address() != null) {
+        	args += " -6 " + cmd.getVmIp6Address();
+        	args += " -u " + cmd.getDuid();
         }
         
         if (s_logger.isDebugEnabled()) {
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index e9690eb..e255070 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -1752,7 +1752,9 @@
     protected synchronized Answer execute(final DhcpEntryCommand cmd) {
         Connection conn = getConnection();
         String args = "-r " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
-        args += " -v " + cmd.getVmIpAddress();
+        if (cmd.getVmIpAddress() != null) {
+        	args += " -v " + cmd.getVmIpAddress();
+        }
         args += " -m " + cmd.getVmMac();
         args += " -n " + cmd.getVmName();
         if (cmd.getDefaultRouter() != null) {
@@ -1766,6 +1768,11 @@
         	args += " -N " + cmd.getDefaultDns();
         }
         
+        if (cmd.getVmIp6Address() != null) {
+        	args += " -6 " + cmd.getVmIp6Address();
+        	args += " -u " + cmd.getDuid();
+        }
+        
         String result = callHostPlugin(conn, "vmops", "saveDhcpEntry", "args", args);
         if (result == null || result.isEmpty()) {
             return new Answer(cmd, false, "DhcpEntry failed");
diff --git a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java
index 30a1129..2104322 100644
--- a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java
+++ b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java
@@ -18,6 +18,7 @@
 
 import javax.ejb.Local;
 
+import com.cloud.event.ActionEventUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.dc.DataCenter;
@@ -25,7 +26,6 @@
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
 import com.cloud.event.EventVO;
 import com.cloud.exception.InsufficientVirtualNetworkCapcityException;
 import com.cloud.network.Network;
@@ -95,7 +95,7 @@
                 throw new InsufficientVirtualNetworkCapcityException("Unable to allocate vnet as a part of network " + network + " implement ", DataCenter.class, dcId);
             }
             implemented.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(vnet));
-            EventUtils.saveEvent(UserContext.current().getCallerUserId(), network.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: "+vnet+ " Network Id: "+network.getId(), 0);
+            ActionEventUtils.onCompletedActionEvent(UserContext.current().getCallerUserId(), network.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: " + vnet + " Network Id: " + network.getId(), 0);
         } else {
             implemented.setBroadcastUri(network.getBroadcastUri());
         }
diff --git a/plugins/pom.xml b/plugins/pom.xml
index c5b6e58..f91c6ee 100644
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -41,6 +41,7 @@
     <module>hypervisors/ovm</module>
     <module>hypervisors/xen</module>
     <module>hypervisors/kvm</module>
+    <module>event-bus/rabbitmq</module>
     <module>hypervisors/simulator</module>
     <module>hypervisors/baremetal</module>
     <module>network-elements/elastic-loadbalancer</module>
diff --git a/pom.xml b/pom.xml
index 35d6520..40de4ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,7 @@
     <cs.lang.version>2.6</cs.lang.version>
     <cs.commons-io.version>1.4</cs.commons-io.version>
     <cs.reflections.version>0.9.8</cs.reflections.version>
+    <cs.java-ipv6.version>0.8</cs.java-ipv6.version>
   </properties>
 
   <distributionManagement>
@@ -163,6 +164,7 @@
     <module>patches</module>
     <module>client</module>
     <module>test</module>
+    <module>framework</module>
   </modules>
 
   <dependencyManagement>
diff --git a/scripts/network/domr/dhcp_entry.sh b/scripts/network/domr/dhcp_entry.sh
index c1bf354..e417f72 100755
--- a/scripts/network/domr/dhcp_entry.sh
+++ b/scripts/network/domr/dhcp_entry.sh
@@ -22,24 +22,12 @@
 # @VERSION@
 
 usage() {
-  printf "Usage: %s: -r <domr-ip> -m <vm mac> -v <vm ip> -n <vm name>\n" $(basename $0) >&2
+  printf "Usage: %s: -r <domr-ip> -m <vm mac> -v <vm ip> -n <vm name> -s <static route> -d <default router> -N <dns> -6 <vm IPv6> -u <duid>\n" $(basename $0) >&2
   exit 2
 }
 
 cert="/root/.ssh/id_rsa.cloud"
 
-add_dhcp_entry() {
-  local domr=$1
-  local mac=$2
-  local ip=$3
-  local vm=$4
-  local dfltrt=$5
-  local ns=$6
-  local staticrt=$7
-  ssh -p 3922 -o StrictHostKeyChecking=no -i $cert root@$domr "/root/edithosts.sh $mac $ip $vm $dfltrt $ns $staticrt" >/dev/null
-  return $?
-}
-
 domrIp=
 vmMac=
 vmIp=
@@ -47,30 +35,46 @@
 staticrt=
 dfltrt=
 dns=
+ipv6=
+duid=
 
-while getopts 'r:m:v:n:d:s:N:' OPTION
+opts=
+
+while getopts 'r:m:v:n:d:s:N:6:u:' OPTION
 do
   case $OPTION in
-  r)	domrIp="$OPTARG"
-		;;
-  v)	vmIp="$OPTARG"
-		;;
-  m)	vmMac="$OPTARG"
-		;;
-  n)	vmName="$OPTARG"
-		;;
-  s)	staticrt="$OPTARG"
-		;;
-  d)	dfltrt="$OPTARG"
-		;;
-  N)	dns="$OPTARG"
-		;;
-  ?)    usage
-		exit 1
-		;;
+  r)  domrIp="$OPTARG"
+      ;;
+  v)  vmIp="$OPTARG"
+      opts="$opts -4 $vmIp"
+      ;;
+  m)  vmMac="$OPTARG"
+      opts="$opts -m $vmMac"
+      ;;
+  n)  vmName="$OPTARG"
+      opts="$opts -h $vmName"
+      ;;
+  s)  staticrt="$OPTARG"
+      opts="$opts -s $staticrt"
+      ;;
+  d)  dfltrt="$OPTARG"
+      opts="$opts -d $dfltrt"
+      ;;
+  N)  dns="$OPTARG"
+      opts="$opts -n $dns"
+      ;;
+  6)  ipv6="$OPTARG"
+      opts="$opts -6 $ipv6"
+      ;;
+  u)  duid="$OPTARG"
+      opts="$opts -u $duid"
+      ;;
+  ?)  usage
+      exit 1
+      ;;
   esac
 done
 
-add_dhcp_entry $domrIp $vmMac $vmIp $vmName $dfltrt $dns $staticrt
+ssh -p 3922 -o StrictHostKeyChecking=no -i $cert root@$domrIp "/root/edithosts.sh $opts " >/dev/null
 
 exit $?
diff --git a/server/pom.xml b/server/pom.xml
index 4c3ba6f..ef1b68a 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -80,6 +80,11 @@
       <classifier>tests</classifier>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-framework-events</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
   <build>
     <defaultGoal>install</defaultGoal>
diff --git a/server/src/com/cloud/alert/AlertManagerImpl.java b/server/src/com/cloud/alert/AlertManagerImpl.java
index a4c0fac..1a93f97 100755
--- a/server/src/com/cloud/alert/AlertManagerImpl.java
+++ b/server/src/com/cloud/alert/AlertManagerImpl.java
@@ -59,6 +59,7 @@
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.dc.dao.DataCenterIpAddressDao;
 import com.cloud.dc.dao.HostPodDao;
+import com.cloud.event.AlertGenerator;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
@@ -260,6 +261,10 @@
 
     @Override
     public void sendAlert(short alertType, long dataCenterId, Long podId, String subject, String body) {
+
+        // publish alert
+        AlertGenerator.publishAlertOnEventBus(getAlertType(alertType), dataCenterId, podId, subject, body);
+
         // TODO:  queue up these messages and send them as one set of issues once a certain number of issues is reached?  If that's the case,
         //         shouldn't we have a type/severity as part of the API so that severe errors get sent right away?
         try {
@@ -271,6 +276,65 @@
         }
     }
 
+    private String getAlertType(short alertType) {
+        if (alertType == ALERT_TYPE_MEMORY) {
+            return "ALERT.MEMORY";
+        } else if (alertType == ALERT_TYPE_CPU) {
+            return "ALERT.MEMORY";
+        } else if (alertType == ALERT_TYPE_STORAGE) {
+            return "ALERT.STORAGE";
+        } else if (alertType == ALERT_TYPE_STORAGE_ALLOCATED) {
+            return "ALERT.STORAGE.ALLOCATED";
+        } else if (alertType == ALERT_TYPE_VIRTUAL_NETWORK_PUBLIC_IP) {
+            return "ALERT.NETWORK.PUBLICIP";
+        } else if (alertType == ALERT_TYPE_PRIVATE_IP) {
+            return "ALERT.NETWORK.PRIVATEIP";
+        } else if (alertType == ALERT_TYPE_SECONDARY_STORAGE) {
+            return "ALERT.STORAGE.SECONDARY";
+        } else if (alertType == ALERT_TYPE_HOST) {
+            return "ALERT.COMPUTE.HOST";
+        } else if (alertType == ALERT_TYPE_USERVM) {
+            return "ALERT.USERVM";
+        } else if (alertType == ALERT_TYPE_DOMAIN_ROUTER) {
+            return "ALERT.SERVICE.DOMAINROUTER";
+        } else if (alertType == ALERT_TYPE_CONSOLE_PROXY) {
+            return "ALERT.SERVICE.CONSOLEPROXY";
+        } else if (alertType == ALERT_TYPE_ROUTING) {
+            return "ALERT.NETWORK.ROUTING";
+        } else if (alertType == ALERT_TYPE_STORAGE_MISC) {
+            return "ALERT.STORAGE.MISC";
+        } else if (alertType == ALERT_TYPE_USAGE_SERVER) {
+            return "ALERT.USAGE";
+        } else if (alertType == ALERT_TYPE_MANAGMENT_NODE) {
+            return "ALERT.MANAGEMENT";
+        } else if (alertType == ALERT_TYPE_DOMAIN_ROUTER_MIGRATE) {
+            return "ALERT.NETWORK.DOMAINROUTERMIGRATE";
+        } else if (alertType == ALERT_TYPE_CONSOLE_PROXY_MIGRATE) {
+            return "ALERT.SERVICE.CONSOLEPROXYMIGRATE";
+        } else if (alertType == ALERT_TYPE_USERVM_MIGRATE) {
+            return "ALERT.USERVM.MIGRATE";
+        } else if (alertType == ALERT_TYPE_VLAN) {
+            return "ALERT.NETWORK.VLAN";
+        } else if (alertType == ALERT_TYPE_SSVM) {
+            return "ALERT.SERVICE.SSVM";
+        } else if (alertType == ALERT_TYPE_USAGE_SERVER_RESULT) {
+            return "ALERT.USAGE.RESULT";
+        } else if (alertType == ALERT_TYPE_STORAGE_DELETE) {
+            return "ALERT.STORAGE.DELETE";
+        } else if (alertType == ALERT_TYPE_UPDATE_RESOURCE_COUNT) {
+            return "ALERT.RESOURCE.COUNT";
+        } else if (alertType == ALERT_TYPE_USAGE_SANITY_RESULT) {
+            return "ALERT.USAGE.SANITY";
+        } else if (alertType == ALERT_TYPE_DIRECT_ATTACHED_PUBLIC_IP) {
+            return "ALERT.NETWORK.DIRECTPUBLICIP";
+        } else if (alertType == ALERT_TYPE_LOCAL_STORAGE) {
+            return "ALERT.STORAGE.LOCAL";
+        } else if (alertType == ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED) {
+            return "ALERT.RESOURCE.EXCEED";
+        }
+        return "UNKNOWN";
+    }
+
     @Override @DB
     public void recalculateCapacity() {
         // FIXME: the right way to do this is to register a listener (see RouterStatsListener, VMSyncListener)
diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java
index 0b08b26..143e280 100755
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -16,68 +16,8 @@
 // under the License.
 package com.cloud.api;
 
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.cloudstack.api.ApiConstants.HostDetails;
-import org.apache.cloudstack.api.ApiConstants.VMDetails;
-import org.apache.cloudstack.api.response.AccountResponse;
-import org.apache.cloudstack.api.response.AsyncJobResponse;
-import org.apache.cloudstack.api.response.DiskOfferingResponse;
-import org.apache.cloudstack.api.response.DomainRouterResponse;
-import org.apache.cloudstack.api.response.EventResponse;
-import org.apache.cloudstack.api.response.HostResponse;
-import org.apache.cloudstack.api.response.InstanceGroupResponse;
-import org.apache.cloudstack.api.response.ProjectAccountResponse;
-import org.apache.cloudstack.api.response.ProjectInvitationResponse;
-import org.apache.cloudstack.api.response.ProjectResponse;
-import org.apache.cloudstack.api.response.ResourceTagResponse;
-import org.apache.cloudstack.api.response.SecurityGroupResponse;
-import org.apache.cloudstack.api.response.ServiceOfferingResponse;
-import org.apache.cloudstack.api.response.StoragePoolResponse;
-import org.apache.cloudstack.api.response.UserResponse;
-import org.apache.cloudstack.api.response.UserVmResponse;
-import org.apache.cloudstack.api.response.VolumeResponse;
-import org.apache.cloudstack.api.response.ZoneResponse;
-
-import com.cloud.api.query.dao.AccountJoinDao;
-import com.cloud.api.query.dao.AsyncJobJoinDao;
-import com.cloud.api.query.dao.DataCenterJoinDao;
-import com.cloud.api.query.dao.DiskOfferingJoinDao;
-import com.cloud.api.query.dao.DomainRouterJoinDao;
-import com.cloud.api.query.dao.HostJoinDao;
-import com.cloud.api.query.dao.InstanceGroupJoinDao;
-import com.cloud.api.query.dao.ProjectAccountJoinDao;
-import com.cloud.api.query.dao.ProjectInvitationJoinDao;
-import com.cloud.api.query.dao.ProjectJoinDao;
-import com.cloud.api.query.dao.ResourceTagJoinDao;
-import com.cloud.api.query.dao.SecurityGroupJoinDao;
-import com.cloud.api.query.dao.ServiceOfferingJoinDao;
-import com.cloud.api.query.dao.StoragePoolJoinDao;
-import com.cloud.api.query.dao.UserAccountJoinDao;
-import com.cloud.api.query.dao.UserVmJoinDao;
-import com.cloud.api.query.dao.VolumeJoinDao;
-import com.cloud.api.query.vo.AccountJoinVO;
-import com.cloud.api.query.vo.AsyncJobJoinVO;
-import com.cloud.api.query.vo.DataCenterJoinVO;
-import com.cloud.api.query.vo.DiskOfferingJoinVO;
-import com.cloud.api.query.vo.DomainRouterJoinVO;
-import com.cloud.api.query.vo.EventJoinVO;
-import com.cloud.api.query.vo.HostJoinVO;
-import com.cloud.api.query.vo.InstanceGroupJoinVO;
-import com.cloud.api.query.vo.ProjectAccountJoinVO;
-import com.cloud.api.query.vo.ProjectInvitationJoinVO;
-import com.cloud.api.query.vo.ProjectJoinVO;
-import com.cloud.api.query.vo.ResourceTagJoinVO;
-import com.cloud.api.query.vo.SecurityGroupJoinVO;
-import com.cloud.api.query.vo.ServiceOfferingJoinVO;
-import com.cloud.api.query.vo.StoragePoolJoinVO;
-import com.cloud.api.query.vo.UserAccountJoinVO;
-import com.cloud.api.query.vo.UserVmJoinVO;
-import com.cloud.api.query.vo.VolumeJoinVO;
+import com.cloud.api.query.dao.*;
+import com.cloud.api.query.vo.*;
 import com.cloud.async.AsyncJob;
 import com.cloud.async.AsyncJobManager;
 import com.cloud.async.AsyncJobVO;
@@ -89,18 +29,8 @@
 import com.cloud.configuration.ConfigurationService;
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.configuration.dao.ConfigurationDao;
-import com.cloud.dc.AccountVlanMapVO;
-import com.cloud.dc.ClusterVO;
-import com.cloud.dc.DataCenter;
-import com.cloud.dc.DataCenterVO;
-import com.cloud.dc.HostPodVO;
-import com.cloud.dc.Vlan;
-import com.cloud.dc.VlanVO;
-import com.cloud.dc.dao.AccountVlanMapDao;
-import com.cloud.dc.dao.ClusterDao;
-import com.cloud.dc.dao.DataCenterDao;
-import com.cloud.dc.dao.HostPodDao;
-import com.cloud.dc.dao.VlanDao;
+import com.cloud.dc.*;
+import com.cloud.dc.dao.*;
 import com.cloud.domain.DomainVO;
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.Event;
@@ -112,65 +42,23 @@
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.network.IPAddressVO;
-import com.cloud.network.IpAddress;
-import com.cloud.network.LoadBalancerVO;
-import com.cloud.network.Network;
+import com.cloud.network.*;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
-import com.cloud.network.NetworkDomainVO;
-import com.cloud.network.NetworkManager;
-import com.cloud.network.NetworkModel;
-import com.cloud.network.NetworkProfile;
-import com.cloud.network.NetworkRuleConfigVO;
-import com.cloud.network.NetworkVO;
-import com.cloud.network.PhysicalNetworkServiceProvider;
-import com.cloud.network.PhysicalNetworkVO;
-import com.cloud.network.Site2SiteVpnGatewayVO;
-import com.cloud.network.Site2SiteCustomerGatewayVO;
 import com.cloud.network.Networks.TrafficType;
-import com.cloud.network.as.AutoScalePolicy;
-import com.cloud.network.as.AutoScalePolicyConditionMapVO;
-import com.cloud.network.as.AutoScalePolicyVO;
-import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO;
-import com.cloud.network.as.AutoScaleVmGroupVO;
-import com.cloud.network.as.AutoScaleVmProfileVO;
-import com.cloud.network.as.ConditionVO;
-import com.cloud.network.as.CounterVO;
-import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao;
-import com.cloud.network.as.dao.AutoScalePolicyDao;
-import com.cloud.network.as.dao.AutoScaleVmGroupDao;
-import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao;
-import com.cloud.network.as.dao.AutoScaleVmProfileDao;
-import com.cloud.network.as.dao.ConditionDao;
-import com.cloud.network.as.dao.CounterDao;
-import com.cloud.network.dao.FirewallRulesCidrsDao;
-import com.cloud.network.dao.FirewallRulesDao;
-import com.cloud.network.dao.IPAddressDao;
-import com.cloud.network.dao.LoadBalancerDao;
-import com.cloud.network.dao.NetworkDao;
-import com.cloud.network.dao.PhysicalNetworkDao;
-import com.cloud.network.dao.NetworkDomainDao;
-import com.cloud.network.dao.NetworkRuleConfigDao;
-import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
-import com.cloud.network.dao.PhysicalNetworkServiceProviderVO;
-import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
-import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
-import com.cloud.network.dao.Site2SiteVpnGatewayDao;
-import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
+import com.cloud.network.as.*;
+import com.cloud.network.as.dao.*;
+import com.cloud.network.dao.*;
 import com.cloud.network.router.VirtualRouter;
 import com.cloud.network.rules.FirewallRuleVO;
 import com.cloud.network.security.SecurityGroup;
 import com.cloud.network.security.SecurityGroupManager;
 import com.cloud.network.security.SecurityGroupVO;
 import com.cloud.network.security.dao.SecurityGroupDao;
-import com.cloud.network.vpc.StaticRouteVO;
-import com.cloud.network.vpc.VpcGatewayVO;
-import com.cloud.network.vpc.VpcManager;
-import com.cloud.network.vpc.VpcOffering;
-import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.*;
 import com.cloud.network.vpc.dao.StaticRouteDao;
+import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.network.vpc.dao.VpcGatewayDao;
 import com.cloud.network.vpc.dao.VpcOfferingDao;
 import com.cloud.offering.DiskOffering;
@@ -183,57 +71,16 @@
 import com.cloud.projects.ProjectInvitation;
 import com.cloud.projects.ProjectService;
 import com.cloud.resource.ResourceManager;
-import com.cloud.server.Criteria;
-import com.cloud.server.ManagementServer;
-import com.cloud.server.ResourceTag;
+import com.cloud.server.*;
 import com.cloud.server.ResourceTag.TaggedResourceType;
-import com.cloud.server.StatsCollector;
-import com.cloud.server.TaggedResourceService;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.storage.GuestOS;
-import com.cloud.storage.GuestOSCategoryVO;
-import com.cloud.storage.Snapshot;
-import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.*;
 import com.cloud.storage.Storage.ImageFormat;
-import com.cloud.storage.StorageManager;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.StoragePoolVO;
-import com.cloud.storage.StorageStats;
-import com.cloud.storage.UploadVO;
-import com.cloud.storage.VMTemplateHostVO;
-import com.cloud.storage.VMTemplateS3VO;
-import com.cloud.storage.VMTemplateSwiftVO;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.Volume;
 import com.cloud.storage.Volume.Type;
-import com.cloud.storage.VolumeHostVO;
-import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.DiskOfferingDao;
-import com.cloud.storage.dao.GuestOSCategoryDao;
-import com.cloud.storage.dao.GuestOSDao;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.storage.dao.SnapshotPolicyDao;
-import com.cloud.storage.dao.StoragePoolDao;
-import com.cloud.storage.dao.UploadDao;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VMTemplateDetailsDao;
-import com.cloud.storage.dao.VMTemplateHostDao;
-import com.cloud.storage.dao.VMTemplateS3Dao;
-import com.cloud.storage.dao.VMTemplateSwiftDao;
-import com.cloud.storage.dao.VolumeDao;
-import com.cloud.storage.dao.VolumeHostDao;
+import com.cloud.storage.dao.*;
 import com.cloud.storage.snapshot.SnapshotPolicy;
-import com.cloud.user.Account;
-import com.cloud.user.AccountDetailsDao;
-import com.cloud.user.AccountVO;
-import com.cloud.user.ResourceLimitService;
-import com.cloud.user.SSHKeyPairVO;
-import com.cloud.user.User;
-import com.cloud.user.UserAccount;
-import com.cloud.user.UserStatisticsVO;
-import com.cloud.user.UserVO;
+import com.cloud.user.*;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.user.dao.SSHKeyPairDao;
 import com.cloud.user.dao.UserDao;
@@ -242,23 +89,13 @@
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.Pair;
 import com.cloud.utils.component.ComponentLocator;
-import com.cloud.vm.ConsoleProxyVO;
-import com.cloud.vm.DomainRouterVO;
-import com.cloud.vm.InstanceGroup;
-import com.cloud.vm.InstanceGroupVO;
-import com.cloud.vm.NicProfile;
-import com.cloud.vm.UserVmDetailVO;
-import com.cloud.vm.UserVmManager;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.VirtualMachine;
-import com.cloud.vm.VmStats;
-import com.cloud.vm.dao.ConsoleProxyDao;
-import com.cloud.vm.dao.DomainRouterDao;
-import com.cloud.vm.dao.UserVmDao;
-import com.cloud.vm.dao.UserVmDetailsDao;
-import com.cloud.vm.dao.VMInstanceDao;
-import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.vm.*;
+import com.cloud.vm.dao.*;
+import org.apache.cloudstack.api.ApiConstants.HostDetails;
+import org.apache.cloudstack.api.ApiConstants.VMDetails;
+import org.apache.cloudstack.api.response.*;
+
+import java.util.*;
 
 public class ApiDBUtils {
     private static ManagementServer _ms;
@@ -699,7 +536,7 @@
 
     public static Snapshot findSnapshotById(long snapshotId) {
         SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
-        if (snapshot != null && snapshot.getRemoved() == null && snapshot.getStatus() == Snapshot.Status.BackedUp) {
+        if (snapshot != null && snapshot.getRemoved() == null && snapshot.getState() == Snapshot.State.BackedUp) {
             return snapshot;
         } else {
             return null;
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 96494b8..0ae4f0f 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -16,128 +16,9 @@
 // under the License.
 package com.cloud.api;
 
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.ResponseGenerator;
-import org.apache.log4j.Logger;
-
-import org.apache.cloudstack.acl.ControlledEntity;
-import org.apache.cloudstack.acl.ControlledEntity.ACLType;
-import org.apache.cloudstack.api.ApiConstants.HostDetails;
-import org.apache.cloudstack.api.ApiConstants.VMDetails;
-import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
-import org.apache.cloudstack.api.response.AccountResponse;
-
 import com.cloud.api.query.ViewResponseHelper;
-import com.cloud.api.query.vo.AccountJoinVO;
-import com.cloud.api.query.vo.AsyncJobJoinVO;
-import com.cloud.api.query.vo.ControlledViewEntity;
-import com.cloud.api.query.vo.DataCenterJoinVO;
-import com.cloud.api.query.vo.DiskOfferingJoinVO;
-import com.cloud.api.query.vo.DomainRouterJoinVO;
-import com.cloud.api.query.vo.EventJoinVO;
-import com.cloud.api.query.vo.HostJoinVO;
-import com.cloud.api.query.vo.InstanceGroupJoinVO;
-import com.cloud.api.query.vo.ProjectAccountJoinVO;
-import com.cloud.api.query.vo.ProjectInvitationJoinVO;
-import com.cloud.api.query.vo.ProjectJoinVO;
-import com.cloud.api.query.vo.ResourceTagJoinVO;
-import com.cloud.api.query.vo.SecurityGroupJoinVO;
-import com.cloud.api.query.vo.ServiceOfferingJoinVO;
-import com.cloud.api.query.vo.StoragePoolJoinVO;
-import com.cloud.api.query.vo.UserAccountJoinVO;
-import com.cloud.api.query.vo.UserVmJoinVO;
-import com.cloud.api.query.vo.VolumeJoinVO;
+import com.cloud.api.query.vo.*;
 import com.cloud.api.response.ApiResponseSerializer;
-import org.apache.cloudstack.api.response.AsyncJobResponse;
-import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
-import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
-import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
-import org.apache.cloudstack.api.response.CapabilityResponse;
-import org.apache.cloudstack.api.response.CapacityResponse;
-import org.apache.cloudstack.api.response.ClusterResponse;
-import org.apache.cloudstack.api.response.ConditionResponse;
-import org.apache.cloudstack.api.response.ConfigurationResponse;
-import org.apache.cloudstack.api.response.ControlledEntityResponse;
-import org.apache.cloudstack.api.response.CounterResponse;
-import org.apache.cloudstack.api.response.CreateCmdResponse;
-import org.apache.cloudstack.api.response.DiskOfferingResponse;
-import org.apache.cloudstack.api.response.DomainResponse;
-import org.apache.cloudstack.api.response.DomainRouterResponse;
-import org.apache.cloudstack.api.response.EventResponse;
-import org.apache.cloudstack.api.response.ExtractResponse;
-import org.apache.cloudstack.api.response.FirewallResponse;
-import org.apache.cloudstack.api.response.FirewallRuleResponse;
-import org.apache.cloudstack.api.response.GuestOSResponse;
-import org.apache.cloudstack.api.response.HostResponse;
-import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse;
-import org.apache.cloudstack.api.response.ControlledViewEntityResponse;
-import org.apache.cloudstack.api.response.IPAddressResponse;
-import org.apache.cloudstack.api.response.InstanceGroupResponse;
-import org.apache.cloudstack.api.response.IpForwardingRuleResponse;
-import org.apache.cloudstack.api.response.LBStickinessPolicyResponse;
-import org.apache.cloudstack.api.response.LBStickinessResponse;
-import org.apache.cloudstack.api.response.LDAPConfigResponse;
-import org.apache.cloudstack.api.response.LoadBalancerResponse;
-import org.apache.cloudstack.api.response.NetworkACLResponse;
-import org.apache.cloudstack.api.response.NetworkOfferingResponse;
-import org.apache.cloudstack.api.response.NetworkResponse;
-import org.apache.cloudstack.api.response.PhysicalNetworkResponse;
-import org.apache.cloudstack.api.response.PodResponse;
-import org.apache.cloudstack.api.response.PrivateGatewayResponse;
-import org.apache.cloudstack.api.response.ProjectAccountResponse;
-import org.apache.cloudstack.api.response.ProjectInvitationResponse;
-import org.apache.cloudstack.api.response.ProjectResponse;
-import org.apache.cloudstack.api.response.ProviderResponse;
-import org.apache.cloudstack.api.response.RegionResponse;
-import org.apache.cloudstack.api.response.RemoteAccessVpnResponse;
-import org.apache.cloudstack.api.response.ResourceCountResponse;
-import org.apache.cloudstack.api.response.ResourceLimitResponse;
-import org.apache.cloudstack.api.response.ResourceTagResponse;
-import org.apache.cloudstack.api.response.SecurityGroupResponse;
-import org.apache.cloudstack.api.response.SecurityGroupRuleResponse;
-import org.apache.cloudstack.api.response.ServiceOfferingResponse;
-import org.apache.cloudstack.api.response.ServiceResponse;
-import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse;
-import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse;
-import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse;
-import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
-import org.apache.cloudstack.api.response.SnapshotResponse;
-import org.apache.cloudstack.api.response.SnapshotScheduleResponse;
-import org.apache.cloudstack.api.response.StaticRouteResponse;
-import org.apache.cloudstack.api.response.StorageNetworkIpRangeResponse;
-import org.apache.cloudstack.api.response.StoragePoolResponse;
-import org.apache.cloudstack.api.response.SwiftResponse;
-import org.apache.cloudstack.api.response.SystemVmInstanceResponse;
-import org.apache.cloudstack.api.response.SystemVmResponse;
-import org.apache.cloudstack.api.response.TemplatePermissionsResponse;
-import org.apache.cloudstack.api.response.TemplateResponse;
-import org.apache.cloudstack.api.response.TrafficTypeResponse;
-import org.apache.cloudstack.api.response.UserResponse;
-import org.apache.cloudstack.api.response.UserVmResponse;
-import org.apache.cloudstack.api.response.VirtualRouterProviderResponse;
-import org.apache.cloudstack.api.response.VlanIpRangeResponse;
-import org.apache.cloudstack.api.response.VolumeResponse;
-import org.apache.cloudstack.api.response.VpcOfferingResponse;
-import org.apache.cloudstack.api.response.VpcResponse;
-import org.apache.cloudstack.api.response.VpnUsersResponse;
-import org.apache.cloudstack.api.response.ZoneResponse;
-
-import org.apache.cloudstack.api.response.S3Response;
-import org.apache.cloudstack.region.Region;
-
 import com.cloud.async.AsyncJob;
 import com.cloud.capacity.Capacity;
 import com.cloud.capacity.CapacityVO;
@@ -146,53 +27,21 @@
 import com.cloud.configuration.Resource.ResourceOwnerType;
 import com.cloud.configuration.ResourceCount;
 import com.cloud.configuration.ResourceLimit;
-import com.cloud.dc.ClusterVO;
-import com.cloud.dc.DataCenter;
-import com.cloud.dc.DataCenterVO;
-import com.cloud.dc.HostPodVO;
-import com.cloud.dc.Pod;
-import com.cloud.dc.StorageNetworkIpRange;
-import com.cloud.dc.Vlan;
+import com.cloud.dc.*;
 import com.cloud.dc.Vlan.VlanType;
-import com.cloud.dc.VlanVO;
 import com.cloud.domain.Domain;
 import com.cloud.event.Event;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.hypervisor.HypervisorCapabilities;
-import com.cloud.network.IPAddressVO;
-import com.cloud.network.IpAddress;
-import com.cloud.network.Network;
+import com.cloud.network.*;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
-import com.cloud.network.NetworkProfile;
-import com.cloud.network.NetworkVO;
 import com.cloud.network.Networks.TrafficType;
-import com.cloud.network.PhysicalNetwork;
-import com.cloud.network.PhysicalNetworkServiceProvider;
-import com.cloud.network.PhysicalNetworkTrafficType;
-import com.cloud.network.PhysicalNetworkVO;
-import com.cloud.network.RemoteAccessVpn;
-import com.cloud.network.Site2SiteCustomerGateway;
-import com.cloud.network.Site2SiteVpnConnection;
-import com.cloud.network.Site2SiteVpnGateway;
-import com.cloud.network.VirtualRouterProvider;
-import com.cloud.network.VpnUser;
-import com.cloud.network.as.AutoScalePolicy;
-import com.cloud.network.as.AutoScaleVmGroup;
-import com.cloud.network.as.AutoScaleVmProfile;
-import com.cloud.network.as.AutoScaleVmProfileVO;
-import com.cloud.network.as.Condition;
-import com.cloud.network.as.ConditionVO;
-import com.cloud.network.as.Counter;
+import com.cloud.network.as.*;
 import com.cloud.network.router.VirtualRouter;
-import com.cloud.network.rules.FirewallRule;
-import com.cloud.network.rules.FirewallRuleVO;
-import com.cloud.network.rules.LoadBalancer;
-import com.cloud.network.rules.PortForwardingRule;
-import com.cloud.network.rules.StaticNatRule;
-import com.cloud.network.rules.StickinessPolicy;
+import com.cloud.network.rules.*;
 import com.cloud.network.security.SecurityGroup;
 import com.cloud.network.security.SecurityRule;
 import com.cloud.network.security.SecurityRule.SecurityRuleType;
@@ -210,25 +59,11 @@
 import com.cloud.server.Criteria;
 import com.cloud.server.ResourceTag;
 import com.cloud.server.ResourceTag.TaggedResourceType;
-import com.cloud.storage.GuestOS;
-import com.cloud.storage.GuestOSCategoryVO;
-import com.cloud.storage.S3;
-import com.cloud.storage.Snapshot;
+import com.cloud.storage.*;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.Storage.TemplateType;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.StoragePoolVO;
-import com.cloud.storage.StorageStats;
-import com.cloud.storage.Swift;
-import com.cloud.storage.UploadVO;
-import com.cloud.storage.VMTemplateHostVO;
-import com.cloud.storage.VMTemplateS3VO;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
-import com.cloud.storage.VMTemplateSwiftVO;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.Volume;
-import com.cloud.storage.VolumeVO;
 import com.cloud.storage.snapshot.SnapshotPolicy;
 import com.cloud.storage.snapshot.SnapshotSchedule;
 import com.cloud.template.VirtualMachineTemplate;
@@ -246,6 +81,22 @@
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.Type;
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.ControlledEntity.ACLType;
+import org.apache.cloudstack.api.ApiConstants.HostDetails;
+import org.apache.cloudstack.api.ApiConstants.VMDetails;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.ResponseGenerator;
+import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
+import org.apache.cloudstack.api.response.*;
+import org.apache.cloudstack.region.Region;
+import org.apache.log4j.Logger;
+
+import java.text.DecimalFormat;
+import java.util.*;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
 
 public class ApiResponseHelper implements ResponseGenerator {
 
@@ -382,7 +233,7 @@
         snapshotResponse.setCreated(snapshot.getCreated());
         snapshotResponse.setName(snapshot.getName());
         snapshotResponse.setIntervalType(ApiDBUtils.getSnapshotIntervalTypes(snapshot.getId()));
-        snapshotResponse.setState(snapshot.getStatus());
+        snapshotResponse.setState(snapshot.getState());
 
         //set tag information
         List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.Snapshot, snapshot.getId());
@@ -485,9 +336,21 @@
 
         // get start ip and end ip of corresponding vlan
         String ipRange = vlan.getIpRange();
-        String[] range = ipRange.split("-");
-        vlanResponse.setStartIp(range[0]);
-        vlanResponse.setEndIp(range[1]);
+        if (ipRange != null) {
+        	String[] range = ipRange.split("-");
+        	vlanResponse.setStartIp(range[0]);
+        	vlanResponse.setEndIp(range[1]);
+        }
+
+        vlanResponse.setIp6Gateway(vlan.getIp6Gateway());
+        vlanResponse.setIp6Cidr(vlan.getIp6Cidr());
+        
+        String ip6Range = vlan.getIp6Range();
+        if (ip6Range != null) {
+        	String[] range = ip6Range.split("-");
+        	vlanResponse.setStartIpv6(range[0]);
+        	vlanResponse.setEndIpv6(range[1]);
+        }
 
         if (vlan.getNetworkId() != null) {
             Network nw = ApiDBUtils.findNetworkById(vlan.getNetworkId());
@@ -2155,6 +2018,9 @@
         if (network.getCidr() != null) {
             response.setNetmask(NetUtils.cidr2Netmask(network.getCidr()));
         }
+        
+        response.setIp6Gateway(network.getIp6Gateway());
+        response.setIp6Cidr(network.getIp6Cidr());
 
         //return vlan information only to Root admin
         if (network.getBroadcastUri() != null && UserContext.current().getCaller().getType() == Account.ACCOUNT_TYPE_ADMIN) {
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index cb2f414..714a500 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -50,10 +50,10 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import com.cloud.event.ActionEventUtils;
 import com.cloud.utils.ReflectUtil;
 import org.apache.cloudstack.acl.APILimitChecker;
 import org.apache.cloudstack.acl.APIChecker;
-import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.*;
 import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
 import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
@@ -117,7 +117,6 @@
 import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.domain.Domain;
 import com.cloud.domain.DomainVO;
-import com.cloud.event.EventUtils;
 import com.cloud.exception.AccountLimitException;
 import com.cloud.exception.CloudAuthenticationException;
 import com.cloud.exception.InsufficientCapacityException;
@@ -470,7 +469,7 @@
             asyncCmd.setStartEventId(startEventId);
 
             // save the scheduled event
-            Long eventId = EventUtils.saveScheduledEvent((callerUserId == null) ? User.UID_SYSTEM : callerUserId,
+            Long eventId = ActionEventUtils.onScheduledActionEvent((callerUserId == null) ? User.UID_SYSTEM : callerUserId,
                     asyncCmd.getEntityOwnerId(), asyncCmd.getEventType(), asyncCmd.getEventDescription(),
                     startEventId);
             if (startEventId == 0) {
diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java
index cf240b1..c607383 100644
--- a/server/src/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/com/cloud/api/query/QueryManagerImpl.java
@@ -1893,23 +1893,23 @@
         }
 
         if (name != null) {
-            sc.setParameters("name", SearchCriteria.Op.LIKE, "%" + name + "%");
+            sc.setParameters("name", "%" + name + "%");
         }
 
         if (path != null) {
-            sc.setParameters("path", SearchCriteria.Op.EQ, path);
+            sc.setParameters("path", path);
         }
         if (zoneId != null) {
-            sc.setParameters("dataCenterId", SearchCriteria.Op.EQ, zoneId);
+            sc.setParameters("dataCenterId", zoneId);
         }
         if (pod != null) {
-            sc.setParameters("podId", SearchCriteria.Op.EQ, pod);
+            sc.setParameters("podId", pod);
         }
         if (address != null) {
-            sc.setParameters("hostAddress", SearchCriteria.Op.EQ, address);
+            sc.setParameters("hostAddress", address);
         }
         if (cluster != null) {
-            sc.setParameters("clusterId", SearchCriteria.Op.EQ, cluster);
+            sc.setParameters("clusterId", cluster);
         }
 
         // search Pool details by ids
diff --git a/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java b/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java
index 25298a9..74200e2 100755
--- a/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java
+++ b/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java
@@ -16,20 +16,10 @@
 // under the License.
 package com.cloud.baremetal;
 
-import java.util.Date;
-import java.util.List;
-
-import javax.ejb.Local;
-
-import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
-import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
-import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
-import org.apache.log4j.Logger;
-
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
@@ -46,6 +36,14 @@
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
+import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
+import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import java.util.Date;
+import java.util.List;
 
 @Local(value=TemplateAdapter.class)
 public class BareMetalTemplateAdapter extends TemplateAdapterBase implements TemplateAdapter {
@@ -82,9 +80,9 @@
 	
 	private void templateCreateUsage(VMTemplateVO template, HostVO host) {
 		if (template.getAccountId() != Account.ACCOUNT_ID_SYSTEM) {
-			UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_TEMPLATE_CREATE, template.getAccountId(), host.getDataCenterId(),
-					template.getId(), template.getName(), null, template.getSourceTemplateId(), 0L);
-			_usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_TEMPLATE_CREATE, template.getAccountId(), host.getDataCenterId(),
+                    template.getId(), template.getName(), null, template.getSourceTemplateId(), 0L,
+                    template.getClass().getName(), template.getUuid());
 		}
 	}
 	
@@ -172,8 +170,8 @@
 					_tmpltZoneDao.remove(templateZone.getId());
 				}
 
-				UsageEventVO usageEvent = new UsageEventVO(eventType, account.getId(), pxeServer.getDataCenterId(), templateId, null);
-				_usageEventDao.persist(usageEvent);
+                UsageEventUtils.publishUsageEvent(eventType, account.getId(), pxeServer.getDataCenterId(),
+                        templateId, null, template.getClass().getName(), template.getUuid());
 			} finally {
 				if (lock != null) {
 					_tmpltHostDao.releaseFromLockTable(lock.getId());
diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java
index 57cfb39..6ff37ea 100755
--- a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java
+++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java
@@ -16,28 +16,11 @@
 // under the License.
 package com.cloud.baremetal;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executors;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd;
-import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
-import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
-import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.StopAnswer;
 import com.cloud.agent.api.baremetal.IpmISetBootDevCommand;
 import com.cloud.agent.api.baremetal.IpmiBootorResetCommand;
 import com.cloud.agent.manager.Commands;
-import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
 import com.cloud.baremetal.PxeServerManager.PxeServerType;
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.configuration.dao.ConfigurationDao;
@@ -47,14 +30,8 @@
 import com.cloud.deploy.DeployDestination;
 import com.cloud.domain.DomainVO;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
-import com.cloud.exception.ConcurrentOperationException;
-import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.StorageUnavailableException;
+import com.cloud.event.UsageEventUtils;
+import com.cloud.exception.*;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
@@ -71,11 +48,7 @@
 import com.cloud.template.TemplateAdapter;
 import com.cloud.template.TemplateAdapter.TemplateAdapterType;
 import com.cloud.template.TemplateProfile;
-import com.cloud.user.Account;
-import com.cloud.user.AccountVO;
-import com.cloud.user.SSHKeyPair;
-import com.cloud.user.User;
-import com.cloud.user.UserContext;
+import com.cloud.user.*;
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.Pair;
@@ -88,19 +61,26 @@
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.fsm.StateListener;
 import com.cloud.utils.net.NetUtils;
-import com.cloud.vm.BareMetalVmService;
-import com.cloud.vm.NicProfile;
-import com.cloud.vm.NicVO;
-import com.cloud.vm.ReservationContext;
-import com.cloud.vm.UserVmManagerImpl;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.*;
 import com.cloud.vm.VirtualMachine.Event;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VirtualMachine.Type;
-import com.cloud.vm.VirtualMachineName;
-import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.VirtualMachineProfile.Param;
+import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd;
+import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
+import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
 
 @Local(value={BareMetalVmManager.class, BareMetalVmService.class})
 public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMetalVmManager, BareMetalVmService, Manager,
@@ -367,8 +347,9 @@
 			s_logger.debug("Successfully allocated DB entry for " + vm);
 		}
 		UserContext.current().setEventDetails("Vm Id: " + vm.getId());
-		 UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, cmd.getZoneId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), HypervisorType.BareMetal.toString());
-		_usageEventDao.persist(usageEvent);
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, cmd.getZoneId(), vm.getId(),
+                vm.getHostName(), offering.getId(), template.getId(), HypervisorType.BareMetal.toString(),
+                VirtualMachine.class.getName(), vm.getUuid());
 
 		_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm);
 
diff --git a/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java b/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java
index c1f0fff..7bb14c9 100755
--- a/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java
+++ b/server/src/com/cloud/baremetal/ExternalDhcpManagerImpl.java
@@ -208,7 +208,7 @@
 		if (dns == null) {
 			dns = nic.getDns2();
 		}
-		DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName(), dns, nic.getGateway());
+		DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), profile.getVirtualMachine().getHostName(), null, dns, nic.getGateway(), null);
 		String errMsg = String.format("Set dhcp entry on external DHCP %1$s failed(ip=%2$s, mac=%3$s, vmname=%4$s)",
 				h.getPrivateIpAddress(), nic.getIp4Address(), nic.getMacAddress(), profile.getVirtualMachine().getHostName());
 		//prepareBareMetalDhcpEntry(nic, dhcpCommand);
diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java
index 19e74bf..09e8ac9 100644
--- a/server/src/com/cloud/configuration/ConfigurationManager.java
+++ b/server/src/com/cloud/configuration/ConfigurationManager.java
@@ -198,7 +198,7 @@
             boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId, boolean conserveMode, Map<Service, Map<Capability, String>> serviceCapabilityMap,
             boolean specifyIpRanges);
 
-    Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException;
+    Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException;
 
     void createDefaultSystemNetworks(long zoneId) throws ConcurrentOperationException;
 
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index df6642a..c8acf3b 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -44,7 +44,6 @@
 import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
-import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd;
 import org.apache.cloudstack.api.command.admin.offering.*;
 import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd;
 import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd;
@@ -2253,7 +2252,7 @@
         txn.start();
 
         Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, podId, startIP, 
-                endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner);
+                endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, null, null, null, null);
 
         if (associateIpRangeToAccount) {
             _networkMgr.associateIpAddressListToAccount(userId, vlanOwner.getId(), zoneId, vlan.getId(), null);
@@ -2279,11 +2278,23 @@
     @DB
     public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, 
             String startIP, String endIP, String vlanGateway, String vlanNetmask,
-            String vlanId, Account vlanOwner) {
-        
-        
+            String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) {
         Network network = _networkModel.getNetwork(networkId);
         
+        boolean ipv4 = false, ipv6 = false;
+        
+        if (startIP != null) {
+        	ipv4 = true;
+        }
+        
+        if (startIPv6 != null) {
+        	ipv6 = true;
+        }
+        
+        if (!ipv4 && !ipv6) {
+            throw new InvalidParameterValueException("Please specify IPv4 or IPv6 address.");
+        }
+        
         //Validate the zone
         DataCenterVO zone = _zoneDao.findById(zoneId);
         if (zone == null) {
@@ -2348,90 +2359,133 @@
             throw new InvalidParameterValueException("Vlan owner can be defined only in the zone of type " + NetworkType.Advanced);
         }
 
-        // Make sure the gateway is valid
-        if (!NetUtils.isValidIp(vlanGateway)) {
-            throw new InvalidParameterValueException("Please specify a valid gateway");
+        if (ipv4) {
+        	// Make sure the gateway is valid
+        	if (!NetUtils.isValidIp(vlanGateway)) {
+        		throw new InvalidParameterValueException("Please specify a valid gateway");
+        	}
+
+        	// Make sure the netmask is valid
+        	if (!NetUtils.isValidIp(vlanNetmask)) {
+        		throw new InvalidParameterValueException("Please specify a valid netmask");
+        	}
+        }
+        
+        if (ipv6) {
+        	if (!NetUtils.isValidIpv6(vlanIp6Gateway)) {
+        		throw new InvalidParameterValueException("Please specify a valid IPv6 gateway");
+        	}
+        	if (!NetUtils.isValidIp6Cidr(vlanIp6Cidr)) {
+        		throw new InvalidParameterValueException("Please specify a valid IPv6 CIDR");
+        	}
         }
 
-        // Make sure the netmask is valid
-        if (!NetUtils.isValidIp(vlanNetmask)) {
-            throw new InvalidParameterValueException("Please specify a valid netmask");
+        if (ipv4) {
+        	String newVlanSubnet = NetUtils.getSubNet(vlanGateway, vlanNetmask);
+
+        	// Check if the new VLAN's subnet conflicts with the guest network in
+        	// the specified zone (guestCidr is null for basic zone)
+        	String guestNetworkCidr = zone.getGuestNetworkCidr();
+        	if (guestNetworkCidr != null) {
+        		String[] cidrPair = guestNetworkCidr.split("\\/");
+        		String guestIpNetwork = NetUtils.getIpRangeStartIpFromCidr(cidrPair[0], Long.parseLong(cidrPair[1]));
+        		long guestCidrSize = Long.parseLong(cidrPair[1]);
+        		long vlanCidrSize = NetUtils.getCidrSize(vlanNetmask);
+
+        		long cidrSizeToUse = -1;
+        		if (vlanCidrSize < guestCidrSize) {
+        			cidrSizeToUse = vlanCidrSize;
+        		} else {
+        			cidrSizeToUse = guestCidrSize;
+        		}
+
+        		String guestSubnet = NetUtils.getCidrSubNet(guestIpNetwork, cidrSizeToUse);
+
+        		if (newVlanSubnet.equals(guestSubnet)) {
+        			throw new InvalidParameterValueException("The new IP range you have specified has the same subnet as the guest network in zone: " + zone.getName()
+        					+ ". Please specify a different gateway/netmask.");
+        		}
+        	}
+
+        	// Check if there are any errors with the IP range
+        	checkPublicIpRangeErrors(zoneId, vlanId, vlanGateway, vlanNetmask, startIP, endIP);
+
+        	// Throw an exception if any of the following is true:
+        	// 1. Another VLAN in the same zone has a different tag but the same
+        	// subnet as the new VLAN. Make an exception for the
+        	// case when both vlans are Direct.
+        	// 2. Another VLAN in the same zone that has the same tag and subnet as
+        	// the new VLAN has IPs that overlap with the IPs
+        	// being added
+        	// 3. Another VLAN in the same zone that has the same tag and subnet as
+        	// the new VLAN has a different gateway than the
+        	// new VLAN
+        	// 4. If VLAN is untagged and Virtual, and there is existing UNTAGGED
+        	// vlan with different subnet
+        	List<VlanVO> vlans = _vlanDao.listByZone(zone.getId());
+        	for (VlanVO vlan : vlans) {
+        		String otherVlanGateway = vlan.getVlanGateway();
+        		// Continue if it's not IPv4 
+        		if (otherVlanGateway == null) {
+        			continue;
+        		}
+        		String otherVlanSubnet = NetUtils.getSubNet(vlan.getVlanGateway(), vlan.getVlanNetmask());
+        		String[] otherVlanIpRange = vlan.getIpRange().split("\\-");
+        		String otherVlanStartIP = otherVlanIpRange[0];
+        		String otherVlanEndIP = null;
+        		if (otherVlanIpRange.length > 1) {
+        			otherVlanEndIP = otherVlanIpRange[1];
+        		}
+
+        		if (forVirtualNetwork && !vlanId.equals(vlan.getVlanTag()) && newVlanSubnet.equals(otherVlanSubnet) && !allowIpRangeOverlap(vlan, forVirtualNetwork, networkId)) {
+        			throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " in zone " + zone.getName()
+        					+ " has the same subnet. Please specify a different gateway/netmask.");
+        		}
+
+        		boolean vlansUntaggedAndVirtual = (vlanId.equals(Vlan.UNTAGGED) && vlanId.equals(vlan.getVlanTag()) && forVirtualNetwork && vlan.getVlanType() == VlanType.VirtualNetwork);
+
+        		if (vlansUntaggedAndVirtual && !newVlanSubnet.equals(otherVlanSubnet)) {
+        			throw new InvalidParameterValueException("The Untagged ip range with different subnet already exists in zone " + zone.getId());
+        		}
+
+        		if (vlanId.equals(vlan.getVlanTag()) && newVlanSubnet.equals(otherVlanSubnet)) {
+        			if (NetUtils.ipRangesOverlap(startIP, endIP, otherVlanStartIP, otherVlanEndIP)) {
+        				throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag()
+        						+ " already has IPs that overlap with the new range. Please specify a different start IP/end IP.");
+        			}
+
+        			if (!vlanGateway.equals(otherVlanGateway)) {
+        				throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " has already been added with gateway " + otherVlanGateway
+        						+ ". Please specify a different tag.");
+        			}
+        		}
+        	}
         }
+        
+        String ipv6Range = null;
+        if (ipv6) {
+        	ipv6Range = startIPv6;
+        	if (endIPv6 != null) {
+        		ipv6Range += "-" + endIPv6;
+        	}
+        	
+        	List<VlanVO> vlans = _vlanDao.listByZone(zone.getId());
+        	for (VlanVO vlan : vlans) {
+        		if (vlan.getIp6Gateway() == null) {
+        			continue;
+        		}
+        		if (vlanId.equals(vlan.getVlanTag())) {
+        			if (NetUtils.isIp6RangeOverlap(ipv6Range, vlan.getIp6Range())) {
+        				throw new InvalidParameterValueException("The IPv6 range with tag: " + vlan.getVlanTag()
+        						+ " already has IPs that overlap with the new range. Please specify a different start IP/end IP.");
+        			}
 
-        String newVlanSubnet = NetUtils.getSubNet(vlanGateway, vlanNetmask);
-
-        // Check if the new VLAN's subnet conflicts with the guest network in
-        // the specified zone (guestCidr is null for basic zone)
-        String guestNetworkCidr = zone.getGuestNetworkCidr();
-        if (guestNetworkCidr != null) {
-            String[] cidrPair = guestNetworkCidr.split("\\/");
-            String guestIpNetwork = NetUtils.getIpRangeStartIpFromCidr(cidrPair[0], Long.parseLong(cidrPair[1]));
-            long guestCidrSize = Long.parseLong(cidrPair[1]);
-            long vlanCidrSize = NetUtils.getCidrSize(vlanNetmask);
-
-            long cidrSizeToUse = -1;
-            if (vlanCidrSize < guestCidrSize) {
-                cidrSizeToUse = vlanCidrSize;
-            } else {
-                cidrSizeToUse = guestCidrSize;
-            }
-
-            String guestSubnet = NetUtils.getCidrSubNet(guestIpNetwork, cidrSizeToUse);
-
-            if (newVlanSubnet.equals(guestSubnet)) {
-                throw new InvalidParameterValueException("The new IP range you have specified has the same subnet as the guest network in zone: " + zone.getName()
-                        + ". Please specify a different gateway/netmask.");
-            }
-        }
-
-        // Check if there are any errors with the IP range
-        checkPublicIpRangeErrors(zoneId, vlanId, vlanGateway, vlanNetmask, startIP, endIP);
-
-        // Throw an exception if any of the following is true:
-        // 1. Another VLAN in the same zone has a different tag but the same
-        // subnet as the new VLAN. Make an exception for the
-        // case when both vlans are Direct.
-        // 2. Another VLAN in the same zone that has the same tag and subnet as
-        // the new VLAN has IPs that overlap with the IPs
-        // being added
-        // 3. Another VLAN in the same zone that has the same tag and subnet as
-        // the new VLAN has a different gateway than the
-        // new VLAN
-        // 4. If VLAN is untagged and Virtual, and there is existing UNTAGGED
-        // vlan with different subnet
-        List<VlanVO> vlans = _vlanDao.listByZone(zone.getId());
-        for (VlanVO vlan : vlans) {
-            String otherVlanGateway = vlan.getVlanGateway();
-            String otherVlanSubnet = NetUtils.getSubNet(vlan.getVlanGateway(), vlan.getVlanNetmask());
-            String[] otherVlanIpRange = vlan.getIpRange().split("\\-");
-            String otherVlanStartIP = otherVlanIpRange[0];
-            String otherVlanEndIP = null;
-            if (otherVlanIpRange.length > 1) {
-                otherVlanEndIP = otherVlanIpRange[1];
-            }
-
-            if (forVirtualNetwork && !vlanId.equals(vlan.getVlanTag()) && newVlanSubnet.equals(otherVlanSubnet) && !allowIpRangeOverlap(vlan, forVirtualNetwork, networkId)) {
-                throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " in zone " + zone.getName()
-                        + " has the same subnet. Please specify a different gateway/netmask.");
-            }
-
-            boolean vlansUntaggedAndVirtual = (vlanId.equals(Vlan.UNTAGGED) && vlanId.equals(vlan.getVlanTag()) && forVirtualNetwork && vlan.getVlanType() == VlanType.VirtualNetwork);
-
-            if (vlansUntaggedAndVirtual && !newVlanSubnet.equals(otherVlanSubnet)) {
-                throw new InvalidParameterValueException("The Untagged ip range with different subnet already exists in zone " + zone.getId());
-            }
-
-            if (vlanId.equals(vlan.getVlanTag()) && newVlanSubnet.equals(otherVlanSubnet)) {
-                if (NetUtils.ipRangesOverlap(startIP, endIP, otherVlanStartIP, otherVlanEndIP)) {
-                    throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag()
-                            + " already has IPs that overlap with the new range. Please specify a different start IP/end IP.");
-                }
-
-                if (!vlanGateway.equals(otherVlanGateway)) {
-                    throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " has already been added with gateway " + otherVlanGateway
-                            + ". Please specify a different tag.");
-                }
-            }
+        			if (!vlanIp6Gateway.equals(vlan.getIp6Gateway())) {
+        				throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " has already been added with gateway " + vlan.getIp6Gateway()
+        						+ ". Please specify a different tag.");
+        			}
+        		}
+        	}
         }
 
         // Check if a guest VLAN is using the same tag
@@ -2453,21 +2507,28 @@
             }
         }
 
-        String ipRange = startIP;
-        if (endIP != null) {
-            ipRange += "-" + endIP;
+        String ipRange = null;
+        
+        if (ipv4) {
+        	ipRange = startIP;
+        	if (endIP != null) {
+        		ipRange += "-" + endIP;
+        	}
         }
-
+        
         // Everything was fine, so persist the VLAN
         Transaction txn = Transaction.currentTxn();
         txn.start();
 
-        VlanVO vlan = new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId, physicalNetworkId);
+        VlanVO vlan = new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId, physicalNetworkId, vlanIp6Gateway, vlanIp6Cidr, ipv6Range);
         s_logger.debug("Saving vlan range " + vlan);
         vlan = _vlanDao.persist(vlan);
 
-        if (!savePublicIPRange(startIP, endIP, zoneId, vlan.getId(), networkId, physicalNetworkId)) {
-            throw new CloudRuntimeException("Failed to save IP range. Please contact Cloud Support."); 
+        // IPv6 use a used ip map, is different from ipv4, no need to save public ip range
+        if (ipv4) {
+        	if (!savePublicIPRange(startIP, endIP, zoneId, vlan.getId(), networkId, physicalNetworkId)) {
+        		throw new CloudRuntimeException("Failed to save IPv4 range. Please contact Cloud Support."); 
+        	}
         }
 
         if (vlanOwner != null) {
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index 276afa3..60f60df 100755
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -96,6 +96,7 @@
 import com.cloud.maint.UpgradeManagerImpl;
 import com.cloud.maint.dao.AgentUpgradeDaoImpl;
 import com.cloud.network.ExternalLoadBalancerUsageManagerImpl;
+import com.cloud.network.Ipv6AddressManagerImpl;
 import com.cloud.network.NetworkManagerImpl;
 import com.cloud.network.NetworkModelImpl;
 import com.cloud.network.NetworkServiceImpl;
@@ -127,6 +128,7 @@
 import com.cloud.network.dao.PhysicalNetworkServiceProviderDaoImpl;
 import com.cloud.network.dao.PhysicalNetworkTrafficTypeDaoImpl;
 import com.cloud.network.dao.PortProfileDaoImpl;
+import com.cloud.network.dao.UserIpv6AddressDaoImpl;
 import com.cloud.network.dao.RemoteAccessVpnDaoImpl;
 import com.cloud.network.dao.Site2SiteCustomerGatewayDaoImpl;
 import com.cloud.network.dao.Site2SiteVpnConnectionDaoImpl;
@@ -386,6 +388,7 @@
         addDao("Site2SiteVpnGatewayDao", Site2SiteVpnGatewayDaoImpl.class);
         addDao("Site2SiteCustomerGatewayDao", Site2SiteCustomerGatewayDaoImpl.class);
         addDao("Site2SiteVpnConnnectionDao", Site2SiteVpnConnectionDaoImpl.class);
+        addDao("UserIpv6AddressDao", UserIpv6AddressDaoImpl.class);
 
         addDao("UserVmJoinDao", UserVmJoinDaoImpl.class);
         addDao("DomainRouterJoinDao", DomainRouterJoinDaoImpl.class);
@@ -471,6 +474,7 @@
         addManager("TaggedResourcesManager", TaggedResourceManagerImpl.class);
         addManager("Site2SiteVpnManager", Site2SiteVpnManagerImpl.class);
         addManager("QueryManager", QueryManagerImpl.class);
+        addManager("Ipv6AddressManager", Ipv6AddressManagerImpl.class);
     }
 
     @Override
diff --git a/server/src/com/cloud/configuration/DefaultInterceptorLibrary.java b/server/src/com/cloud/configuration/DefaultInterceptorLibrary.java
index ced2618..13a22db 100644
--- a/server/src/com/cloud/configuration/DefaultInterceptorLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultInterceptorLibrary.java
@@ -16,18 +16,18 @@
 // under the License.
 package com.cloud.configuration;
 
-import java.util.List;
-
-import com.cloud.event.ActionEventCallback;
+import com.cloud.event.ActionEventUtils;
 import com.cloud.utils.component.AnnotationInterceptor;
 import com.cloud.utils.component.InterceptorLibrary;
 import com.cloud.utils.db.DatabaseCallback;
 
+import java.util.List;
+
 public class DefaultInterceptorLibrary implements InterceptorLibrary {
 
     @Override
     public void addInterceptors(List<AnnotationInterceptor<?>> interceptors) {
         interceptors.add(new DatabaseCallback());
-        interceptors.add(new ActionEventCallback());
+        interceptors.add(new ActionEventUtils.ActionEventCallback());
     }
 }
diff --git a/server/src/com/cloud/dc/VlanVO.java b/server/src/com/cloud/dc/VlanVO.java
index fcb46c4..af6b5fc 100644
--- a/server/src/com/cloud/dc/VlanVO.java
+++ b/server/src/com/cloud/dc/VlanVO.java
@@ -48,12 +48,21 @@
 	@Column(name="vlan_netmask")
 	String vlanNetmask;
 
+	@Column(name="ip6_gateway")
+	String ip6Gateway;
+
+	@Column(name="ip6_cidr")
+	String ip6Cidr;
+
 	@Column(name="data_center_id")
 	long dataCenterId;
 
 	@Column(name="description")
 	String ipRange;
 
+	@Column(name="ip6_range")
+	String ip6Range;
+	
     @Column(name="network_id")
     Long networkId;
 
@@ -67,13 +76,16 @@
     @Column(name="uuid")
     String uuid;
 
-	public VlanVO(VlanType vlanType, String vlanTag, String vlanGateway, String vlanNetmask, long dataCenterId, String ipRange, Long networkId, Long physicalNetworkId) {
+	public VlanVO(VlanType vlanType, String vlanTag, String vlanGateway, String vlanNetmask, long dataCenterId, String ipRange, Long networkId, Long physicalNetworkId, String ip6Gateway, String ip6Cidr, String ip6Range) {
 		this.vlanType = vlanType;
 		this.vlanTag = vlanTag;
 		this.vlanGateway = vlanGateway;
 		this.vlanNetmask = vlanNetmask;
+		this.ip6Gateway = ip6Gateway;
+		this.ip6Cidr = ip6Cidr;
 		this.dataCenterId = dataCenterId;
 		this.ipRange = ipRange;
+		this.ip6Range = ip6Range;
 		this.networkId = networkId;
 		this.uuid = UUID.randomUUID().toString();
 		this.physicalNetworkId = physicalNetworkId;
@@ -149,9 +161,36 @@
     public String toString() {
         if (toString == null) {
             toString = new StringBuilder("Vlan[").append(vlanTag).append("|").append(vlanGateway).append("|").append(vlanNetmask).
-                    append("|").append(ipRange).append("|").append(networkId).append("]").toString();
+                    append("|").append(ip6Gateway).append("|").append(ip6Cidr).append("|").append(ipRange).append("|").
+                    append("|").append(ip6Range).append(networkId).append("]").toString();
         }
         return toString;
     }
+    
+    @Override
+	public String getIp6Gateway() {
+		return ip6Gateway;
+	}
 
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+    @Override
+	public String getIp6Cidr() {
+		return ip6Cidr;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
+
+	@Override
+	public String getIp6Range() {
+		return ip6Range;
+	}
+
+	public void setIp6Range(String ip6Range) {
+		this.ip6Range = ip6Range;
+	}
 }
diff --git a/server/src/com/cloud/event/ActionEventCallback.java b/server/src/com/cloud/event/ActionEventCallback.java
deleted file mode 100644
index f941400..0000000
--- a/server/src/com/cloud/event/ActionEventCallback.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-package com.cloud.event;
-
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Method;
-
-import net.sf.cglib.proxy.Callback;
-import net.sf.cglib.proxy.MethodInterceptor;
-import net.sf.cglib.proxy.MethodProxy;
-
-import com.cloud.user.UserContext;
-import com.cloud.utils.component.AnnotationInterceptor;
-
-public class ActionEventCallback implements MethodInterceptor, AnnotationInterceptor<EventVO> {
-
-    @Override
-    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
-        EventVO event = interceptStart(method);
-        boolean success = true;
-        try {
-            return methodProxy.invokeSuper(object, args);
-        } catch (Exception e){
-            success = false;
-            interceptException(method, event);
-            throw e;
-        } finally {
-            if(success){
-                interceptComplete(method, event);
-            }
-        }
-    }
-
-    @Override
-    public boolean needToIntercept(AnnotatedElement element) {
-        if (!(element instanceof Method)) {
-            return false;
-            
-        }
-        Method method = (Method)element;
-        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
-        if (actionEvent != null) {
-            return true;
-        }
-        
-        return false;
-    }
-
-    @Override
-    public EventVO interceptStart(AnnotatedElement element) {
-        EventVO event = null;
-        Method method = (Method)element;
-        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
-        if (actionEvent != null) {
-            boolean async = actionEvent.async();
-            if(async){
-                UserContext ctx = UserContext.current();
-                long userId = ctx.getCallerUserId();
-                long accountId = ctx.getAccountId();
-                long startEventId = ctx.getStartEventId();
-                String eventDescription = actionEvent.eventDescription();
-                if(ctx.getEventDetails() != null){
-                    eventDescription += ". "+ctx.getEventDetails();
-                }
-                EventUtils.saveStartedEvent(userId, accountId, actionEvent.eventType(), eventDescription, startEventId);
-            }
-        }
-        return event;
-    }
-
-    @Override
-    public void interceptComplete(AnnotatedElement element, EventVO event) {
-        Method method = (Method)element;
-        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
-        if (actionEvent != null) {
-            UserContext ctx = UserContext.current();
-            long userId = ctx.getCallerUserId();
-            long accountId = ctx.getAccountId();
-            long startEventId = ctx.getStartEventId();
-            String eventDescription = actionEvent.eventDescription();
-            if(ctx.getEventDetails() != null){
-                eventDescription += ". "+ctx.getEventDetails();
-            }            
-            if(actionEvent.create()){
-                //This start event has to be used for subsequent events of this action
-                startEventId = EventUtils.saveCreatedEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully created entity for "+eventDescription);
-                ctx.setStartEventId(startEventId);
-            } else {
-                EventUtils.saveEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully completed "+eventDescription, startEventId);
-            }
-        }
-    }
-
-    @Override
-    public void interceptException(AnnotatedElement element, EventVO event) {
-        Method method = (Method)element;
-        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
-        if (actionEvent != null) {
-            UserContext ctx = UserContext.current();
-            long userId = ctx.getCallerUserId();
-            long accountId = ctx.getAccountId();
-            long startEventId = ctx.getStartEventId();
-            String eventDescription = actionEvent.eventDescription();
-            if(ctx.getEventDetails() != null){
-                eventDescription += ". "+ctx.getEventDetails();
-            }
-            if(actionEvent.create()){
-                long eventId = EventUtils.saveCreatedEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while creating entity for "+eventDescription);
-                ctx.setStartEventId(eventId);
-            } else {
-                EventUtils.saveEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while "+eventDescription, startEventId);
-            }
-        }
-    }
-
-    @Override
-    public Callback getCallback() {
-        return this;
-    }
-    
-}
diff --git a/server/src/com/cloud/event/ActionEventUtils.java b/server/src/com/cloud/event/ActionEventUtils.java
new file mode 100755
index 0000000..744f46f
--- /dev/null
+++ b/server/src/com/cloud/event/ActionEventUtils.java
@@ -0,0 +1,288 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.event;
+
+import com.cloud.event.dao.EventDao;
+import com.cloud.server.ManagementServer;
+import com.cloud.user.Account;
+import com.cloud.user.AccountVO;
+import com.cloud.user.User;
+import com.cloud.user.UserContext;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.AnnotationInterceptor;
+import com.cloud.utils.component.ComponentLocator;
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import org.apache.cloudstack.framework.events.EventBus;
+import org.apache.cloudstack.framework.events.EventBusException;
+import org.apache.log4j.Logger;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ActionEventUtils {
+
+    private static EventDao _eventDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(EventDao.class);
+    private static AccountDao _accountDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(AccountDao.class);
+    protected static UserDao _userDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(UserDao.class);
+    private static final Logger s_logger = Logger.getLogger(ActionEventUtils.class);
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
+    public static Long onActionEvent(Long userId, Long accountId, Long domainId, String type, String description) {
+
+        publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(),
+                type, com.cloud.event.Event.State.Completed);
+
+        Event event = persistActionEvent(userId, accountId, domainId, null, type, Event.State.Completed,
+                description, null);
+
+        return event.getId();
+    }
+
+    /*
+     * Save event after scheduling an async job
+     */
+    public static Long onScheduledActionEvent(Long userId, Long accountId, String type, String description,
+                                              long startEventId) {
+
+        publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
+                com.cloud.event.Event.State.Scheduled);
+
+        Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Scheduled,
+                description, startEventId);
+
+        return event.getId();
+    }
+
+    /*
+     * Save event after starting execution of an async job
+     */
+    public static Long onStartedActionEvent(Long userId, Long accountId, String type, String description,
+                                            long startEventId) {
+
+        publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
+                com.cloud.event.Event.State.Started);
+
+        Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Started,
+                description, startEventId);
+        return event.getId();
+    }
+
+    public static Long onCompletedActionEvent(Long userId, Long accountId, String level, String type,
+                                              String description, long startEventId) {
+
+        publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
+                com.cloud.event.Event.State.Completed);
+
+        Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Completed,
+                description, startEventId);
+
+        return event.getId();
+    }
+
+    public static Long onCreatedActionEvent(Long userId, Long accountId, String level, String type, String description) {
+
+        publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
+                com.cloud.event.Event.State.Created);
+
+        Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Created, description, null);
+
+        return event.getId();
+    }
+
+    private static Event persistActionEvent(Long userId, Long accountId, Long domainId, String level, String type,
+                                           Event.State state, String description, Long startEventId) {
+        EventVO event = new EventVO();
+        event.setUserId(userId);
+        event.setAccountId(accountId);
+        event.setType(type);
+        event.setState(state);
+        event.setDescription(description);
+        if (domainId != null) {
+            event.setDomainId(domainId);
+        } else {
+            event.setDomainId(getDomainId(accountId));
+        }
+        if (level != null && !level.isEmpty()) {
+            event.setLevel(level);
+        }
+        if (startEventId != null) {
+            event.setStartId(startEventId);
+        }
+        event = _eventDao.persist(event);
+        return event;
+    }
+
+    private static void publishOnEventBus(long userId, long accountId, String eventCategory,
+                                          String eventType, Event.State state) {
+        if (_eventBus == null) {
+            return; // no provider is configured to provide events bus, so just return
+        }
+
+        org.apache.cloudstack.framework.events.Event event = new org.apache.cloudstack.framework.events.Event(
+                ManagementServer.Name,
+                eventCategory,
+                eventType,
+                EventTypes.getEntityForEvent(eventType), null);
+
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        Account account = _accountDao.findById(accountId);
+        User user = _userDao.findById(userId);
+        eventDescription.put("user", user.getUuid());
+        eventDescription.put("account", account.getUuid());
+        eventDescription.put("event", eventType);
+        eventDescription.put("status", state.toString());
+        event.setDescription(eventDescription);
+
+        try {
+            _eventBus.publish(event);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to publish action event on the the event bus.");
+        }
+    }
+
+    private static long getDomainId(long accountId){
+        AccountVO account = _accountDao.findByIdIncludingRemoved(accountId);
+        return account.getDomainId();
+    }
+
+    public static class ActionEventCallback implements MethodInterceptor, AnnotationInterceptor<EventVO> {
+
+        @Override
+        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
+            EventVO event = interceptStart(method);
+            boolean success = true;
+            try {
+                return methodProxy.invokeSuper(object, args);
+            } catch (Exception e){
+                success = false;
+                interceptException(method, event);
+                throw e;
+            } finally {
+                if(success){
+                    interceptComplete(method, event);
+                }
+            }
+        }
+
+        @Override
+        public boolean needToIntercept(AnnotatedElement element) {
+            if (!(element instanceof Method)) {
+                return false;
+
+            }
+            Method method = (Method)element;
+            ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
+            if (actionEvent != null) {
+                return true;
+            }
+
+            return false;
+        }
+
+        @Override
+        public EventVO interceptStart(AnnotatedElement element) {
+            EventVO event = null;
+            Method method = (Method)element;
+            ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
+            if (actionEvent != null) {
+                boolean async = actionEvent.async();
+                if(async){
+                    UserContext ctx = UserContext.current();
+                    long userId = ctx.getCallerUserId();
+                    long accountId = ctx.getAccountId();
+                    long startEventId = ctx.getStartEventId();
+                    String eventDescription = actionEvent.eventDescription();
+                    if(ctx.getEventDetails() != null){
+                        eventDescription += ". "+ctx.getEventDetails();
+                    }
+                    ActionEventUtils.onStartedActionEvent(userId, accountId, actionEvent.eventType(), eventDescription, startEventId);
+                }
+            }
+            return event;
+        }
+
+        @Override
+        public void interceptComplete(AnnotatedElement element, EventVO event) {
+            Method method = (Method)element;
+            ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
+            if (actionEvent != null) {
+                UserContext ctx = UserContext.current();
+                long userId = ctx.getCallerUserId();
+                long accountId = ctx.getAccountId();
+                long startEventId = ctx.getStartEventId();
+                String eventDescription = actionEvent.eventDescription();
+                if(ctx.getEventDetails() != null){
+                    eventDescription += ". "+ctx.getEventDetails();
+                }
+                if(actionEvent.create()){
+                    //This start event has to be used for subsequent events of this action
+                    startEventId = ActionEventUtils.onCreatedActionEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully created entity for " + eventDescription);
+                    ctx.setStartEventId(startEventId);
+                } else {
+                    ActionEventUtils.onCompletedActionEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully completed " + eventDescription, startEventId);
+                }
+            }
+        }
+
+        @Override
+        public void interceptException(AnnotatedElement element, EventVO event) {
+            Method method = (Method)element;
+            ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
+            if (actionEvent != null) {
+                UserContext ctx = UserContext.current();
+                long userId = ctx.getCallerUserId();
+                long accountId = ctx.getAccountId();
+                long startEventId = ctx.getStartEventId();
+                String eventDescription = actionEvent.eventDescription();
+                if(ctx.getEventDetails() != null){
+                    eventDescription += ". "+ctx.getEventDetails();
+                }
+                if(actionEvent.create()){
+                    long eventId = ActionEventUtils.onCreatedActionEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while creating entity for " + eventDescription);
+                    ctx.setStartEventId(eventId);
+                } else {
+                    ActionEventUtils.onCompletedActionEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while " + eventDescription, startEventId);
+                }
+            }
+        }
+
+        @Override
+        public Callback getCallback() {
+            return this;
+        }
+    }
+}
diff --git a/server/src/com/cloud/event/AlertGenerator.java b/server/src/com/cloud/event/AlertGenerator.java
new file mode 100644
index 0000000..4286377
--- /dev/null
+++ b/server/src/com/cloud/event/AlertGenerator.java
@@ -0,0 +1,87 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.event;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.HostPodVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.HostPodDao;
+import com.cloud.server.ManagementServer;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.ComponentLocator;
+import org.apache.cloudstack.framework.events.*;
+import org.apache.log4j.Logger;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AlertGenerator {
+
+    private static final Logger s_logger = Logger.getLogger(AlertGenerator.class);
+    private static DataCenterDao _dcDao =  ComponentLocator.getLocator(ManagementServer.Name).getDao(DataCenterDao.class);
+    private static HostPodDao _podDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(HostPodDao.class);
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
+    public static void publishAlertOnEventBus(String alertType, long dataCenterId, Long podId, String subject, String body) {
+        if (_eventBus == null) {
+            return; // no provider is configured to provider events bus, so just return
+        }
+
+        org.apache.cloudstack.framework.events.Event event =
+                new org.apache.cloudstack.framework.events.Event(ManagementServer.Name,
+                        EventCategory.ALERT_EVENT.getName(),
+                        alertType,
+                        null,
+                        null);
+
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        DataCenterVO dc = _dcDao.findById(dataCenterId);
+        HostPodVO pod = _podDao.findById(podId);
+
+        eventDescription.put("event", alertType);
+        if (dc != null) {
+            eventDescription.put("dataCenterId", dc.getUuid());
+        } else {
+            eventDescription.put("dataCenterId", null);
+        }
+        if (pod != null) {
+            eventDescription.put("podId", pod.getUuid());
+        } else {
+            eventDescription.put("podId", null);
+        }
+        event.setDescription(eventDescription);
+
+        try {
+            _eventBus.publish(event);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to publish alert on the the event bus.");
+        }
+    }
+}
diff --git a/server/src/com/cloud/event/EventUtils.java b/server/src/com/cloud/event/EventUtils.java
deleted file mode 100755
index 3672ee7..0000000
--- a/server/src/com/cloud/event/EventUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-package com.cloud.event;
-
-import com.cloud.event.dao.EventDao;
-import com.cloud.server.ManagementServer;
-import com.cloud.user.AccountVO;
-import com.cloud.user.dao.AccountDao;
-import com.cloud.utils.component.ComponentLocator;
-
-public class EventUtils {
-	private static EventDao _eventDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(EventDao.class);
-	private static AccountDao _accountDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(AccountDao.class);
-
-    public static Long saveEvent(Long userId, Long accountId, Long domainId, String type, String description) {
-        EventVO event = new EventVO();
-        event.setUserId(userId);
-        event.setAccountId(accountId);
-        event.setDomainId(domainId);
-        event.setType(type);
-        event.setDescription(description);
-        event = _eventDao.persist(event);
-        return event.getId();
-    }
-    
-    /*
-     * Save event after scheduling an async job
-     */
-    public static Long saveScheduledEvent(Long userId, Long accountId, String type, String description, long startEventId) {
-        EventVO event = new EventVO();
-        event.setUserId(userId);
-        event.setAccountId(accountId);
-        event.setDomainId(getDomainId(accountId));
-        event.setType(type);
-        event.setStartId(startEventId);
-        event.setState(Event.State.Scheduled);
-        event.setDescription("Scheduled async job for "+description);
-        event = _eventDao.persist(event);
-        return event.getId();
-    }
-    
-    /*
-     * Save event after starting execution of an async job
-     */
-    public static Long saveStartedEvent(Long userId, Long accountId, String type, String description, long startEventId) {
-        EventVO event = new EventVO();
-        event.setUserId(userId);
-        event.setAccountId(accountId);
-        event.setDomainId(getDomainId(accountId));
-        event.setType(type);
-        event.setState(Event.State.Started);
-        event.setDescription("Starting job for "+description);
-        event.setStartId(startEventId);
-        event = _eventDao.persist(event);
-    	return event.getId();
-    }    
-
-    public static Long saveEvent(Long userId, Long accountId, String level, String type, String description, long startEventId) {
-        EventVO event = new EventVO();
-        event.setUserId(userId);
-        event.setAccountId(accountId);
-        event.setDomainId(getDomainId(accountId));
-        event.setType(type);
-        event.setDescription(description);
-        event.setLevel(level);
-        event.setStartId(startEventId);
-        event = _eventDao.persist(event);
-        return (event != null ? event.getId() : null);
-    }
-    
-    public static Long saveCreatedEvent(Long userId, Long accountId, String level, String type, String description) {
-        EventVO event = new EventVO();
-        event.setUserId(userId);
-        event.setAccountId(accountId);
-        event.setDomainId(getDomainId(accountId));
-        event.setType(type);
-        event.setLevel(level);
-        event.setState(Event.State.Created);
-        event.setDescription(description);
-        event = _eventDao.persist(event);
-        return event.getId();
-    }
-    
-    private static long getDomainId(long accountId){
-    	AccountVO account = _accountDao.findByIdIncludingRemoved(accountId);
-    	return account.getDomainId();
-    }
-}
diff --git a/server/src/com/cloud/event/UsageEventUtils.java b/server/src/com/cloud/event/UsageEventUtils.java
new file mode 100644
index 0000000..904525e
--- /dev/null
+++ b/server/src/com/cloud/event/UsageEventUtils.java
@@ -0,0 +1,119 @@
+package com.cloud.event;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.event.dao.UsageEventDao;
+import com.cloud.server.ManagementServer;
+import com.cloud.user.Account;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.ComponentLocator;
+import org.apache.cloudstack.framework.events.EventBus;
+import org.apache.cloudstack.framework.events.Event;
+import org.apache.cloudstack.framework.events.EventBusException;
+import org.apache.log4j.Logger;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class UsageEventUtils {
+
+    private static UsageEventDao _usageEventDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(UsageEventDao.class);
+    private static AccountDao _accountDao = ComponentLocator.getLocator(ManagementServer.Name).getDao(AccountDao.class);
+    private static DataCenterDao _dcDao =  ComponentLocator.getLocator(ManagementServer.Name).getDao(DataCenterDao.class);
+    private static final Logger s_logger = Logger.getLogger(UsageEventUtils.class);
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
+    public static void publishUsageEvent(String usageType, long accountId, long zoneId,
+                                         long resourceId, String resourceName,
+                                         Long offeringId, Long templateId, Long size,
+                                         String entityType, String entityUUID) {
+        saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size);
+        publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID);
+    }
+
+    public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId,
+                                         String resourceName, String entityType, String entityUUID) {
+        saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName);
+        publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID);
+    }
+
+    public static void publishUsageEvent(String usageType, long accountId, long zoneId,
+                                         long ipAddressId, String ipAddress, boolean isSourceNat,
+                                         String guestType, boolean isSystem, String entityType, String entityUUID) {
+        saveUsageEvent(usageType, accountId, zoneId, ipAddressId, ipAddress, isSourceNat, guestType, isSystem);
+        publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID);
+    }
+
+    public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId,
+                                         String resourceName, Long offeringId, Long templateId, String resourceType,
+                                         String entityType, String entityUUID) {
+        saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, resourceType);
+        publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID);
+    }
+
+    public static void publishUsageEvent(String usageType, long accountId,long zoneId, long vmId,
+                                         long securityGroupId, String entityType, String entityUUID) {
+        saveUsageEvent(usageType, accountId, zoneId, vmId, securityGroupId);
+        publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID);
+    }
+
+    public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size) {
+        _usageEventDao.persist( new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size));
+    }
+
+    public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName) {
+        _usageEventDao.persist( new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName));
+    }
+
+    public static void saveUsageEvent(String usageType, long accountId, long zoneId, long ipAddressId, String ipAddress, boolean isSourceNat, String guestType, boolean isSystem) {
+        _usageEventDao.persist( new UsageEventVO(usageType, accountId, zoneId, ipAddressId, ipAddress, isSourceNat, guestType, isSystem));
+    }
+
+    public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, String resourceType) {
+        _usageEventDao.persist( new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, resourceType));
+    }
+
+    public static void saveUsageEvent(String usageType, long accountId,long zoneId, long vmId, long securityGroupId) {
+        _usageEventDao.persist( new UsageEventVO( usageType, accountId, zoneId, vmId, securityGroupId));
+    }
+
+    private static void publishUsageEvent(String usageEventType, Long accountId, Long zoneId, String resourceType, String resourceUUID) {
+
+        if (_eventBus == null) {
+            return; // no provider is configured to provider events bus, so just return
+        }
+
+        Account account = _accountDao.findById(accountId);
+        DataCenterVO dc = _dcDao.findById(zoneId);
+
+        Event event = new Event(ManagementServer.Name, EventCategory.USAGE_EVENT.getName(), usageEventType,
+                resourceType, resourceUUID);
+
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        eventDescription.put("account", account.getUuid());
+        eventDescription.put("zone", dc.getUuid());
+        eventDescription.put("event", usageEventType);
+        eventDescription.put("resource", resourceType);
+        eventDescription.put("id", resourceUUID);
+        event.setDescription(eventDescription);
+
+        try {
+            _eventBus.publish(event);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to publish usage event on the the event bus.");
+        }
+    }
+}
diff --git a/server/src/com/cloud/network/Ipv6AddressManager.java b/server/src/com/cloud/network/Ipv6AddressManager.java
new file mode 100644
index 0000000..2ab7e5a
--- /dev/null
+++ b/server/src/com/cloud/network/Ipv6AddressManager.java
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.network;
+
+import com.cloud.exception.InsufficientAddressCapacityException;
+import com.cloud.user.Account;
+import com.cloud.utils.component.Manager;
+
+public interface Ipv6AddressManager extends Manager {
+    public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException;
+
+	public void revokeDirectIpv6Address(long networkId, String ip6Address);
+}
diff --git a/server/src/com/cloud/network/Ipv6AddressManagerImpl.java b/server/src/com/cloud/network/Ipv6AddressManagerImpl.java
new file mode 100644
index 0000000..6666596
--- /dev/null
+++ b/server/src/com/cloud/network/Ipv6AddressManagerImpl.java
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.network;
+
+import java.util.Map;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.Vlan;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.VlanDao;
+import com.cloud.exception.InsufficientAddressCapacityException;
+import com.cloud.network.dao.UserIpv6AddressDao;
+import com.cloud.user.Account;
+import com.cloud.utils.component.Inject;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
+
+@Local(value = { Ipv6AddressManager.class } )
+public class Ipv6AddressManagerImpl implements Ipv6AddressManager {
+    public static final Logger s_logger = Logger.getLogger(Ipv6AddressManagerImpl.class.getName());
+
+	String _name = null;
+			
+    @Inject
+    DataCenterDao _dcDao;
+    @Inject
+    VlanDao _vlanDao;
+    @Inject
+    NetworkModel _networkModel;
+    @Inject
+    UserIpv6AddressDao _ipv6Dao;
+    
+	@Override
+	public boolean configure(String name, Map<String, Object> params)
+			throws ConfigurationException {
+		_name = name;
+		return true;
+	}
+
+	@Override
+	public boolean start() {
+		return true;
+	}
+
+	@Override
+	public boolean stop() {
+		return true;
+	}
+
+	@Override
+	public String getName() {
+		return _name;
+	}
+
+	@Override
+	public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6)
+			throws InsufficientAddressCapacityException {
+    	Vlan vlan = _networkModel.getVlanForNetwork(networkId);
+    	if (vlan == null) {
+    		s_logger.debug("Cannot find related vlan or too many vlan attached to network " + networkId);
+    		return null;
+    	}
+    	String ip = null;
+    	if (requestedIp6 == null) {
+    		int count = 0;
+    		while (ip == null || count >= 10) {
+    			ip = NetUtils.getIp6FromRange(vlan.getIp6Range());
+    			//Check for duplicate IP
+    			if (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) == null) {
+    				break;
+    			} else {
+    				ip = null;
+    			}
+    			count ++;
+    		}
+    		if (ip == null) {
+    			throw new CloudRuntimeException("Fail to get unique ipv6 address after 10 times trying!");
+    		}
+    	} else {
+    		if (!NetUtils.isIp6InRange(requestedIp6, vlan.getIp6Range())) {
+    			throw new CloudRuntimeException("Requested IPv6 is not in the predefined range!");
+    		}
+    		ip = requestedIp6;
+    		if (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) != null) {
+    			throw new CloudRuntimeException("The requested IP is already taken!");
+    		}
+    	}
+    	DataCenterVO dc = _dcDao.findById(dcId);
+        Long mac = dc.getMacAddress();
+        Long nextMac = mac + 1;
+        dc.setMacAddress(nextMac);
+        _dcDao.update(dc.getId(), dc);
+        
+    	String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(mac));
+    	UserIpv6AddressVO ipVO = new UserIpv6AddressVO(ip, dcId, macAddress, vlan.getId());
+    	ipVO.setPhysicalNetworkId(vlan.getPhysicalNetworkId());
+    	ipVO.setSourceNetworkId(vlan.getNetworkId());
+    	ipVO.setState(UserIpv6Address.State.Allocated);
+    	ipVO.setDomainId(owner.getDomainId());
+    	ipVO.setAccountId(owner.getAccountId());
+    	_ipv6Dao.persist(ipVO);
+    	return ipVO;
+	}
+
+	@Override
+	public void revokeDirectIpv6Address(long networkId, String ip6Address) {
+		UserIpv6AddressVO ip = _ipv6Dao.findByNetworkIdAndIp(networkId, ip6Address);
+		if (ip != null) {
+			_ipv6Dao.remove(ip.getId());
+		}
+	}
+}
diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java
index ffe7561..ec9e01d 100755
--- a/server/src/com/cloud/network/NetworkManager.java
+++ b/server/src/com/cloud/network/NetworkManager.java
@@ -123,7 +123,7 @@
 
     Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr,
             String vlanId, String networkDomain, Account owner, Long domainId, PhysicalNetwork physicalNetwork,
-            long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId) 
+            long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr) 
                     throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
 
     /**
@@ -169,7 +169,7 @@
 
     void allocateDirectIp(NicProfile nic, DataCenter dc,
             VirtualMachineProfile<? extends VirtualMachine> vm,
-            Network network, String requestedIp)
+            Network network, String requestedIpv4, String requestedIpv6)
             throws InsufficientVirtualNetworkCapcityException,
             InsufficientAddressCapacityException;
 
diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java
index 0a4851f..218583b 100755
--- a/server/src/com/cloud/network/NetworkManagerImpl.java
+++ b/server/src/com/cloud/network/NetworkManagerImpl.java
@@ -16,39 +16,9 @@
 // under the License.
 package com.cloud.network;
 
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.acl.ControlledEntity.ACLType;
-import org.apache.cloudstack.acl.SecurityChecker.AccessType;
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.Listener;
-import com.cloud.agent.api.AgentControlAnswer;
-import com.cloud.agent.api.AgentControlCommand;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.CheckNetworkAnswer;
-import com.cloud.agent.api.CheckNetworkCommand;
-import com.cloud.agent.api.Command;
-import com.cloud.agent.api.StartupCommand;
-import com.cloud.agent.api.StartupRoutingCommand;
+import com.cloud.agent.api.*;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.alert.AlertManager;
 import com.cloud.api.ApiDBUtils;
@@ -56,15 +26,9 @@
 import com.cloud.configuration.ConfigurationManager;
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.configuration.dao.ConfigurationDao;
-import com.cloud.dc.AccountVlanMapVO;
-import com.cloud.dc.DataCenter;
+import com.cloud.dc.*;
 import com.cloud.dc.DataCenter.NetworkType;
-import com.cloud.dc.DataCenterVO;
-import com.cloud.dc.Pod;
-import com.cloud.dc.PodVlanMapVO;
-import com.cloud.dc.Vlan;
 import com.cloud.dc.Vlan.VlanType;
-import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.AccountVlanMapDao;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.dc.dao.PodVlanMapDao;
@@ -75,63 +39,30 @@
 import com.cloud.domain.Domain;
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.UsageEventDao;
-import com.cloud.exception.AccountLimitException;
-import com.cloud.exception.ConcurrentOperationException;
-import com.cloud.exception.ConnectionException;
-import com.cloud.exception.InsufficientAddressCapacityException;
-import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.InsufficientVirtualNetworkCapcityException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.UnsupportedServiceException;
+import com.cloud.exception.*;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.IpAddress.State;
-import com.cloud.network.Network.Capability;
-import com.cloud.network.Network.GuestType;
-import com.cloud.network.Network.Provider;
-import com.cloud.network.Network.Service;
+import com.cloud.network.Network.*;
 import com.cloud.network.Networks.AddressFormat;
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.network.Networks.IsolationType;
 import com.cloud.network.Networks.TrafficType;
 import com.cloud.network.addr.PublicIp;
-import com.cloud.network.dao.FirewallRulesDao;
-import com.cloud.network.dao.IPAddressDao;
-import com.cloud.network.dao.LoadBalancerDao;
-import com.cloud.network.dao.NetworkDao;
-import com.cloud.network.dao.NetworkServiceMapDao;
-import com.cloud.network.dao.PhysicalNetworkDao;
-import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
-import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
-import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
-import com.cloud.network.element.DhcpServiceProvider;
-import com.cloud.network.element.IpDeployer;
-import com.cloud.network.element.LoadBalancingServiceProvider;
-import com.cloud.network.element.NetworkElement;
-import com.cloud.network.element.StaticNatServiceProvider;
-import com.cloud.network.element.UserDataServiceProvider;
+import com.cloud.network.dao.*;
+import com.cloud.network.element.*;
 import com.cloud.network.guru.NetworkGuru;
 import com.cloud.network.lb.LoadBalancingRule;
 import com.cloud.network.lb.LoadBalancingRule.LbDestination;
 import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy;
 import com.cloud.network.lb.LoadBalancingRulesManager;
-import com.cloud.network.rules.FirewallManager;
-import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.rules.*;
 import com.cloud.network.rules.FirewallRule.Purpose;
-import com.cloud.network.rules.FirewallRuleVO;
-import com.cloud.network.rules.PortForwardingRuleVO;
-import com.cloud.network.rules.RulesManager;
-import com.cloud.network.rules.StaticNat;
-import com.cloud.network.rules.StaticNatRule;
-import com.cloud.network.rules.StaticNatRuleImpl;
 import com.cloud.network.rules.dao.PortForwardingRulesDao;
 import com.cloud.network.vpc.NetworkACLManager;
 import com.cloud.network.vpc.VpcManager;
@@ -144,11 +75,7 @@
 import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 import com.cloud.org.Grouping;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.ResourceLimitService;
-import com.cloud.user.User;
-import com.cloud.user.UserContext;
+import com.cloud.user.*;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.Pair;
@@ -156,30 +83,30 @@
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
 import com.cloud.utils.concurrency.NamedThreadFactory;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.JoinBuilder.JoinType;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.fsm.NoTransitionException;
+import com.cloud.utils.fsm.StateMachine2;
 import com.cloud.utils.net.Ip;
 import com.cloud.utils.net.NetUtils;
-import com.cloud.vm.Nic;
-import com.cloud.vm.NicProfile;
-import com.cloud.vm.NicVO;
-import com.cloud.vm.ReservationContext;
-import com.cloud.vm.ReservationContextImpl;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.*;
 import com.cloud.vm.VirtualMachine.Type;
-import com.cloud.vm.VirtualMachineProfile;
-import com.cloud.vm.VirtualMachineProfileImpl;
 import com.cloud.vm.dao.NicDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.acl.ControlledEntity.ACLType;
+import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.net.URI;
+import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 /**
  * NetworkManagerImpl implements NetworkManager.
@@ -222,8 +149,6 @@
     @Inject
     LoadBalancingRulesManager _lbMgr;
     @Inject
-    UsageEventDao _usageEventDao;
-    @Inject
     RemoteAccessVpnService _vpnMgr;
     @Inject
     PodVlanMapDao _podVlanMapDao;
@@ -272,7 +197,17 @@
     @Inject
     NetworkACLManager _networkACLMgr;
     @Inject
+    UsageEventDao _usageEventDao;
+    @Inject
     NetworkModel _networkModel;
+    @Inject
+    UserIpv6AddressDao _ipv6Dao;
+    @Inject
+    Ipv6AddressManager _ipv6Mgr;
+
+    protected StateMachine2<Network.State, Network.Event, Network> _stateMachine;
+    private final HashMap<String, NetworkOfferingVO> _systemNetworks = new HashMap<String, NetworkOfferingVO>(5);
+    private static Long _privateOfferingId = null;
 
     ScheduledExecutorService _executor;
 
@@ -405,11 +340,9 @@
             VlanVO vlan = _vlanDao.findById(addr.getVlanId());
 
             String guestType = vlan.getVlanType().toString();
-
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_IP_ASSIGN, owner.getId(),
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, owner.getId(),
                     addr.getDataCenterId(), addr.getId(), addr.getAddress().toString(), addr.isSourceNat(), guestType,
-                    addr.getSystem());
-            _usageEventDao.persist(usageEvent);
+                    addr.getSystem(), addr.getClass().getName(), addr.getUuid());
             // don't increment resource count for direct ip addresses
             if (addr.getAssociatedWithNetworkId() != null) {
                 _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.public_ip);
@@ -1046,6 +979,8 @@
 
         _agentMgr.registerForHostEvents(this, true, false, true);
 
+        Network.State.getStateMachine().registerListener(new NetworkStateListener(_usageEventDao, _networksDao));
+
         s_logger.info("Network Manager is configured.");
 
         return true;
@@ -1069,6 +1004,7 @@
     }
 
     protected NetworkManagerImpl() {
+        setStateMachine();
     }
 
     @Override
@@ -1312,10 +1248,8 @@
 
         vo.setDefaultNic(profile.isDefaultNic());
 
-        if (profile.getIp4Address() != null) {
-            vo.setIp4Address(profile.getIp4Address());
-            vo.setAddressFormat(AddressFormat.Ip4);
-        }
+        vo.setIp4Address(profile.getIp4Address());
+        vo.setAddressFormat(profile.getFormat());
 
         if (profile.getMacAddress() != null) {
             vo.setMacAddress(profile.getMacAddress());
@@ -1334,6 +1268,11 @@
         }
 
         vo.setState(Nic.State.Allocated);
+        
+        vo.setIp6Address(profile.getIp6Address());
+        vo.setIp6Gateway(profile.getIp6Gateway());
+        vo.setIp6Cidr(profile.getIp6Cidr());
+        
         return deviceId;
     }
 
@@ -1425,9 +1364,7 @@
             NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 
             network.setReservationId(context.getReservationId());
-            network.setState(Network.State.Implementing);
-
-            _networksDao.update(networkId, network);
+            stateTransitTo(network, Event.ImplementNetwork);
 
             Network result = guru.implement(network, offering, dest, context);
             network.setCidr(result.getCidr());
@@ -1440,16 +1377,23 @@
             // implement network elements and re-apply all the network rules
             implementNetworkElementsAndResources(dest, context, network, offering);
 
-            network.setState(Network.State.Implemented);
+            stateTransitTo(network,Event.OperationSucceeded);
+
             network.setRestartRequired(false);
             _networksDao.update(network.getId(), network);
             implemented.set(guru, network);
             return implemented;
+        } catch (NoTransitionException e) {
+            s_logger.error(e.getMessage());
+            return null;
         } finally {
             if (implemented.first() == null) {
                 s_logger.debug("Cleaning up because we're unable to implement the network " + network);
-                network.setState(Network.State.Shutdown);
-                _networksDao.update(networkId, network);
+                try {
+                    stateTransitTo(network,Event.OperationFailed);
+                } catch (NoTransitionException e) {
+                    s_logger.error(e.getMessage());
+                }
 
                 shutdownNetwork(networkId, context, false);
             }
@@ -1792,7 +1736,7 @@
     @DB
     public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, 
             String cidr, String vlanId, String networkDomain, Account owner, Long domainId,
-            PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId)
+            PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr)
                     throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
 
         NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
@@ -1825,9 +1769,18 @@
             throw ex;
         }
 
+        boolean ipv6 = false;
+        
+        if (ip6Gateway != null && ip6Cidr != null) {
+        	ipv6 = true;
+        }
         // Validate zone
         DataCenterVO zone = _dcDao.findById(zoneId);
         if (zone.getNetworkType() == NetworkType.Basic) {
+        	if (ipv6) {
+                throw new InvalidParameterValueException("IPv6 is not supported in Basic zone");
+        	}
+        	
             // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true
             if (aclType == null || aclType != ACLType.Domain) {
                 throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone");
@@ -1870,6 +1823,9 @@
 
         } else if (zone.getNetworkType() == NetworkType.Advanced) {
             if (zone.isSecurityGroupEnabled()) {
+            	if (ipv6) {
+            		throw new InvalidParameterValueException("IPv6 is not supported with security group!");
+            	}
                 // Only Account specific Isolated network with sourceNat service disabled are allowed in security group
                 // enabled zone
                 boolean allowCreation = (ntwkOff.getGuestType() == GuestType.Isolated 
@@ -1948,14 +1904,14 @@
                 }
             }
         }
-
+        
         // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x
         // limitation, remove after we introduce support for multiple ip ranges
         // with different Cidrs for the same Shared network
         boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getTrafficType() == TrafficType.Guest
                 && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated 
                 && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)));
-        if (cidr == null && cidrRequired) {
+        if (cidr == null && ip6Cidr == null  && cidrRequired) {
             throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of" +
                     " type " + Network.GuestType.Shared + " and network of type " + GuestType.Isolated + " with service "
                     + Service.SourceNat.getName() + " disabled");
@@ -1966,11 +1922,11 @@
             throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic);
         }
 
-        // Check if cidr is RFC1918 compliant if the network is Guest Isolated
+        // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4
         if (cidr != null && ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) {
-            if (!NetUtils.validateGuestCidr(cidr)) {
-                throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC1918 compliant");
-            }
+        	if (!NetUtils.validateGuestCidr(cidr)) {
+        		throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC1918 compliant");
+        	}
         }
 
         Transaction txn = Transaction.currentTxn();
@@ -1997,7 +1953,21 @@
                 }
             }
         }
-
+        
+        if (ip6Cidr != null && ip6Gateway != null) {
+            userNetwork.setIp6Cidr(ip6Cidr);
+            userNetwork.setIp6Gateway(ip6Gateway);
+            if (vlanId != null) {
+                userNetwork.setBroadcastUri(URI.create("vlan://" + vlanId));
+                userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan);
+                if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) {
+                    userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan);
+                } else {
+                    userNetwork.setBroadcastDomainType(BroadcastDomainType.Native);
+                }
+            }
+        }
+        
         List<NetworkVO> networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId,
                 aclType, subdomainAccess, vpcId);
 
@@ -2045,9 +2015,12 @@
             s_logger.debug("Network is not implemented: " + network);
             return false;
         }
-
-        network.setState(Network.State.Shutdown);
-        _networksDao.update(network.getId(), network);
+        try {
+            stateTransitTo(network, Event.DestroyNetwork);
+        } catch (NoTransitionException e) {
+            network.setState(Network.State.Shutdown);
+            _networksDao.update(network.getId(), network);
+        }
 
         boolean success = shutdownNetworkElementsAndResources(context, cleanupElements, network);
 
@@ -2062,15 +2035,22 @@
             guru.shutdown(profile, _networkOfferingDao.findById(network.getNetworkOfferingId()));
 
             applyProfileToNetwork(network, profile);
-
-            network.setState(Network.State.Allocated);
-            network.setRestartRequired(false);
+            try {
+                stateTransitTo(network, Event.OperationSucceeded);
+            } catch (NoTransitionException e) {
+                network.setState(Network.State.Allocated);
+                network.setRestartRequired(false);
+            }
             _networksDao.update(network.getId(), network);
             _networksDao.clearCheckForGc(networkId);
             result = true;
         } else {
-            network.setState(Network.State.Implemented);
-            _networksDao.update(network.getId(), network);
+            try {
+                stateTransitTo(network, Event.OperationFailed);
+            } catch (NoTransitionException e) {
+                network.setState(Network.State.Implemented);
+                _networksDao.update(network.getId(), network);
+            }
             result = false;
         }
         txn.commit();
@@ -2230,8 +2210,11 @@
                 s_logger.warn("Failed to delete network " + network + "; was unable to cleanup corresponding ip ranges");
             } else {
                 // commit transaction only when ips and vlans for the network are released successfully
-                network.setState(Network.State.Destroy);
-                _networksDao.update(network.getId(), network);
+                try {
+                    stateTransitTo(network, Event.DestroyNetwork);
+                 } catch (NoTransitionException e) {
+                     s_logger.debug(e.getMessage());
+                 }
                 _networksDao.remove(network.getId());
 
                 NetworkOffering ntwkOff = _configMgr.getNetworkOffering(network.getNetworkOfferingId());
@@ -2570,7 +2553,7 @@
                 guestNetwork = createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network"
                         , owner.getAccountName() + "-network", null, null, null, null, owner, null, physicalNetwork,
                         zoneId, ACLType.Account,
-                        null, null);
+                        null, null, null, null);
                 if (guestNetwork == null) {
                     s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId);
                     throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT " +
@@ -2737,10 +2720,9 @@
 
                 String guestType = vlan.getVlanType().toString();
 
-                UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_IP_RELEASE,
+                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_RELEASE,
                         ip.getAllocatedToAccountId(), ip.getDataCenterId(), addrId, ip.getAddress().addr(),
-                        ip.isSourceNat(), guestType, ip.getSystem());
-                _usageEventDao.persist(usageEvent);
+                        ip.isSourceNat(), guestType, ip.getSystem(), ip.getClass().getName(), ip.getUuid());
             }
 
             ip = _ipAddressDao.markAsUnavailable(addrId);
@@ -3346,27 +3328,56 @@
         return success;
     }
 
-    public void allocateDirectIp(NicProfile nic, DataCenter dc, VirtualMachineProfile<? extends VirtualMachine> vm, Network network, String requestedIp) throws InsufficientVirtualNetworkCapcityException,
+    public void allocateDirectIp(NicProfile nic, DataCenter dc, VirtualMachineProfile<? extends VirtualMachine> vm, Network network,
+    							 String requestedIpv4, String requestedIpv6) throws InsufficientVirtualNetworkCapcityException,
             InsufficientAddressCapacityException {
-        if (nic.getIp4Address() == null) {
-            PublicIp ip = assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.DirectAttached, network.getId(), requestedIp, false);
-            nic.setIp4Address(ip.getAddress().toString());
-            nic.setGateway(ip.getGateway());
-            nic.setNetmask(ip.getNetmask());
-            nic.setIsolationUri(IsolationType.Vlan.toUri(ip.getVlanTag()));
-            nic.setBroadcastType(BroadcastDomainType.Vlan);
-            nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(ip.getVlanTag()));
-            nic.setFormat(AddressFormat.Ip4);
-            nic.setReservationId(String.valueOf(ip.getVlanTag()));
-            nic.setMacAddress(ip.getMacAddress());
-        }
+    	boolean ipv4 = false, ipv6 = false;
+    	if (network.getGateway() != null) {
+    		if (nic.getIp4Address() == null) {
+    			ipv4 = true;
+    			PublicIp ip = assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.DirectAttached, network.getId(), requestedIpv4, false);
+    			nic.setIp4Address(ip.getAddress().toString());
+    			nic.setGateway(ip.getGateway());
+    			nic.setNetmask(ip.getNetmask());
+    			nic.setIsolationUri(IsolationType.Vlan.toUri(ip.getVlanTag()));
+    			nic.setBroadcastType(BroadcastDomainType.Vlan);
+    			nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(ip.getVlanTag()));
+    			nic.setFormat(AddressFormat.Ip4);
+    			nic.setReservationId(String.valueOf(ip.getVlanTag()));
+    			nic.setMacAddress(ip.getMacAddress());
+    		}
+    	}
+    	
+    	if (network.getIp6Gateway() != null) {
+    		if (nic.getIp6Address() == null) {
+    			ipv6 = true;
+    			UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6);
+    			Vlan vlan = _networkModel.getVlanForNetwork(network.getId());
+    			if (vlan == null) {
+    				s_logger.debug("Cannot find related vlan or too many vlan attached to network " + network.getId());
+    				return;
+    			}
+    			nic.setIp6Address(ip.getAddress().toString());
+    			nic.setIp6Gateway(vlan.getIp6Gateway());
+    			nic.setIp6Cidr(vlan.getIp6Cidr());
+    			if (ipv4) {
+    				nic.setFormat(AddressFormat.DualStack);
+    			} else {
+    				nic.setIsolationUri(IsolationType.Vlan.toUri(vlan.getVlanTag()));
+    				nic.setBroadcastType(BroadcastDomainType.Vlan);
+    				nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan.getVlanTag()));
+    				nic.setFormat(AddressFormat.Ip6);
+    				nic.setReservationId(String.valueOf(vlan.getVlanTag()));
+    				nic.setMacAddress(ip.getMacAddress());
+    			}
+    		}
+    	}
 
         nic.setDns1(dc.getDns1());
         nic.setDns2(dc.getDns2());
     }
 
-    
-    @Override
+	@Override
     public boolean setupDns(Network network, Provider provider) {
         boolean dnsProvided = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, provider );
         boolean dhcpProvided =_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, 
@@ -3458,6 +3469,15 @@
         return _networkLockTimeout;
     }
 
+
+    protected boolean stateTransitTo(NetworkVO network, Network.Event e) throws NoTransitionException {
+        return _stateMachine.transitTo(network, e, null, _networksDao);
+    }
+
+    private void setStateMachine() {
+        _stateMachine = Network.State.getStateMachine();
+    }
+
     private Map<Service, Set<Provider>> getServiceProvidersMap(long networkId) {
         Map<Service, Set<Provider>> map = new HashMap<Service, Set<Provider>>();
         List<NetworkServiceMapVO> nsms = _ntwkSrvcDao.getServicesInNetwork(networkId);
@@ -3471,7 +3491,7 @@
         }
         return map;
     }
-    
+
     @Override
     public List<Provider> getProvidersForServiceInNetwork(Network network, Service service) {
         Map<Service, Set<Provider>> service2ProviderMap = getServiceProvidersMap(network.getId());
diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java
index ce53d6b..a74b589 100644
--- a/server/src/com/cloud/network/NetworkModelImpl.java
+++ b/server/src/com/cloud/network/NetworkModelImpl.java
@@ -36,6 +36,7 @@
 import com.cloud.configuration.ConfigurationManager;
 import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.dc.PodVlanMapVO;
+import com.cloud.dc.Vlan;
 import com.cloud.dc.Vlan.VlanType;
 import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.DataCenterDao;
@@ -65,6 +66,7 @@
 import com.cloud.network.dao.PhysicalNetworkServiceProviderVO;
 import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
 import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
+import com.cloud.network.dao.UserIpv6AddressDao;
 import com.cloud.network.element.NetworkElement;
 import com.cloud.network.element.UserDataServiceProvider;
 import com.cloud.network.rules.FirewallRule.Purpose;
@@ -157,7 +159,8 @@
     NetworkServiceMapDao _ntwkSrvcDao;
     @Inject
     PrivateIpDao _privateIpDao;
-    
+    @Inject
+    UserIpv6AddressDao _ipv6Dao;
 
 
     private final HashMap<String, NetworkOfferingVO> _systemNetworks = new HashMap<String, NetworkOfferingVO>(5);
@@ -510,7 +513,15 @@
         }
         boolean hasFreeIps = true;
         if (network.getGuestType() == GuestType.Shared) {
-            hasFreeIps = _ipAddressDao.countFreeIPsInNetwork(network.getId()) > 0;
+        	if (network.getGateway() != null) {
+        		hasFreeIps = _ipAddressDao.countFreeIPsInNetwork(network.getId()) > 0;
+        	}
+        	if (!hasFreeIps) {
+        		return false;
+        	}
+        	if (network.getIp6Gateway() != null) {
+        		hasFreeIps = isIP6AddressAvailable(network);
+        	}
         } else {
             hasFreeIps = (getAvailableIps(network, null)).size() > 0;
         }
@@ -518,9 +529,27 @@
         return hasFreeIps;
     }
 
-   
-
     @Override
+    public Vlan getVlanForNetwork(long networkId) {
+    	List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
+    	if (vlans == null || vlans.size() > 1) {
+    		s_logger.debug("Cannot find related vlan or too many vlan attached to network " + networkId);
+    		return null;
+    	}
+    	return vlans.get(0);
+    }
+
+    private boolean isIP6AddressAvailable(Network network) {
+    	if (network.getIp6Gateway() == null) {
+    		return false;
+    	}
+    	Vlan vlan = getVlanForNetwork(network.getId());
+    	long existedCount = _ipv6Dao.countExistedIpsInNetwork(network.getId());
+    	long rangeCount = NetUtils.countIp6InRange(vlan.getIp6Range());
+		return (existedCount < rangeCount);
+	}
+
+	@Override
     public Map<Service, Map<Capability, String>> getNetworkCapabilities(long networkId) {
     
         Map<Service, Map<Capability, String>> networkCapabilities = new HashMap<Service, Map<Capability, String>>();
diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java
index 7530e94..622b448 100755
--- a/server/src/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/com/cloud/network/NetworkServiceImpl.java
@@ -16,31 +16,6 @@
 // under the License.
 package com.cloud.network;
 
-import java.security.InvalidParameterException;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.log4j.Logger;
-
-import org.apache.cloudstack.acl.ControlledEntity.ACLType;
-import org.apache.cloudstack.acl.SecurityChecker.AccessType;
-import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd;
-import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
-import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
-import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd;
-
 import com.cloud.configuration.Config;
 import com.cloud.configuration.ConfigurationManager;
 import com.cloud.configuration.dao.ConfigurationDao;
@@ -58,17 +33,10 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.EventDao;
 import com.cloud.event.dao.UsageEventDao;
-import com.cloud.exception.ConcurrentOperationException;
-import com.cloud.exception.InsufficientAddressCapacityException;
-import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.UnsupportedServiceException;
+import com.cloud.exception.*;
 import com.cloud.network.IpAddress.State;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.GuestType;
@@ -79,16 +47,7 @@
 import com.cloud.network.PhysicalNetwork.BroadcastDomainRange;
 import com.cloud.network.VirtualRouterProvider.VirtualRouterProviderType;
 import com.cloud.network.addr.PublicIp;
-import com.cloud.network.dao.FirewallRulesDao;
-import com.cloud.network.dao.IPAddressDao;
-import com.cloud.network.dao.NetworkDao;
-import com.cloud.network.dao.NetworkDomainDao;
-import com.cloud.network.dao.NetworkServiceMapDao;
-import com.cloud.network.dao.PhysicalNetworkDao;
-import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
-import com.cloud.network.dao.PhysicalNetworkServiceProviderVO;
-import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
-import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
+import com.cloud.network.dao.*;
 import com.cloud.network.element.NetworkElement;
 import com.cloud.network.element.VirtualRouterElement;
 import com.cloud.network.element.VpcVirtualRouterElement;
@@ -109,13 +68,7 @@
 import com.cloud.server.ResourceTag.TaggedResourceType;
 import com.cloud.tags.ResourceTagVO;
 import com.cloud.tags.dao.ResourceTagDao;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.AccountVO;
-import com.cloud.user.DomainManager;
-import com.cloud.user.ResourceLimitService;
-import com.cloud.user.User;
-import com.cloud.user.UserContext;
+import com.cloud.user.*;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.AnnotationHelper;
 import com.cloud.utils.NumbersUtil;
@@ -123,25 +76,28 @@
 import com.cloud.utils.component.Adapters;
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
-import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
-import com.cloud.vm.NicVO;
-import com.cloud.vm.ReservationContext;
-import com.cloud.vm.ReservationContextImpl;
-import com.cloud.vm.SecondaryStorageVmVO;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.*;
 import com.cloud.vm.dao.NicDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.acl.ControlledEntity.ACLType;
+import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd;
+import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
+import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
+import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.security.InvalidParameterException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
 
 /**
  * NetworkServiceImpl implements NetworkService.
@@ -615,6 +571,10 @@
         boolean isDomainSpecific = false;
         Boolean subdomainAccess = cmd.getSubdomainAccess();
         Long vpcId = cmd.getVpcId();
+        String startIPv6 = cmd.getStartIpv6();
+        String endIPv6 = cmd.getEndIpv6();
+        String ip6Gateway = cmd.getIp6Gateway();
+        String ip6Cidr = cmd.getIp6Cidr();
 
         // Validate network offering
         NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
@@ -731,37 +691,90 @@
 
         UserContext.current().setAccountId(owner.getAccountId());
 
-        // VALIDATE IP INFO
-        // if end ip is not specified, default it to startIp
+        boolean ipv4 = false, ipv6 = false;
         if (startIP != null) {
-            if (!NetUtils.isValidIp(startIP)) {
-                throw new InvalidParameterValueException("Invalid format for the startIp parameter");
-            }
-            if (endIP == null) {
-                endIP = startIP;
-            } else if (!NetUtils.isValidIp(endIP)) {
-                throw new InvalidParameterValueException("Invalid format for the endIp parameter");
-            }
+        	ipv4 = true;
         }
-
-        if (startIP != null && endIP != null) {
-            if (!(gateway != null && netmask != null)) {
-                throw new InvalidParameterValueException("gateway and netmask should be defined when startIP/endIP are passed in");
-            }
+        if (startIPv6 != null) {
+        	ipv6 = true;
         }
-
+        
         String cidr = null;
-        if (gateway != null && netmask != null) {
-            if (!NetUtils.isValidIp(gateway)) {
-                throw new InvalidParameterValueException("Invalid gateway");
-            }
-            if (!NetUtils.isValidNetmask(netmask)) {
-                throw new InvalidParameterValueException("Invalid netmask");
-            }
+        if (ipv4) {
+        	// if end ip is not specified, default it to startIp
+        	if (startIP != null) {
+        		if (!NetUtils.isValidIp(startIP)) {
+        			throw new InvalidParameterValueException("Invalid format for the startIp parameter");
+        		}
+        		if (endIP == null) {
+        			endIP = startIP;
+        		} else if (!NetUtils.isValidIp(endIP)) {
+        			throw new InvalidParameterValueException("Invalid format for the endIp parameter");
+        		}
+        	}
 
-            cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
+        	if (startIP != null && endIP != null) {
+        		if (!(gateway != null && netmask != null)) {
+        			throw new InvalidParameterValueException("gateway and netmask should be defined when startIP/endIP are passed in");
+        		}
+        	}
+
+        	if (gateway != null && netmask != null) {
+        		if (!NetUtils.isValidIp(gateway)) {
+        			throw new InvalidParameterValueException("Invalid gateway");
+        		}
+        		if (!NetUtils.isValidNetmask(netmask)) {
+        			throw new InvalidParameterValueException("Invalid netmask");
+        		}
+
+        		cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
+        	}
+
+        }
+        
+        if (ipv6) {
+        	if (!NetUtils.isValidIpv6(startIPv6)) {
+        		throw new InvalidParameterValueException("Invalid format for the startIPv6 parameter");
+        	}
+        	if (endIPv6 == null) {
+        		endIPv6 = startIPv6;
+        	} else if (!NetUtils.isValidIpv6(endIPv6)) {
+        		throw new InvalidParameterValueException("Invalid format for the endIPv6 parameter");
+        	}
+        	
+        	if (!(ip6Gateway != null && ip6Cidr != null)) {
+        		throw new InvalidParameterValueException("ip6Gateway and ip6Cidr should be defined when startIPv6/endIPv6 are passed in");
+        	}
+        	
+        	if (!NetUtils.isValidIpv6(ip6Gateway)) {
+        		throw new InvalidParameterValueException("Invalid ip6Gateway");
+        	}
+        	if (!NetUtils.isValidIp6Cidr(ip6Cidr)) {
+        		throw new InvalidParameterValueException("Invalid ip6cidr");
+        	}
+        	if (!NetUtils.isIp6InRange(startIPv6, ip6Cidr)) {
+        		throw new InvalidParameterValueException("startIPv6 is not in ip6cidr indicated network range!");
+        	}
+        	if (!NetUtils.isIp6InRange(endIPv6, ip6Cidr)) {
+        		throw new InvalidParameterValueException("endIPv6 is not in ip6cidr indicated network range!");
+        	}
+        	if (!NetUtils.isIp6InRange(ip6Gateway, ip6Cidr)) {
+        		throw new InvalidParameterValueException("ip6Gateway is not in ip6cidr indicated network range!");
+        	}
+        	
+        	int cidrSize = NetUtils.getIp6CidrSize(ip6Cidr);
+        	// Ipv6 cidr limit should be at least /64
+        	if (cidrSize < 64) {
+        		throw new InvalidParameterValueException("The cidr size of IPv6 network must be no less than 64 bits!");
+        	}
         }
 
+        if (ipv6) {
+        	if (zone.getNetworkType() != NetworkType.Advanced || ntwkOff.getGuestType() != Network.GuestType.Shared) {
+        		throw new InvalidParameterValueException("Can only support create IPv6 network with advance shared network!");
+        	}
+        }
+        
         // Regular user can create Guest Isolated Source Nat enabled network only
         if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL
                 && (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != Network.GuestType.Isolated
@@ -775,19 +788,25 @@
         if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL && (ntwkOff.getSpecifyVlan() || vlanId != null)) {
             throw new InvalidParameterValueException("Regular user is not allowed to specify vlanId");
         }
+        
+        if (ipv4) {
+        	// For non-root admins check cidr limit - if it's allowed by global config value
+        	if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN && cidr != null) {
 
-        // For non-root admins check cidr limit - if it's allowed by global config value
-        if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN && cidr != null) {
+        		String[] cidrPair = cidr.split("\\/");
+        		int cidrSize = Integer.valueOf(cidrPair[1]);
 
-            String[] cidrPair = cidr.split("\\/");
-            int cidrSize = Integer.valueOf(cidrPair[1]);
-
-            if (cidrSize < _cidrLimit) {
-                throw new InvalidParameterValueException("Cidr size can't be less than " + _cidrLimit);
-            }
+        		if (cidrSize < _cidrLimit) {
+        			throw new InvalidParameterValueException("Cidr size can't be less than " + _cidrLimit);
+        		}
+        	}
         }
 
         Collection<String> ntwkProviders = _networkMgr.finalizeServicesAndProvidersForNetwork(ntwkOff, physicalNetworkId).values();
+        if (ipv6 && providersConfiguredForExternalNetworking(ntwkProviders)) {
+        	throw new InvalidParameterValueException("Cannot support IPv6 on network offering with external devices!");
+        }
+        
         if (cidr != null && providersConfiguredForExternalNetworking(ntwkProviders)) {
             if (ntwkOff.getGuestType() == GuestType.Shared && (zone.getNetworkType() == NetworkType.Advanced) &&
                     isSharedNetworkOfferingWithServices(networkOfferingId)) {
@@ -798,7 +817,6 @@
             }
         }
 
-
         // Vlan is created in 2 cases - works in Advance zone only:
         // 1) GuestType is Shared
         // 2) GuestType is Isolated, but SourceNat service is disabled
@@ -806,6 +824,13 @@
                 && ((ntwkOff.getGuestType() == Network.GuestType.Shared)
                 || (ntwkOff.getGuestType() == GuestType.Isolated && 
                 !areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))));
+        
+        if (!createVlan) {
+        	// Only support advance shared network in IPv6, which means createVlan is a must
+        	if (ipv6) {
+        		createVlan = true;
+        	}
+        }
 
         // Can add vlan range only to the network which allows it
         if (createVlan && !ntwkOff.getSpecifyIpRanges()) {
@@ -851,13 +876,13 @@
                 throw new InvalidParameterValueException("Network offering can be used for VPC networks only");
             }
             network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, 
-                    networkDomain, owner, sharedDomainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId);
+            		networkDomain, owner, sharedDomainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr);
         }  
 
         if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN && createVlan) {
             // Create vlan ip range
             _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId,
-                    false, null, startIP, endIP, gateway, netmask, vlanId, null);
+                    false, null, startIP, endIP, gateway, netmask, vlanId, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr);
         }
 
         txn.commit();
@@ -1623,10 +1648,8 @@
                         continue;
                     }
                     long isDefault = (nic.isDefaultNic()) ? 1 : 0;
-                    UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), null, oldNetworkOfferingId, null, 0L);
-                    _usageEventDao.persist(usageEvent);
-                    usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm.getHostName(), networkOfferingId, null, isDefault);
-                    _usageEventDao.persist(usageEvent);
+                    UsageEventUtils.saveUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), null, oldNetworkOfferingId, null, 0L);
+                    UsageEventUtils.saveUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm.getHostName(), networkOfferingId, null, isDefault);
                 }
                 txn.commit();
             } else {
@@ -2813,7 +2836,7 @@
         if (privateNetwork == null) {
             //create Guest network
             privateNetwork = _networkMgr.createGuestNetwork(ntwkOff.getId(), networkName, displayText, gateway, cidr, vlan, 
-                    null, owner, null, pNtwk, pNtwk.getDataCenterId(), ACLType.Account, null, null);
+                    null, owner, null, pNtwk, pNtwk.getDataCenterId(), ACLType.Account, null, null, null, null);
             s_logger.debug("Created private network " + privateNetwork);
         } else {
             s_logger.debug("Private network already exists: " + privateNetwork);
diff --git a/server/src/com/cloud/network/NetworkStateListener.java b/server/src/com/cloud/network/NetworkStateListener.java
new file mode 100644
index 0000000..1300633
--- /dev/null
+++ b/server/src/com/cloud/network/NetworkStateListener.java
@@ -0,0 +1,90 @@
+package com.cloud.network;
+
+import com.cloud.event.EventCategory;
+import com.cloud.event.dao.UsageEventDao;
+import com.cloud.network.Network.Event;
+import com.cloud.network.Network.State;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.server.ManagementServer;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.fsm.StateListener;
+import org.apache.cloudstack.framework.events.EventBus;
+import org.apache.cloudstack.framework.events.EventBusException;
+import org.apache.log4j.Logger;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NetworkStateListener implements StateListener<State, Event, Network> {
+
+    protected UsageEventDao _usageEventDao;
+    protected NetworkDao _networkDao;
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
+    private static final Logger s_logger = Logger.getLogger(NetworkStateListener.class);
+
+    public NetworkStateListener(UsageEventDao usageEventDao, NetworkDao networkDao) {
+        this._usageEventDao = usageEventDao;
+        this._networkDao = networkDao;
+    }
+
+    @Override
+    public boolean preStateTransitionEvent(State oldState, Event event, State newState, Network vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
+        return true;
+    }
+
+    @Override
+    public boolean postStateTransitionEvent(State oldState, Event event, State newState, Network vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState);
+        return true;
+    }
+
+    private void pubishOnEventBus(String event, String status, Network vo, State oldState, State newState) {
+
+        if (_eventBus == null) {
+            return; // no provider is configured to provide events bus, so just return
+        }
+
+        String resourceName = getEntityFromClassName(Network.class.getName());
+        org.apache.cloudstack.framework.events.Event eventMsg =  new org.apache.cloudstack.framework.events.Event(
+                ManagementServer.Name,
+                EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(),
+                event,
+                resourceName,
+                vo.getUuid());
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        eventDescription.put("resource", resourceName);
+        eventDescription.put("id", vo.getUuid());
+        eventDescription.put("old-state", oldState.name());
+        eventDescription.put("new-state", newState.name());
+        eventMsg.setDescription(eventDescription);
+        try {
+            _eventBus.publish(eventMsg);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to publish state change event on the the event bus.");
+        }
+    }
+
+    private String getEntityFromClassName(String entityClassName) {
+        int index = entityClassName.lastIndexOf(".");
+        String entityName = entityClassName;
+        if (index != -1) {
+            entityName = entityClassName.substring(index+1);
+        }
+        return entityName;
+    }
+}
diff --git a/server/src/com/cloud/network/NetworkVO.java b/server/src/com/cloud/network/NetworkVO.java
index 14b643b..8020e7a 100644
--- a/server/src/com/cloud/network/NetworkVO.java
+++ b/server/src/com/cloud/network/NetworkVO.java
@@ -148,6 +148,12 @@
 
     @Column(name="specify_ip_ranges")
     boolean specifyIpRanges = false;
+    
+    @Column(name="ip6_gateway")
+    String ip6Gateway;
+
+    @Column(name="ip6_cidr")
+    String ip6Cidr;
 
     public NetworkVO() {
     	this.uuid = UUID.randomUUID().toString();
@@ -195,6 +201,8 @@
             state = State.Allocated;
         }
     	this.uuid = UUID.randomUUID().toString();
+    	this.ip6Gateway = that.getIp6Gateway();
+    	this.ip6Cidr = that.getIp6Cidr();
     }
 
     /**
@@ -246,6 +254,7 @@
         return state;
     }
 
+    // don't use this directly when possible, use Network state machine instead
     public void setState(State state) {
         this.state = state;
     }
@@ -491,4 +500,20 @@
     public Long getVpcId() {
         return vpcId;
     }
+
+	public String getIp6Cidr() {
+		return ip6Cidr;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
+
+	public String getIp6Gateway() {
+		return ip6Gateway;
+	}
+
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
 }
diff --git a/server/src/com/cloud/network/UserIpv6Address.java b/server/src/com/cloud/network/UserIpv6Address.java
new file mode 100644
index 0000000..4c33d45
--- /dev/null
+++ b/server/src/com/cloud/network/UserIpv6Address.java
@@ -0,0 +1,52 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network;
+
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+/**
+ * @author Sheng Yang
+ *
+ */
+public interface UserIpv6Address extends ControlledEntity, Identity, InternalIdentity {
+    enum State {
+        Allocating, // The IP Address is being propagated to other network elements and is not ready for use yet.
+        Allocated, // The IP address is in used.
+        Releasing, // The IP address is being released for other network elements and is not ready for allocation.
+        Free // The IP address is ready to be allocated.
+    }
+
+    long getDataCenterId();
+
+    String getAddress();
+
+    long getVlanId();
+
+    State getState();
+
+    Long getNetworkId();
+    
+    Long getSourceNetworkId();
+
+    Long getPhysicalNetworkId();
+
+    void setState(UserIpv6Address.State state);
+    
+    String getMacAddress();
+}
diff --git a/server/src/com/cloud/network/UserIpv6AddressVO.java b/server/src/com/cloud/network/UserIpv6AddressVO.java
new file mode 100644
index 0000000..70eb12d
--- /dev/null
+++ b/server/src/com/cloud/network/UserIpv6AddressVO.java
@@ -0,0 +1,188 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network;
+
+import java.util.Date;
+import java.util.UUID;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import com.cloud.utils.db.GenericDao;
+
+@Entity
+@Table(name=("user_ipv6_address"))
+public class UserIpv6AddressVO implements UserIpv6Address {
+    @Id
+    @GeneratedValue(strategy=GenerationType.IDENTITY)
+    @Column(name="id")
+    long id;
+
+	@Column(name="ip_address")
+	@Enumerated(value=EnumType.STRING)
+	private String address = null;
+
+	@Column(name="data_center_id", updatable=false)
+	private long dataCenterId;
+
+	@Column(name="vlan_id")
+	private long vlanId;
+
+	@Column(name="state")
+	private State state;
+
+	@Column(name="mac_address")
+	private String macAddress;
+
+	@Column(name="source_network_id")
+    private Long sourceNetworkId;
+
+	@Column(name="network_id")
+	private Long networkId;
+
+	@Column(name="uuid")
+	private String uuid;
+
+    @Column(name="physical_network_id")
+    private Long physicalNetworkId;
+
+	@Column(name="account_id")
+	private Long accountId = null;
+
+    @Column(name="domain_id")
+    private Long domainId = null;
+	
+    @Column(name = GenericDao.CREATED_COLUMN)
+    Date created;
+	
+	protected UserIpv6AddressVO() {
+		this.uuid = UUID.randomUUID().toString();
+	}
+
+	public UserIpv6AddressVO(String address, long dataCenterId, String macAddress, long vlanDbId) {
+		this.address = address;
+		this.dataCenterId = dataCenterId;
+		this.vlanId = vlanDbId;
+		this.state = State.Free;
+		this.setMacAddress(macAddress);
+		this.uuid = UUID.randomUUID().toString();
+	}
+
+
+	@Override
+	public long getAccountId() {
+		return accountId;
+	}
+
+	@Override
+	public long getDomainId() {
+		return domainId;
+	}
+
+	@Override
+	public String getUuid() {
+		return uuid;
+	}
+
+	@Override
+	public long getId() {
+		return id;
+	}
+
+	@Override
+	public long getDataCenterId() {
+		return dataCenterId;
+	}
+
+	@Override
+	public String getAddress() {
+		return address;
+	}
+
+	@Override
+	public long getVlanId() {
+		return vlanId;
+	}
+
+	@Override
+	public State getState() {
+		return state;
+	}
+
+	@Override
+	public Long getNetworkId() {
+		return networkId;
+	}
+
+	@Override
+	public Long getSourceNetworkId() {
+		return sourceNetworkId;
+	}
+
+	@Override
+	public Long getPhysicalNetworkId() {
+		return physicalNetworkId;
+	}
+
+	@Override
+	public void setState(State state) {
+		this.state = state;
+	}
+
+	public String getMacAddress() {
+		return macAddress;
+	}
+
+	public void setMacAddress(String macAddress) {
+		this.macAddress = macAddress;
+	}
+
+	public void setSourceNetworkId(Long sourceNetworkId) {
+		this.sourceNetworkId = sourceNetworkId;
+	}
+
+	public void setNetworkId(Long networkId) {
+		this.networkId = networkId;
+	}
+
+	public void setPhysicalNetworkId(Long physicalNetworkId) {
+		this.physicalNetworkId = physicalNetworkId;
+	}
+
+	public void setDomainId(Long domainId) {
+		this.domainId = domainId;
+	}
+
+	public void setAccountId(Long accountId) {
+		this.accountId = accountId;
+	}
+
+	public Date getCreated() {
+		return created;
+	}
+
+	public void setCreated(Date created) {
+		this.created = created;
+	}
+}
diff --git a/server/src/com/cloud/network/addr/PublicIp.java b/server/src/com/cloud/network/addr/PublicIp.java
index 8ad716f..246aa60 100644
--- a/server/src/com/cloud/network/addr/PublicIp.java
+++ b/server/src/com/cloud/network/addr/PublicIp.java
@@ -206,4 +206,19 @@
     public void setVpcId(Long vpcId) {
         _addr.setVpcId(vpcId);
     }
+
+	@Override
+	public String getIp6Gateway() {
+		return _vlan.getIp6Gateway();
+	}
+
+	@Override
+	public String getIp6Cidr() {
+		return _vlan.getIp6Cidr();
+	}
+
+	@Override
+	public String getIp6Range() {
+		return _vlan.getIp6Range();
+	}
 }
diff --git a/server/src/com/cloud/network/dao/NetworkDao.java b/server/src/com/cloud/network/dao/NetworkDao.java
index 1fefb75..18dcb6f 100644
--- a/server/src/com/cloud/network/dao/NetworkDao.java
+++ b/server/src/com/cloud/network/dao/NetworkDao.java
@@ -24,10 +24,12 @@
 import com.cloud.network.NetworkAccountVO;
 import com.cloud.network.NetworkVO;
 import com.cloud.network.Networks.TrafficType;
+import com.cloud.network.Network.State;
 import com.cloud.utils.db.GenericDao;
 import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.fsm.StateDao;
 
-public interface NetworkDao extends GenericDao<NetworkVO, Long> {
+public interface NetworkDao extends GenericDao<NetworkVO, Long> , StateDao<State, Network.Event, Network> {
 
     List<NetworkVO> listByOwner(long ownerId);
 
diff --git a/server/src/com/cloud/network/dao/NetworkDaoImpl.java b/server/src/com/cloud/network/dao/NetworkDaoImpl.java
index 29e2f81..2c5b46d 100644
--- a/server/src/com/cloud/network/dao/NetworkDaoImpl.java
+++ b/server/src/com/cloud/network/dao/NetworkDaoImpl.java
@@ -28,6 +28,8 @@
 import com.cloud.network.Network.GuestType;
 import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
+import com.cloud.network.Network.State;
+import com.cloud.network.Network.Event;
 import com.cloud.network.NetworkAccountDaoImpl;
 import com.cloud.network.NetworkAccountVO;
 import com.cloud.network.NetworkDomainVO;
@@ -42,19 +44,18 @@
 import com.cloud.server.ResourceTag.TaggedResourceType;
 import com.cloud.tags.dao.ResourceTagsDaoImpl;
 import com.cloud.utils.component.ComponentLocator;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.GenericDaoBase;
-import com.cloud.utils.db.GenericSearchBuilder;
-import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.JoinBuilder.JoinType;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Func;
 import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.SequenceFetcher;
-import com.cloud.utils.db.Transaction;
 import com.cloud.utils.net.NetUtils;
 
+import javax.ejb.Local;
+import javax.persistence.TableGenerator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
 @Local(value = NetworkDao.class)
 @DB(txn = false)
 public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long> implements NetworkDao {
@@ -565,6 +566,20 @@
     }
 
     @Override
+    public boolean updateState(State currentState, Event event, State nextState, Network vo, Object data) {
+       // TODO: ensure this update is correct
+       Transaction txn = Transaction.currentTxn();
+       txn.start();
+
+       NetworkVO networkVo = (NetworkVO) vo;
+       networkVo.setState(nextState);
+       super.update(networkVo.getId(), networkVo);
+
+       txn.commit();
+       return true;
+    }
+
+    @Override
     public List<NetworkVO> listNetworksByAccount(long accountId, long zoneId, Network.GuestType type, boolean isSystem) {
         SearchCriteria<NetworkVO> sc = OfferingAccountNetworkSearch.create();
         sc.setJoinParameters("ntwkOfferingSearch", "isSystem", isSystem);
@@ -580,7 +595,6 @@
     public List<NetworkVO> listRedundantNetworks() {
         SearchCriteria<NetworkVO> sc = AllFieldsSearch.create();
         sc.setJoinParameters("offerings", "isRedundant", true);
-        
         return listBy(sc, null);
     }
 }
diff --git a/server/src/com/cloud/network/dao/UserIpv6AddressDao.java b/server/src/com/cloud/network/dao/UserIpv6AddressDao.java
new file mode 100644
index 0000000..81e0da8
--- /dev/null
+++ b/server/src/com/cloud/network/dao/UserIpv6AddressDao.java
@@ -0,0 +1,39 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network.dao;
+
+import java.util.List;
+
+import com.cloud.network.Network;
+import com.cloud.network.UserIpv6AddressVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface UserIpv6AddressDao extends GenericDao<UserIpv6AddressVO, Long> {
+	List<UserIpv6AddressVO> listByAccount(long accountId);
+	
+	List<UserIpv6AddressVO> listByVlanId(long vlanId);
+	
+	List<UserIpv6AddressVO> listByDcId(long dcId); 
+	
+	List<UserIpv6AddressVO> listByNetwork(long networkId);
+	
+	public UserIpv6AddressVO findByNetworkIdAndIp(long networkId, String ipAddress);
+
+	List<UserIpv6AddressVO> listByPhysicalNetworkId(long physicalNetworkId);
+
+	long countExistedIpsInNetwork(long networkId);
+}
diff --git a/server/src/com/cloud/network/dao/UserIpv6AddressDaoImpl.java b/server/src/com/cloud/network/dao/UserIpv6AddressDaoImpl.java
new file mode 100644
index 0000000..c77e72d
--- /dev/null
+++ b/server/src/com/cloud/network/dao/UserIpv6AddressDaoImpl.java
@@ -0,0 +1,112 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.network.Network;
+import com.cloud.network.UserIpv6AddressVO;
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.GenericSearchBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Func;
+import com.cloud.utils.db.SearchCriteria.Op;
+import com.cloud.utils.db.SearchCriteria2;
+
+@Local(value=UserIpv6AddressDao.class)
+public class UserIpv6AddressDaoImpl extends GenericDaoBase<UserIpv6AddressVO, Long> implements UserIpv6AddressDao {
+    private static final Logger s_logger = Logger.getLogger(IPAddressDaoImpl.class);
+
+    protected final SearchBuilder<UserIpv6AddressVO> AllFieldsSearch;
+    protected GenericSearchBuilder<UserIpv6AddressVO, Long> CountFreePublicIps;
+    
+    public UserIpv6AddressDaoImpl() {
+        AllFieldsSearch = createSearchBuilder();
+        AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ);
+        AllFieldsSearch.and("dataCenterId", AllFieldsSearch.entity().getDataCenterId(), Op.EQ);
+        AllFieldsSearch.and("ipAddress", AllFieldsSearch.entity().getAddress(), Op.EQ);
+        AllFieldsSearch.and("vlan", AllFieldsSearch.entity().getVlanId(), Op.EQ);
+        AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ);
+        AllFieldsSearch.and("network", AllFieldsSearch.entity().getNetworkId(), Op.EQ);
+        AllFieldsSearch.and("physicalNetworkId", AllFieldsSearch.entity().getPhysicalNetworkId(), Op.EQ);
+        AllFieldsSearch.done();
+        
+        CountFreePublicIps = createSearchBuilder(Long.class);
+        CountFreePublicIps.select(null, Func.COUNT, null);
+        CountFreePublicIps.and("networkId", CountFreePublicIps.entity().getSourceNetworkId(), SearchCriteria.Op.EQ);
+        CountFreePublicIps.done();
+    }
+
+	@Override
+	public List<UserIpv6AddressVO> listByAccount(long accountId) {
+        SearchCriteria<UserIpv6AddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("accountId", accountId);
+        return listBy(sc);
+	}
+
+	@Override
+	public List<UserIpv6AddressVO> listByVlanId(long vlanId) {
+        SearchCriteria<UserIpv6AddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("vlan", vlanId);
+        return listBy(sc);
+	}
+
+	@Override
+	public List<UserIpv6AddressVO> listByDcId(long dcId) {
+        SearchCriteria<UserIpv6AddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("dataCenterId", dcId);
+        return listBy(sc);
+	}
+
+	@Override
+	public List<UserIpv6AddressVO> listByNetwork(long networkId) {
+        SearchCriteria<UserIpv6AddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("network", networkId);
+        return listBy(sc);
+	}
+
+	@Override
+	public UserIpv6AddressVO findByNetworkIdAndIp(long networkId, String ipAddress) {
+        SearchCriteria<UserIpv6AddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("networkId", networkId);
+        sc.setParameters("ipAddress", ipAddress);
+        return findOneBy(sc);
+	}
+
+	@Override
+	public List<UserIpv6AddressVO> listByPhysicalNetworkId(long physicalNetworkId) {
+        SearchCriteria<UserIpv6AddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("physicalNetworkId", physicalNetworkId);
+        return listBy(sc);
+	}
+
+	@Override
+	public long countExistedIpsInNetwork(long networkId) {
+        SearchCriteria<Long> sc = CountFreePublicIps.create();
+        sc.setParameters("networkId", networkId);
+        return customSearch(sc, null).get(0);       
+	}
+}
diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java
index 8293ad7..b224537 100755
--- a/server/src/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/com/cloud/network/element/VirtualRouterElement.java
@@ -841,6 +841,11 @@
             if (vm.getType() != VirtualMachine.Type.User) {
                 return false;
             }
+            
+            if (network.getIp6Gateway() != null) {
+            	s_logger.info("Skip password and userdata service setup for IPv6 VM");
+            	return true;
+            }
 
             @SuppressWarnings("unchecked")
             VirtualMachineProfile<UserVm> uservm = (VirtualMachineProfile<UserVm>) vm;
diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java
index d4958f3..a3f60cb 100644
--- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java
+++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java
@@ -40,7 +40,7 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.EventDao;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.InvalidParameterValueException;
@@ -61,18 +61,12 @@
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.element.FirewallServiceProvider;
 import com.cloud.network.element.NetworkACLServiceProvider;
-import com.cloud.network.element.NetworkElement;
 import com.cloud.network.element.PortForwardingServiceProvider;
 import com.cloud.network.element.StaticNatServiceProvider;
-import com.cloud.network.rules.FirewallManager;
-import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.rules.*;
 import com.cloud.network.rules.FirewallRule.FirewallRuleType;
 import com.cloud.network.rules.FirewallRule.Purpose;
 import com.cloud.network.rules.FirewallRule.State;
-import com.cloud.network.rules.FirewallRuleVO;
-import com.cloud.network.rules.PortForwardingRule;
-import com.cloud.network.rules.PortForwardingRuleVO;
-import com.cloud.network.rules.StaticNat;
 import com.cloud.network.rules.dao.PortForwardingRulesDao;
 import com.cloud.network.vpc.VpcManager;
 import com.cloud.projects.Project.ListProjectResourcesCriteria;
@@ -88,17 +82,18 @@
 import com.cloud.utils.component.Adapters;
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
-import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.dao.UserVmDao;
+import org.apache.cloudstack.api.command.user.firewall.ListFirewallRulesCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.util.*;
 
 @Local(value = { FirewallService.class, FirewallManager.class})
 public class FirewallManagerImpl implements FirewallService, FirewallManager, NetworkRuleApplier, Manager {
@@ -709,8 +704,8 @@
         }
 
         if (generateUsageEvent && needUsageEvent) {
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_RULE_DELETE, rule.getAccountId(), 0, rule.getId(), null);
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_RULE_DELETE, rule.getAccountId(), 0, rule.getId(),
+                    null, rule.getClass().getName(), rule.getUuid());
         }
 
         txn.commit();
diff --git a/server/src/com/cloud/network/guru/DirectNetworkGuru.java b/server/src/com/cloud/network/guru/DirectNetworkGuru.java
index b290c1d..0d5425f 100755
--- a/server/src/com/cloud/network/guru/DirectNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/DirectNetworkGuru.java
@@ -31,6 +31,7 @@
 import com.cloud.exception.InsufficientVirtualNetworkCapcityException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.network.IPAddressVO;
+import com.cloud.network.Ipv6AddressManager;
 import com.cloud.network.Network;
 import com.cloud.network.Network.GuestType;
 import com.cloud.network.Network.Service;
@@ -42,7 +43,9 @@
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.network.Networks.Mode;
 import com.cloud.network.Networks.TrafficType;
+import com.cloud.network.UserIpv6AddressVO;
 import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.UserIpv6AddressDao;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.user.Account;
@@ -72,6 +75,10 @@
     IPAddressDao _ipAddressDao;
     @Inject
     NetworkOfferingDao _networkOfferingDao;
+    @Inject
+    UserIpv6AddressDao _ipv6Dao;
+    @Inject
+    Ipv6AddressManager _ipv6Mgr;
     
     private static final TrafficType[] _trafficTypes = {TrafficType.Guest};
     
@@ -120,11 +127,20 @@
                 throw new InvalidParameterValueException("cidr and gateway must be specified together.");
             }
 
+            if ((userSpecified.getIp6Cidr() == null && userSpecified.getIp6Gateway() != null) || (userSpecified.getIp6Cidr() != null && userSpecified.getIp6Gateway() == null)) {
+                throw new InvalidParameterValueException("cidrv6 and gatewayv6 must be specified together.");
+            }
+
             if (userSpecified.getCidr() != null) {
                 config.setCidr(userSpecified.getCidr());
                 config.setGateway(userSpecified.getGateway());
             }
 
+            if (userSpecified.getIp6Cidr() != null) {
+                config.setIp6Cidr(userSpecified.getIp6Cidr());
+                config.setIp6Gateway(userSpecified.getIp6Gateway());
+            }
+
             if (userSpecified.getBroadcastUri() != null) {
                 config.setBroadcastUri(userSpecified.getBroadcastUri());
                 config.setState(State.Setup);
@@ -137,6 +153,9 @@
 
         boolean isSecurityGroupEnabled = _networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Service.SecurityGroup);
         if (isSecurityGroupEnabled) {
+        	if (userSpecified.getIp6Cidr() != null) {
+                throw new InvalidParameterValueException("Didn't support security group with IPv6");
+        	}
             config.setName("SecurityGroupEnabledNetwork");
             config.setDisplayText("SecurityGroupEnabledNetwork");
         }
@@ -165,13 +184,13 @@
 
         if (nic == null) {
             nic = new NicProfile(ReservationStrategy.Create, null, null, null, null);
-        } else if (nic.getIp4Address() == null) {
+        } else if (nic.getIp4Address() == null && nic.getIp6Address() == null) {
             nic.setStrategy(ReservationStrategy.Start);
         } else {
             nic.setStrategy(ReservationStrategy.Create);
         }
 
-        _networkMgr.allocateDirectIp(nic, dc, vm, network, nic.getRequestedIp());
+        _networkMgr.allocateDirectIp(nic, dc, vm, network, nic.getRequestedIpv4(), nic.getRequestedIpv6());
         nic.setStrategy(ReservationStrategy.Create);
 
         return nic;
@@ -180,8 +199,8 @@
     @Override
     public void reserve(NicProfile nic, Network network, VirtualMachineProfile<? extends VirtualMachine> vm, DeployDestination dest, ReservationContext context)
             throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException {
-        if (nic.getIp4Address() == null) {
-            _networkMgr.allocateDirectIp(nic, dest.getDataCenter(), vm, network, null);
+        if (nic.getIp4Address() == null && nic.getIp6Address() == null) {
+            _networkMgr.allocateDirectIp(nic, dest.getDataCenter(), vm, network, null, null);
             nic.setStrategy(ReservationStrategy.Create);
         }
     }
@@ -202,14 +221,20 @@
             s_logger.debug("Deallocate network: networkId: " + nic.getNetworkId() + ", ip: " + nic.getIp4Address());
         }
     	
-        IPAddressVO ip = _ipAddressDao.findByIpAndSourceNetworkId(nic.getNetworkId(), nic.getIp4Address());
-        if (ip != null) {
-            Transaction txn = Transaction.currentTxn();
-            txn.start();
-            _networkMgr.markIpAsUnavailable(ip.getId());
-            _ipAddressDao.unassignIpAddress(ip.getId());
-            txn.commit();
-        }
+    	if (nic.getIp4Address() != null) {
+    		IPAddressVO ip = _ipAddressDao.findByIpAndSourceNetworkId(nic.getNetworkId(), nic.getIp4Address());
+    		if (ip != null) {
+    			Transaction txn = Transaction.currentTxn();
+    			txn.start();
+    			_networkMgr.markIpAsUnavailable(ip.getId());
+    			_ipAddressDao.unassignIpAddress(ip.getId());
+    			txn.commit();
+    		}
+    	}
+    	
+    	if (nic.getIp6Address() != null) {
+    		_ipv6Mgr.revokeDirectIpv6Address(nic.getNetworkId(), nic.getIp6Address());
+    	}
         nic.deallocate();
     }
 
diff --git a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
index 8ca4547..709d280 100755
--- a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
@@ -96,7 +96,7 @@
             rsStrategy = ReservationStrategy.Create;
         }
         
-        if (nic != null && nic.getRequestedIp() != null) {
+        if (nic != null && nic.getRequestedIpv4() != null) {
             throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic);
         }
        
diff --git a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java
index f8a8a95..519ad26 100644
--- a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java
@@ -20,17 +20,16 @@
 
 import javax.ejb.Local;
 
+import com.cloud.event.ActionEventUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.configuration.Config;
-import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.dc.DataCenter;
 import com.cloud.dc.DataCenter.NetworkType;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
 import com.cloud.event.EventVO;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapcityException;
@@ -42,9 +41,7 @@
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.network.PhysicalNetwork;
 import com.cloud.network.PhysicalNetwork.IsolationMethod;
-import com.cloud.network.PhysicalNetworkVO;
 import com.cloud.network.dao.NetworkDao;
-import com.cloud.network.dao.PhysicalNetworkDao;
 import com.cloud.network.rules.PortForwardingRuleVO;
 import com.cloud.network.rules.dao.PortForwardingRulesDao;
 import com.cloud.offering.NetworkOffering;
@@ -142,7 +139,7 @@
             }
 
             implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlanTag));
-            EventUtils.saveEvent(UserContext.current().getCallerUserId(), config.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: "+vnet+ " Network Id: "+config.getId(), 0);
+            ActionEventUtils.onCompletedActionEvent(UserContext.current().getCallerUserId(), config.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: " + vnet + " Network Id: " + config.getId(), 0);
         } else {
             vlanTag = Integer.parseInt(config.getBroadcastUri().getHost());
             implemented.setBroadcastUri(config.getBroadcastUri());
@@ -195,7 +192,7 @@
     public NicProfile allocate(Network config, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm) throws InsufficientVirtualNetworkCapcityException,
             InsufficientAddressCapacityException {
 
-        if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId()) && nic != null && nic.getRequestedIp() != null) {
+        if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId()) && nic != null && nic.getRequestedIpv4() != null) {
             throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic);
         }
         
diff --git a/server/src/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/com/cloud/network/guru/GuestNetworkGuru.java
index 9587885..dc23b72 100755
--- a/server/src/com/cloud/network/guru/GuestNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/GuestNetworkGuru.java
@@ -19,12 +19,12 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
-import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
 import javax.ejb.Local;
 
+import com.cloud.event.ActionEventUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.configuration.Config;
@@ -36,7 +36,6 @@
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
 import com.cloud.event.EventVO;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapcityException;
@@ -298,8 +297,8 @@
                 		"part of network " + network + " implement ", DataCenter.class, dcId);
             }
             implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vnet));
-            EventUtils.saveEvent(UserContext.current().getCallerUserId(), network.getAccountId(), 
-                    EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: "+vnet+ " Network Id: "+network.getId(), 0);
+            ActionEventUtils.onCompletedActionEvent(UserContext.current().getCallerUserId(), network.getAccountId(),
+                    EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: " + vnet + " Network Id: " + network.getId(), 0);
         } else {
             implemented.setBroadcastUri(network.getBroadcastUri());
         }
@@ -352,7 +351,7 @@
 
             String guestIp = null;
             if (network.getSpecifyIpRanges()) {
-                _networkMgr.allocateDirectIp(nic, dc, vm, network, nic.getRequestedIp());
+                _networkMgr.allocateDirectIp(nic, dc, vm, network, nic.getRequestedIpv4(), null);
             } else {
                 //if Vm is router vm and source nat is enabled in the network, set ip4 to the network gateway
                 boolean isGateway = false;
@@ -371,7 +370,7 @@
                 if (isGateway) {
                     guestIp = network.getGateway();
                 } else {
-                    guestIp = _networkMgr.acquireGuestIpAddress(network, nic.getRequestedIp());
+                    guestIp = _networkMgr.acquireGuestIpAddress(network, nic.getRequestedIpv4());
                     if (guestIp == null) {
                         throw new InsufficientVirtualNetworkCapcityException("Unable to acquire Guest IP" +
                                 " address for network " + network, DataCenter.class, dc.getId());
@@ -433,9 +432,9 @@
         	s_logger.debug("Releasing vnet for the network id=" + profile.getId());
             _dcDao.releaseVnet(profile.getBroadcastUri().getHost(), profile.getDataCenterId(), 
                     profile.getPhysicalNetworkId(), profile.getAccountId(), profile.getReservationId());
-            EventUtils.saveEvent(UserContext.current().getCallerUserId(), profile.getAccountId(), 
+            ActionEventUtils.onCompletedActionEvent(UserContext.current().getCallerUserId(), profile.getAccountId(),
                     EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_RELEASE, "Released Zone Vlan: "
-                    +profile.getBroadcastUri().getHost()+" for Network: "+profile.getId(), 0);
+                    + profile.getBroadcastUri().getHost() + " for Network: " + profile.getId(), 0);
         }
         profile.setBroadcastUri(null);
     }
diff --git a/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java
index b513325..bbe568d 100755
--- a/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java
@@ -100,7 +100,7 @@
         assert trafficType == TrafficType.Management || trafficType == TrafficType.Storage: "Well, I can't take care of this config now can I? " + config; 
         
         if (nic != null) {
-            if (nic.getRequestedIp() != null) {
+            if (nic.getRequestedIpv4() != null) {
                 throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic);
             }
             nic.setStrategy(nic.getIp4Address() != null ? ReservationStrategy.Create : ReservationStrategy.Start);
diff --git a/server/src/com/cloud/network/guru/PublicNetworkGuru.java b/server/src/com/cloud/network/guru/PublicNetworkGuru.java
index ddee5a5..8e912d6 100755
--- a/server/src/com/cloud/network/guru/PublicNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/PublicNetworkGuru.java
@@ -142,7 +142,7 @@
 
         DataCenter dc = _dcDao.findById(network.getDataCenterId());       
 
-        if (nic != null && nic.getRequestedIp() != null) {
+        if (nic != null && nic.getRequestedIpv4() != null) {
             throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic);
         }
 
diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
index 482c1fe..8e47545 100755
--- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
+++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
@@ -30,6 +30,7 @@
 import javax.ejb.Local;
 import javax.naming.ConfigurationException;
 
+import com.cloud.event.UsageEventUtils;
 import org.apache.cloudstack.api.command.user.loadbalancer.*;
 import org.apache.log4j.Logger;
 
@@ -47,7 +48,6 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
 import com.cloud.event.dao.EventDao;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.InsufficientAddressCapacityException;
@@ -93,7 +93,6 @@
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkServiceMapDao;
 import com.cloud.network.element.LoadBalancingServiceProvider;
-import com.cloud.network.element.NetworkElement;
 import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy;
 import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup;
 import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmProfile;
@@ -871,8 +870,8 @@
 
         if (generateUsageEvent) {
             // Generate usage event right after all rules were marked for revoke
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null);
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(),
+                    null, LoadBalancingRule.class.getName(), lb.getUuid());
         }
 
         txn.commit();
@@ -1104,8 +1103,8 @@
             }
             s_logger.debug("Load balancer " + newRule.getId() + " for Ip address id=" + sourceIpId + ", public port " + srcPortStart + ", private port " + defPortStart + " is added successfully.");
             UserContext.current().setEventDetails("Load balancer Id: " + newRule.getId());
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), null);
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(),
+                    ipAddr.getDataCenterId(), newRule.getId(), null, LoadBalancingRule.class.getName(), newRule.getUuid());
             txn.commit();
 
             return newRule;
diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index 132dbaa..dee418b 100755
--- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@ -1678,7 +1678,7 @@
                 }
             }
 
-            NicProfile gatewayNic = new NicProfile(defaultNetworkStartIp);
+            NicProfile gatewayNic = new NicProfile(defaultNetworkStartIp, null);
             if (setupPublicNetwork) {
                 if (isRedundant) {
                     gatewayNic.setIp4Address(_networkMgr.acquireGuestIpAddress(guestNetwork, null));
@@ -1908,11 +1908,25 @@
         String defaultDns2 = null;
         for (NicProfile nic : profile.getNics()) {
             int deviceId = nic.getDeviceId();
-            buf.append(" eth").append(deviceId).append("ip=").append(nic.getIp4Address());
-            buf.append(" eth").append(deviceId).append("mask=").append(nic.getNetmask());
+            boolean ipv4 = false, ipv6 = false;
+            if (nic.getIp4Address() != null) {
+            	ipv4 = true;
+            	buf.append(" eth").append(deviceId).append("ip=").append(nic.getIp4Address());
+            	buf.append(" eth").append(deviceId).append("mask=").append(nic.getNetmask());
+            }
+            if (nic.getIp6Address() != null) {
+            	ipv6 = true;
+            	buf.append(" eth").append(deviceId).append("ip6=").append(nic.getIp6Address());
+            	buf.append(" eth").append(deviceId).append("ip6prelen=").append(NetUtils.getIp6CidrSize(nic.getIp6Cidr()));
+            }
             
             if (nic.isDefaultNic()) {
-                buf.append(" gateway=").append(nic.getGateway());
+            	if (ipv4) {
+            		buf.append(" gateway=").append(nic.getGateway());
+            	}
+            	if (ipv6) {
+            		buf.append(" ip6gateway=").append(nic.getIp6Gateway());
+            	}
                 defaultDns1 = nic.getDns1();
                 defaultDns2 = nic.getDns2();
             }
@@ -3052,11 +3066,11 @@
     }
     
     private void createDhcpEntryCommand(VirtualRouter router, UserVm vm, NicVO nic, Commands cmds) {
-        DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), vm.getHostName());
+        DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIp4Address(), vm.getHostName(), nic.getIp6Address());
         DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn());
         String gatewayIp = findGatewayIp(vm.getId());
         boolean needGateway = true;
-        if (!gatewayIp.equals(nic.getGateway())) {
+        if (gatewayIp != null && !gatewayIp.equals(nic.getGateway())) {
             needGateway = false;
             GuestOSVO guestOS = _guestOSDao.findById(vm.getGuestOSId());
             // Do set dhcp:router option for non-default nic on certain OS(including Windows), and leave other OS unset.
@@ -3072,7 +3086,9 @@
             gatewayIp = "0.0.0.0";
         }
         dhcpCommand.setDefaultRouter(gatewayIp);
+        dhcpCommand.setIp6Gateway(nic.getIp6Gateway());
         dhcpCommand.setDefaultDns(findDefaultDnsIp(vm.getId()));
+        dhcpCommand.setDuid(NetUtils.getDuidLL(nic.getMacAddress()));
 
         dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId()));
         dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java
index fe86a8e..605ffd6 100755
--- a/server/src/com/cloud/network/rules/RulesManagerImpl.java
+++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java
@@ -20,7 +20,7 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.EventDao;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.InsufficientAddressCapacityException;
@@ -276,9 +276,9 @@
                     throw new CloudRuntimeException("Unable to update the state to add for " + newRule);
                 }
                 UserContext.current().setEventDetails("Rule Id: " + newRule.getId());
-                UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_RULE_ADD, newRule.getAccountId(), 
-                        ipAddress.getDataCenterId(), newRule.getId(), null);
-                _usageEventDao.persist(usageEvent);
+                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_RULE_ADD, newRule.getAccountId(),
+                        ipAddress.getDataCenterId(), newRule.getId(), null, PortForwardingRule.class.getName(),
+                        newRule.getUuid());
                 txn.commit();
                 return newRule;
             } catch (Exception e) {
@@ -358,8 +358,8 @@
                 throw new CloudRuntimeException("Unable to update the state to add for " + newRule);
             }
             UserContext.current().setEventDetails("Rule Id: " + newRule.getId());
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_RULE_ADD, newRule.getAccountId(), 0, newRule.getId(), null);
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_RULE_ADD, newRule.getAccountId(), 0, newRule.getId(),
+                    null, FirewallRule.class.getName(), newRule.getUuid());
 
             txn.commit();
             StaticNatRule staticNatRule = new StaticNatRuleImpl(newRule, dstIp);
diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
index b564e3d..5635f52 100755
--- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
+++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java
@@ -16,30 +16,6 @@
 // under the License.
 package com.cloud.network.security;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.api.command.user.securitygroup.*;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.NetworkRulesSystemVmCommand;
 import com.cloud.agent.api.SecurityGroupRulesCmd;
@@ -47,33 +23,20 @@
 import com.cloud.agent.manager.Commands;
 import com.cloud.api.query.dao.SecurityGroupJoinDao;
 import com.cloud.api.query.vo.SecurityGroupJoinVO;
-
-import org.apache.cloudstack.api.command.user.securitygroup.RevokeSecurityGroupEgressCmd;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
-import com.cloud.event.dao.UsageEventDao;
-import com.cloud.exception.AgentUnavailableException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.OperationTimedoutException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.exception.ResourceInUseException;
+import com.cloud.event.UsageEventUtils;
+import com.cloud.exception.*;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkManager;
 import com.cloud.network.NetworkModel;
 import com.cloud.network.security.SecurityGroupWork.Step;
 import com.cloud.network.security.SecurityRule.SecurityRuleType;
-import com.cloud.network.security.dao.SecurityGroupDao;
-import com.cloud.network.security.dao.SecurityGroupRuleDao;
-import com.cloud.network.security.dao.SecurityGroupRulesDao;
-import com.cloud.network.security.dao.SecurityGroupVMMapDao;
-import com.cloud.network.security.dao.SecurityGroupWorkDao;
-import com.cloud.network.security.dao.VmRulesetLogDao;
-import com.cloud.projects.Project.ListProjectResourcesCriteria;
+import com.cloud.network.security.dao.*;
 import com.cloud.projects.ProjectManager;
 import com.cloud.server.ManagementServer;
 import com.cloud.tags.dao.ResourceTagDao;
@@ -85,7 +48,6 @@
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.Pair;
-import com.cloud.utils.Ternary;
 import com.cloud.utils.component.ComponentLocator;
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
@@ -93,26 +55,26 @@
 import com.cloud.utils.db.DB;
 import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.GlobalLock;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.Transaction;
-import com.cloud.utils.db.SearchCriteria.Func;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.fsm.StateListener;
 import com.cloud.utils.net.NetUtils;
-import com.cloud.vm.Nic;
-import com.cloud.vm.NicProfile;
-import com.cloud.vm.UserVmManager;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.*;
 import com.cloud.vm.VirtualMachine.Event;
 import com.cloud.vm.VirtualMachine.State;
-import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
-
 import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.cloudstack.api.command.user.securitygroup.*;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 @Local(value = { SecurityGroupManager.class, SecurityGroupService.class })
 public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityGroupService, Manager, StateListener<State, VirtualMachine.Event, VirtualMachine> {
@@ -159,8 +121,6 @@
     @Inject
     ProjectManager _projectMgr;
     @Inject
-    UsageEventDao _usageEventDao;
-    @Inject
     ResourceTagDao _resourceTagDao;
 
     ScheduledExecutorService _executorPool;
@@ -460,8 +420,9 @@
         // For each group, find the security rules that allow the group
         for (SecurityGroupVMMapVO mapVO : groupsForVm) {// FIXME: use custom sql in the dao
         	//Add usage events for security group assign
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SECURITY_GROUP_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), mapVO.getSecurityGroupId());
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SECURITY_GROUP_ASSIGN, vm.getAccountId(),
+                    vm.getDataCenterIdToDeployIn(), vm.getId(), mapVO.getSecurityGroupId(),
+                    vm.getClass().getName(), vm.getUuid());
 
             List<SecurityGroupRuleVO> allowingRules = _securityGroupRuleDao.listByAllowedSecurityGroupId(mapVO.getSecurityGroupId());
             // For each security rule that allows a group that the vm belongs to, find the group it belongs to
@@ -476,8 +437,9 @@
         // For each group, find the security rules rules that allow the group
         for (SecurityGroupVMMapVO mapVO : groupsForVm) {// FIXME: use custom sql in the dao
         	//Add usage events for security group remove
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SECURITY_GROUP_REMOVE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), mapVO.getSecurityGroupId());
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SECURITY_GROUP_REMOVE,
+                    vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), mapVO.getSecurityGroupId(),
+                    vm.getClass().getName(), vm.getUuid());
 
             List<SecurityGroupRuleVO> allowingRules = _securityGroupRuleDao.listByAllowedSecurityGroupId(mapVO.getSecurityGroupId());
             // For each security rule that allows a group that the vm belongs to, find the group it belongs to
diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java
index fa6bd30..26e882e 100644
--- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java
@@ -1884,7 +1884,7 @@
 
         //2) Create network
         Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, 
-                networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId);
+                networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, null, null);
 
         return guestNetwork;
     }
diff --git a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java
index e360bca..858c362 100755
--- a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java
+++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java
@@ -16,44 +16,24 @@
 // under the License.
 package com.cloud.network.vpn;
 
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.api.command.user.vpn.ListVpnUsersCmd;
-import org.apache.log4j.Logger;
-
-import org.apache.cloudstack.api.command.user.vpn.ListRemoteAccessVpnsCmd;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.domain.DomainVO;
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.AccountLimitException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.NetworkRuleConflictException;
 import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.network.IPAddressVO;
-import com.cloud.network.Network;
+import com.cloud.network.*;
 import com.cloud.network.Network.Service;
-import com.cloud.network.NetworkModel;
-import com.cloud.network.PublicIpAddress;
-import com.cloud.network.RemoteAccessVpn;
-import com.cloud.network.RemoteAccessVpnVO;
-import com.cloud.network.VpnUser;
 import com.cloud.network.VpnUser.State;
-import com.cloud.network.VpnUserVO;
 import com.cloud.network.dao.FirewallRulesDao;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.RemoteAccessVpnDao;
 import com.cloud.network.dao.VpnUserDao;
-import com.cloud.network.element.NetworkElement;
 import com.cloud.network.element.RemoteAccessVPNServiceProvider;
 import com.cloud.network.rules.FirewallManager;
 import com.cloud.network.rules.FirewallRule;
@@ -74,14 +54,19 @@
 import com.cloud.utils.component.ComponentLocator;
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
-import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.Transaction;
 import com.cloud.utils.net.NetUtils;
+import org.apache.cloudstack.api.command.user.vpn.ListRemoteAccessVpnsCmd;
+import org.apache.cloudstack.api.command.user.vpn.ListVpnUsersCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
 
 @Local(value = RemoteAccessVpnService.class)
 public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manager {
@@ -281,8 +266,8 @@
                         for(VpnUserVO user : vpnUsers){
                         	// VPN_USER_REMOVE event is already generated for users in Revoke state
                         	if(user.getState() != VpnUser.State.Revoke){
-                        		UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(), user.getUsername());
-                        		_usageEventDao.persist(usageEvent);
+                                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(),
+                                        0, user.getId(), user.getUsername(), user.getClass().getName(), user.getUuid());
                         	}
                         }
                         if (vpnFwRules != null) {
@@ -333,8 +318,8 @@
         }
         
         VpnUser user = _vpnUsersDao.persist(new VpnUserVO(vpnOwnerId, owner.getDomainId(), username, password));
-        UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0, user.getId(), user.getUsername());
-        _usageEventDao.persist(usageEvent);
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0, user.getId(),
+                user.getUsername(), user.getClass().getName(), user.getUuid());
         txn.commit();
         return user;
     }
@@ -350,8 +335,8 @@
         txn.start();
         user.setState(State.Revoke);
         _vpnUsersDao.update(user.getId(), user);
-        UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(), user.getUsername());
-        _usageEventDao.persist(usageEvent);
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(),
+                user.getUsername(), user.getClass().getName(), user.getUuid());
         txn.commit();
         return true;
     }
@@ -407,8 +392,8 @@
                 List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
                 for(VpnUserVO user : vpnUsers){
                 	if(user.getState() != VpnUser.State.Revoke){
-                		UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0, user.getId(), user.getUsername());
-                		_usageEventDao.persist(usageEvent);
+                        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0,
+                                user.getId(), user.getUsername(), user.getClass().getName(), user.getUuid());
                 	}
                 }
                 txn.commit();
@@ -483,8 +468,8 @@
                     Transaction txn = Transaction.currentTxn();
                     txn.start();            		
                     _vpnUsersDao.remove(user.getId());
-                    UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(), user.getUsername());
-                    _usageEventDao.persist(usageEvent);
+                    UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(),
+                            0, user.getId(), user.getUsername(), user.getClass().getName(), user.getUuid());
                     txn.commit();
                 }
                 s_logger.warn("Failed to apply vpn for user " + user.getUsername() + ", accountId=" + user.getAccountId());
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index 40c5508..0b5c531 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -29,7 +29,6 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,6 +41,7 @@
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 
+import com.cloud.event.ActionEventUtils;
 import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoCmd;
 import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
 import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
@@ -60,7 +60,6 @@
 import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
 import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd;
 import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
-import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.log4j.Logger;
 
@@ -128,7 +127,6 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
 import com.cloud.event.EventVO;
 import com.cloud.event.dao.EventDao;
 import com.cloud.exception.ConcurrentOperationException;
@@ -230,7 +228,6 @@
 import com.cloud.utils.net.NetUtils;
 import com.cloud.utils.ssh.SSHKeysHelper;
 import com.cloud.vm.ConsoleProxyVO;
-import com.cloud.vm.DomainRouterVO;
 import com.cloud.vm.InstanceGroupVO;
 import com.cloud.vm.SecondaryStorageVmVO;
 import com.cloud.vm.UserVmVO;
@@ -2422,12 +2419,12 @@
 
     @Override
     public Long saveStartedEvent(Long userId, Long accountId, String type, String description, long startEventId) {
-        return EventUtils.saveStartedEvent(userId, accountId, type, description, startEventId);
+        return ActionEventUtils.onStartedActionEvent(userId, accountId, type, description, startEventId);
     }
 
     @Override
     public Long saveCompletedEvent(Long userId, Long accountId, String level, String type, String description, long startEventId) {
-        return EventUtils.saveEvent(userId, accountId, level, type, description, startEventId);
+        return ActionEventUtils.onCompletedActionEvent(userId, accountId, level, type, description, startEventId);
     }
 
     @Override
diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java
index 07f4d8a..5a799f9 100755
--- a/server/src/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/com/cloud/storage/StorageManagerImpl.java
@@ -16,68 +16,14 @@
 // under the License.
 package com.cloud.storage;
 
-import java.math.BigDecimal;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.UnknownHostException;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.api.command.admin.storage.*;
-import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
-import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
-import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.AgentManager;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.BackupSnapshotCommand;
-import com.cloud.agent.api.CleanupSnapshotBackupCommand;
-import com.cloud.agent.api.Command;
-import com.cloud.agent.api.CreateStoragePoolCommand;
-import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
-import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
-import com.cloud.agent.api.DeleteStoragePoolCommand;
-import com.cloud.agent.api.ManageSnapshotCommand;
-import com.cloud.agent.api.ModifyStoragePoolAnswer;
-import com.cloud.agent.api.ModifyStoragePoolCommand;
-import com.cloud.agent.api.UpgradeSnapshotCommand;
-import com.cloud.agent.api.storage.CopyVolumeAnswer;
-import com.cloud.agent.api.storage.CopyVolumeCommand;
-import com.cloud.agent.api.storage.CreateAnswer;
-import com.cloud.agent.api.storage.CreateCommand;
-import com.cloud.agent.api.storage.DeleteTemplateCommand;
-import com.cloud.agent.api.storage.DeleteVolumeCommand;
-import com.cloud.agent.api.storage.DestroyCommand;
-import com.cloud.agent.api.storage.ResizeVolumeCommand;
-import com.cloud.agent.api.storage.ResizeVolumeAnswer;
+import com.cloud.agent.api.*;
+import com.cloud.agent.api.storage.*;
 import com.cloud.agent.api.to.StorageFilerTO;
 import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.agent.manager.Commands;
 import com.cloud.alert.AlertManager;
 import com.cloud.api.ApiDBUtils;
-import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd;
 import com.cloud.async.AsyncJobManager;
 import com.cloud.capacity.Capacity;
 import com.cloud.capacity.CapacityManager;
@@ -104,21 +50,9 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.EventDao;
-import com.cloud.event.dao.UsageEventDao;
-import com.cloud.exception.AgentUnavailableException;
-import com.cloud.exception.ConcurrentOperationException;
-import com.cloud.exception.DiscoveryException;
-import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.InsufficientStorageCapacityException;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.OperationTimedoutException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceInUseException;
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.StorageUnavailableException;
+import com.cloud.exception.*;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
@@ -140,32 +74,17 @@
 import com.cloud.storage.Volume.Event;
 import com.cloud.storage.Volume.Type;
 import com.cloud.storage.allocator.StoragePoolAllocator;
-import com.cloud.storage.dao.DiskOfferingDao;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.storage.dao.SnapshotPolicyDao;
-import com.cloud.storage.dao.StoragePoolDao;
-import com.cloud.storage.dao.StoragePoolHostDao;
-import com.cloud.storage.dao.StoragePoolWorkDao;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VMTemplateHostDao;
-import com.cloud.storage.dao.VMTemplatePoolDao;
-import com.cloud.storage.dao.VMTemplateS3Dao;
-import com.cloud.storage.dao.VMTemplateSwiftDao;
-import com.cloud.storage.dao.VolumeDao;
-import com.cloud.storage.dao.VolumeHostDao;
+import com.cloud.storage.dao.*;
 import com.cloud.storage.download.DownloadMonitor;
 import com.cloud.storage.listener.StoragePoolMonitor;
+import com.cloud.storage.listener.VolumeStateListener;
 import com.cloud.storage.s3.S3Manager;
 import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.storage.snapshot.SnapshotManager;
 import com.cloud.storage.snapshot.SnapshotScheduler;
 import com.cloud.tags.dao.ResourceTagDao;
 import com.cloud.template.TemplateManager;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.ResourceLimitService;
-import com.cloud.user.User;
-import com.cloud.user.UserContext;
+import com.cloud.user.*;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.user.dao.UserDao;
 import com.cloud.uservm.UserVm;
@@ -178,36 +97,35 @@
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
 import com.cloud.utils.concurrency.NamedThreadFactory;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.GenericSearchBuilder;
-import com.cloud.utils.db.GlobalLock;
-import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.JoinBuilder.JoinType;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.exception.ExecutionException;
 import com.cloud.utils.fsm.NoTransitionException;
 import com.cloud.utils.fsm.StateMachine2;
-import com.cloud.vm.ConsoleProxyVO;
-import com.cloud.vm.DiskProfile;
-import com.cloud.vm.DomainRouterVO;
-import com.cloud.vm.SecondaryStorageVmVO;
-import com.cloud.vm.UserVmManager;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.*;
 import com.cloud.vm.VirtualMachine.State;
-import com.cloud.vm.VirtualMachineManager;
-import com.cloud.vm.VirtualMachineProfile;
-import com.cloud.vm.VirtualMachineProfileImpl;
-import com.cloud.vm.dao.ConsoleProxyDao;
-import com.cloud.vm.dao.DomainRouterDao;
-import com.cloud.vm.dao.SecondaryStorageVmDao;
-import com.cloud.vm.dao.UserVmDao;
-import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.dao.*;
+import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd;
+import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd;
+import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd;
+import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
+import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.math.BigDecimal;
+import java.net.*;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 @Local(value = { StorageManager.class, StorageService.class })
 public class StorageManagerImpl implements StorageManager, Manager, ClusterManagerListener {
@@ -301,8 +219,6 @@
     @Inject
     protected ClusterDao _clusterDao;
     @Inject
-    protected UsageEventDao _usageEventDao;
-    @Inject
     protected VirtualMachineManager _vmMgr;
     @Inject
     protected DomainRouterDao _domrDao;
@@ -653,9 +569,9 @@
         Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(volume, snapshot);
         if (volumeDetails != null) {
             createdVolume = volumeDetails.first();
-        	UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(), createdVolume.getName(),
-        			                                   createdVolume.getDiskOfferingId(), null, createdVolume.getSize());
-        	_usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(),
+                    createdVolume.getDataCenterId(), createdVolume.getId(), createdVolume.getName(), createdVolume.getDiskOfferingId(),
+                    null, createdVolume.getSize(), Volume.class.getName(), createdVolume.getUuid());
         }
         return createdVolume;
     }
@@ -775,8 +691,9 @@
         volume.setPoolId(destPool.getId());
         volume.setPodId(destPool.getPodId());
         stateTransitTo(volume, Event.CopySucceeded);
-        UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize());
-        _usageEventDao.persist(usageEvent);
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(),
+                volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(),
+                null, volume.getSize(), Volume.class.getName(), volume.getUuid());
         _volumeHostDao.remove(volumeHostVO.getId());
     	txn.commit();
 		return volume;
@@ -1050,6 +967,9 @@
         LocalStorageSearch.join("poolHost", storageHostSearch, storageHostSearch.entity().getPoolId(), LocalStorageSearch.entity().getId(), JoinBuilder.JoinType.INNER);
         LocalStorageSearch.and("type", LocalStorageSearch.entity().getPoolType(), SearchCriteria.Op.IN);
         LocalStorageSearch.done();
+
+        Volume.State.getStateMachine().registerListener( new VolumeStateListener());
+
         return true;
     }
 
@@ -1967,8 +1887,8 @@
                 throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId);
             }
 
-            if (snapshotCheck.getStatus() != Snapshot.Status.BackedUp) {
-                throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.Status.BackedUp + " state yet and can't be used for volume creation");
+            if (snapshotCheck.getState() != Snapshot.State.BackedUp) {
+                throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.State.BackedUp + " state yet and can't be used for volume creation");
             }
 
             diskOfferingId = snapshotCheck.getDiskOfferingId();
@@ -2058,9 +1978,10 @@
 
         volume = _volsDao.persist(volume);
         if(cmd.getSnapshotId() == null){
-        	//for volume created from snapshot, create usage event after volume creation
-        	UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size);
-        	_usageEventDao.persist(usageEvent);
+            //for volume created from snapshot, create usage event after volume creation
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(),
+                    volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size,
+                    Volume.class.getName(), volume.getUuid());
         }
 
         UserContext.current().setEventDetails("Volume Id: " + volume.getId());
@@ -2305,8 +2226,9 @@
             // Decrement the resource count for volumes belonging user VM's only
             _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume);
             // Log usage event for volumes belonging user VM's only
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName());
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(),
+                    volume.getDataCenterId(), volume.getId(), volume.getName(),
+                    Volume.class.getName(), volume.getUuid());
         }
 
         try {
@@ -2471,7 +2393,7 @@
                     }
 
                     // remove snapshots in Error state
-                    List<SnapshotVO> snapshots = _snapshotDao.listAllByStatus(Snapshot.Status.Error);
+                    List<SnapshotVO> snapshots = _snapshotDao.listAllByStatus(Snapshot.State.Error);
                     for (SnapshotVO snapshotVO : snapshots) {
                         try{
                             _snapshotDao.expunge(snapshotVO.getId());
@@ -3140,10 +3062,9 @@
 
         // Save usage event and update resource count for user vm volumes
         if (vm instanceof UserVm) {
-
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offering.getId(), null, size);
-            _usageEventDao.persist(usageEvent);
-
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(),
+                    vol.getDataCenterId(), vol.getId(), vol.getName(), offering.getId(), null, size,
+                    Volume.class.getName(), vol.getUuid());
             _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume);
         }
         return toDiskProfile(vol, offering);
@@ -3204,9 +3125,9 @@
                 offeringId = offering.getId();
             }
 
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offeringId, template.getId(),
-                    vol.getSize());
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(),
+                    vol.getDataCenterId(), vol.getId(), vol.getName(), offeringId, template.getId(),
+                    vol.getSize(), Volume.class.getName(), vol.getUuid());
 
             _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume);
         }
diff --git a/server/src/com/cloud/storage/dao/SnapshotDao.java b/server/src/com/cloud/storage/dao/SnapshotDao.java
index b32d278..3b961f6 100644
--- a/server/src/com/cloud/storage/dao/SnapshotDao.java
+++ b/server/src/com/cloud/storage/dao/SnapshotDao.java
@@ -16,15 +16,16 @@
 // under the License.
 package com.cloud.storage.dao;
 
-import java.util.List;
-
 import com.cloud.storage.Snapshot;
 import com.cloud.storage.Snapshot.Type;
 import com.cloud.storage.SnapshotVO;
 import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.GenericDao;
+import com.cloud.utils.fsm.StateDao;
 
-public interface SnapshotDao extends GenericDao<SnapshotVO, Long> {
+import java.util.List;
+
+public interface SnapshotDao extends GenericDao<SnapshotVO, Long>, StateDao<Snapshot.State, Snapshot.Event, Snapshot> {
 	List<SnapshotVO> listByVolumeId(long volumeId);
 	List<SnapshotVO> listByVolumeId(Filter filter, long volumeId);
 	SnapshotVO findNextSnapshot(long parentSnapId);
@@ -39,7 +40,7 @@
     List<SnapshotVO> listByHostId(Filter filter, long hostId);
     List<SnapshotVO> listByHostId(long hostId);
     public Long countSnapshotsForAccount(long accountId);
-	List<SnapshotVO> listByInstanceId(long instanceId, Snapshot.Status... status);
-	List<SnapshotVO> listByStatus(long volumeId, Snapshot.Status... status);
-    List<SnapshotVO> listAllByStatus(Snapshot.Status... status);
+	List<SnapshotVO> listByInstanceId(long instanceId, Snapshot.State... status);
+	List<SnapshotVO> listByStatus(long volumeId, Snapshot.State... status);
+    List<SnapshotVO> listAllByStatus(Snapshot.State... status);
 }
diff --git a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java
index 65e2f5f..3fe1aba 100644
--- a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java
+++ b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java
@@ -16,33 +16,27 @@
 // under the License.
 package com.cloud.storage.dao;
 
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.List;
-
-import javax.ejb.Local;
-
-import org.apache.log4j.Logger;
-
 import com.cloud.server.ResourceTag.TaggedResourceType;
 import com.cloud.storage.Snapshot;
+import com.cloud.storage.Snapshot.Event;
+import com.cloud.storage.Snapshot.State;
 import com.cloud.storage.Snapshot.Type;
 import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeVO;
 import com.cloud.tags.dao.ResourceTagsDaoImpl;
 import com.cloud.utils.component.ComponentLocator;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
-import com.cloud.utils.db.GenericDaoBase;
-import com.cloud.utils.db.GenericSearchBuilder;
+import com.cloud.utils.db.*;
 import com.cloud.utils.db.JoinBuilder.JoinType;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Func;
-import com.cloud.utils.db.Transaction;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.dao.VMInstanceDaoImpl;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.List;
 
 @Local (value={SnapshotDao.class})
 public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements SnapshotDao {
@@ -113,7 +107,7 @@
     public List<SnapshotVO> listByHostId(Filter filter, long hostId ) {
         SearchCriteria<SnapshotVO> sc = HostIdSearch.create();
         sc.setParameters("hostId", hostId);
-        sc.setParameters("status", Snapshot.Status.BackedUp);
+        sc.setParameters("status", Snapshot.State.BackedUp);
         return listBy(sc, filter);
     }
         
@@ -145,7 +139,7 @@
         
         HostIdSearch = createSearchBuilder();
         HostIdSearch.and("hostId", HostIdSearch.entity().getSecHostId(), SearchCriteria.Op.EQ);
-        HostIdSearch.and("status", HostIdSearch.entity().getStatus(), SearchCriteria.Op.EQ);
+        HostIdSearch.and("status", HostIdSearch.entity().getState(), SearchCriteria.Op.EQ);
         HostIdSearch.done();
         
         VolumeIdTypeSearch = createSearchBuilder();
@@ -172,7 +166,7 @@
         
         StatusSearch = createSearchBuilder();
         StatusSearch.and("volumeId", StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
-        StatusSearch.and("status", StatusSearch.entity().getStatus(), SearchCriteria.Op.IN);
+        StatusSearch.and("status", StatusSearch.entity().getState(), SearchCriteria.Op.IN);
         StatusSearch.done();
         
         CountSnapshotsByAccount = createSearchBuilder(Long.class);
@@ -182,7 +176,7 @@
         CountSnapshotsByAccount.done();
         
     	InstanceIdSearch = createSearchBuilder();
-    	InstanceIdSearch.and("status", InstanceIdSearch.entity().getStatus(), SearchCriteria.Op.IN);
+        InstanceIdSearch.and("status", InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN);
     	
     	SearchBuilder<VMInstanceVO> instanceSearch = _instanceDao.createSearchBuilder();
     	instanceSearch.and("instanceId", instanceSearch.entity().getId(), SearchCriteria.Op.EQ);
@@ -274,7 +268,7 @@
     }
     
     @Override
-	public List<SnapshotVO> listByInstanceId(long instanceId, Snapshot.Status... status) {
+	public List<SnapshotVO> listByInstanceId(long instanceId, Snapshot.State... status) {
     	SearchCriteria<SnapshotVO> sc = this.InstanceIdSearch.create();
     	
     	if (status != null && status.length != 0) {
@@ -287,7 +281,7 @@
     }
     
     @Override
-    public List<SnapshotVO> listByStatus(long volumeId, Snapshot.Status... status) {
+    public List<SnapshotVO> listByStatus(long volumeId, Snapshot.State... status) {
     	SearchCriteria<SnapshotVO> sc = this.StatusSearch.create();
     	sc.setParameters("volumeId", volumeId);
     	sc.setParameters("status", (Object[])status);
@@ -309,9 +303,20 @@
     }
     
     @Override
-    public List<SnapshotVO> listAllByStatus(Snapshot.Status... status) {
+    public List<SnapshotVO> listAllByStatus(Snapshot.State... status) {
         SearchCriteria<SnapshotVO> sc = this.StatusSearch.create();
         sc.setParameters("status", (Object[])status);
         return listBy(sc, null);
     }
+
+    @Override
+    public boolean updateState(State currentState, Event event, State nextState, Snapshot snapshot, Object data) {
+        Transaction txn = Transaction.currentTxn();
+        txn.start();
+        SnapshotVO snapshotVO = (SnapshotVO)snapshot;
+        snapshotVO.setStatus(nextState);
+        super.update(snapshotVO.getId(), snapshotVO);
+        txn.commit();
+        return true;
+    }
 }
diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java
index 2736777..cf51567 100755
--- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java
+++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java
@@ -16,36 +16,14 @@
 // under the License.
 package com.cloud.storage.download;
 
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Timer;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.ejb.Local;
-
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.Listener;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
-import com.cloud.agent.api.storage.DeleteTemplateCommand;
-import com.cloud.agent.api.storage.DeleteVolumeCommand;
-import com.cloud.agent.api.storage.DownloadCommand;
-import com.cloud.agent.api.storage.ListVolumeAnswer;
-import com.cloud.agent.api.storage.ListVolumeCommand;
+import com.cloud.agent.api.storage.*;
 import com.cloud.agent.api.storage.DownloadCommand.Proxy;
 import com.cloud.agent.api.storage.DownloadCommand.ResourceType;
-import com.cloud.agent.api.storage.DownloadProgressCommand;
 import com.cloud.agent.api.storage.DownloadProgressCommand.RequestType;
-import com.cloud.agent.api.storage.ListTemplateAnswer;
-import com.cloud.agent.api.storage.ListTemplateCommand;
 import com.cloud.agent.manager.Commands;
 import com.cloud.alert.AlertManager;
 import com.cloud.configuration.Config;
@@ -54,8 +32,7 @@
 import com.cloud.dc.dao.ClusterDao;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
-import com.cloud.event.dao.UsageEventDao;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.AgentUnavailableException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.StorageUnavailableException;
@@ -65,26 +42,9 @@
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.resource.ResourceManager;
 import com.cloud.storage.Storage.ImageFormat;
-import com.cloud.storage.StorageManager;
-import com.cloud.storage.SwiftVO;
-import com.cloud.storage.VMTemplateHostVO;
-import com.cloud.storage.VMTemplateStorageResourceAssoc;
-import com.cloud.storage.Volume;
-import com.cloud.storage.VolumeHostVO;
-import com.cloud.storage.VolumeVO;
+import com.cloud.storage.*;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
-import com.cloud.storage.Volume.Event;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.VMTemplateZoneVO;
-import com.cloud.storage.dao.StoragePoolHostDao;
-import com.cloud.storage.dao.SwiftDao;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VMTemplateHostDao;
-import com.cloud.storage.dao.VMTemplatePoolDao;
-import com.cloud.storage.dao.VMTemplateSwiftDao;
-import com.cloud.storage.dao.VMTemplateZoneDao;
-import com.cloud.storage.dao.VolumeDao;
-import com.cloud.storage.dao.VolumeHostDao;
+import com.cloud.storage.dao.*;
 import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.storage.swift.SwiftManager;
 import com.cloud.storage.template.TemplateConstants;
@@ -92,19 +52,21 @@
 import com.cloud.user.Account;
 import com.cloud.user.ResourceLimitService;
 import com.cloud.utils.component.Inject;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.*;
 import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.utils.fsm.NoTransitionException;
 import com.cloud.vm.SecondaryStorageVm;
 import com.cloud.vm.SecondaryStorageVmVO;
 import com.cloud.vm.UserVmManager;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.SecondaryStorageVmDao;
 import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
 
 @Local(value={DownloadMonitor.class})
@@ -147,11 +109,6 @@
     ConfigurationDao _configDao;
     @Inject
     UserVmManager _vmMgr;
-
-    
-    @Inject 
-    private UsageEventDao _usageEventDao;
-    
     @Inject
     private ClusterDao _clusterDao;
     @Inject
@@ -517,8 +474,9 @@
                 eventType = EventTypes.EVENT_ISO_CREATE;
             }
             if(template.getAccountId() != Account.ACCOUNT_ID_SYSTEM){
-                UsageEventVO usageEvent = new UsageEventVO(eventType, template.getAccountId(), host.getDataCenterId(), template.getId(), template.getName(), null, template.getSourceTemplateId() , size);
-                _usageEventDao.persist(usageEvent);
+                UsageEventUtils.publishUsageEvent(eventType, template.getAccountId(), host.getDataCenterId(),
+                        template.getId(), template.getName(), null, template.getSourceTemplateId(), size,
+                        template.getClass().getName(), template.getUuid());
             }
         }
         txn.commit();
@@ -550,8 +508,8 @@
             }
             String eventType = EventTypes.EVENT_VOLUME_UPLOAD;            
             if(volume.getAccountId() != Account.ACCOUNT_ID_SYSTEM){
-               UsageEventVO usageEvent = new UsageEventVO(eventType, volume.getAccountId(), host.getDataCenterId(), volume.getId(), volume.getName(), null, 0l , size);
-               _usageEventDao.persist(usageEvent);
+                UsageEventUtils.publishUsageEvent(eventType, volume.getAccountId(), host.getDataCenterId(),
+                        volume.getId(), volume.getName(), null, 0l, size, volume.getClass().getName(), volume.getUuid());
             }
         }else if (dnldStatus == Status.DOWNLOAD_ERROR || dnldStatus == Status.ABANDONED || dnldStatus == Status.UNKNOWN){
             //Decrement the volume count
diff --git a/server/src/com/cloud/storage/listener/SnapshotStateListener.java b/server/src/com/cloud/storage/listener/SnapshotStateListener.java
new file mode 100644
index 0000000..2b19887
--- /dev/null
+++ b/server/src/com/cloud/storage/listener/SnapshotStateListener.java
@@ -0,0 +1,85 @@
+package com.cloud.storage.listener;
+
+import com.cloud.event.EventCategory;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.Snapshot.Event;
+import com.cloud.storage.Snapshot.State;
+import com.cloud.server.ManagementServer;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.fsm.StateListener;
+import org.apache.cloudstack.framework.events.EventBus;
+import org.apache.cloudstack.framework.events.EventBusException;
+import org.apache.log4j.Logger;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SnapshotStateListener implements StateListener<State, Event, Snapshot> {
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
+    private static final Logger s_logger = Logger.getLogger(VolumeStateListener.class);
+
+    public SnapshotStateListener() {
+
+    }
+
+    @Override
+    public boolean preStateTransitionEvent(State oldState, Event event, State newState, Snapshot vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
+        return true;
+    }
+
+    @Override
+    public boolean postStateTransitionEvent(State oldState, Event event, State newState, Snapshot vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState);
+        return true;
+    }
+
+    private void pubishOnEventBus(String event, String status, Snapshot vo, State oldState, State newState) {
+
+        if (_eventBus == null) {
+            return;  // no provider is configured to provide events bus, so just return
+        }
+
+        String resourceName = getEntityFromClassName(Snapshot.class.getName());
+        org.apache.cloudstack.framework.events.Event eventMsg =  new org.apache.cloudstack.framework.events.Event(
+                ManagementServer.Name,
+                EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(),
+                event,
+                resourceName,
+                vo.getUuid());
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        eventDescription.put("resource", resourceName);
+        eventDescription.put("id", vo.getUuid());
+        eventDescription.put("old-state", oldState.name());
+        eventDescription.put("new-state", newState.name());
+        eventMsg.setDescription(eventDescription);
+        try {
+            _eventBus.publish(eventMsg);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to publish state change event on the the event bus.");
+        }
+    }
+
+    private String getEntityFromClassName(String entityClassName) {
+        int index = entityClassName.lastIndexOf(".");
+        String entityName = entityClassName;
+        if (index != -1) {
+            entityName = entityClassName.substring(index+1);
+        }
+        return entityName;
+    }
+}
diff --git a/server/src/com/cloud/storage/listener/VolumeStateListener.java b/server/src/com/cloud/storage/listener/VolumeStateListener.java
new file mode 100644
index 0000000..c460016
--- /dev/null
+++ b/server/src/com/cloud/storage/listener/VolumeStateListener.java
@@ -0,0 +1,85 @@
+package com.cloud.storage.listener;
+
+import com.cloud.event.EventCategory;
+import com.cloud.storage.Volume;
+import com.cloud.storage.Volume.Event;
+import com.cloud.storage.Volume.State;
+import com.cloud.server.ManagementServer;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.fsm.StateListener;
+import org.apache.cloudstack.framework.events.EventBus;
+import org.apache.cloudstack.framework.events.EventBusException;
+import org.apache.log4j.Logger;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VolumeStateListener implements StateListener<State, Event, Volume> {
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
+    private static final Logger s_logger = Logger.getLogger(VolumeStateListener.class);
+
+    public VolumeStateListener() {
+
+    }
+
+    @Override
+    public boolean preStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
+        return true;
+    }
+
+    @Override
+    public boolean postStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState);
+        return true;
+    }
+
+    private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) {
+
+        if (_eventBus == null) {
+            return;  // no provider is configured to provide events bus, so just return
+        }
+
+        String resourceName = getEntityFromClassName(Volume.class.getName());
+        org.apache.cloudstack.framework.events.Event eventMsg =  new org.apache.cloudstack.framework.events.Event(
+                ManagementServer.Name,
+                EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(),
+                event,
+                resourceName,
+                vo.getUuid());
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        eventDescription.put("resource", resourceName);
+        eventDescription.put("id", vo.getUuid());
+        eventDescription.put("old-state", oldState.name());
+        eventDescription.put("new-state", newState.name());
+        eventMsg.setDescription(eventDescription);
+        try {
+            _eventBus.publish(eventMsg);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to state change event on the the event bus.");
+        }
+    }
+
+    private String getEntityFromClassName(String entityClassName) {
+        int index = entityClassName.lastIndexOf(".");
+        String entityName = entityClassName;
+        if (index != -1) {
+            entityName = entityClassName.substring(index+1);
+        }
+        return entityName;
+    }
+}
diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
index 15d8c53..66eb8e1 100755
--- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
+++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
@@ -16,37 +16,12 @@
 // under the License.
 package com.cloud.storage.snapshot;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd;
-import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd;
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.AgentManager;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.BackupSnapshotAnswer;
-import com.cloud.agent.api.BackupSnapshotCommand;
-import com.cloud.agent.api.Command;
-import com.cloud.agent.api.DeleteSnapshotBackupCommand;
-import com.cloud.agent.api.DeleteSnapshotsDirCommand;
-import com.cloud.agent.api.DownloadSnapshotFromS3Command;
-import com.cloud.agent.api.ManageSnapshotAnswer;
-import com.cloud.agent.api.ManageSnapshotCommand;
-import com.cloud.agent.api.downloadSnapshotFromSwiftCommand;
+import com.cloud.agent.api.*;
 import com.cloud.agent.api.to.S3TO;
 import com.cloud.agent.api.to.SwiftTO;
 import com.cloud.alert.AlertManager;
-import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd;
 import com.cloud.api.commands.ListRecurringSnapshotScheduleCmd;
-import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.configuration.dao.ConfigurationDao;
@@ -55,11 +30,7 @@
 import com.cloud.dc.dao.ClusterDao;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.domain.dao.DomainDao;
-import com.cloud.event.ActionEvent;
-import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
-import com.cloud.event.EventVO;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.*;
 import com.cloud.event.dao.EventDao;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.InvalidParameterValueException;
@@ -69,44 +40,21 @@
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.network.PhysicalNetworkTrafficType;
 import com.cloud.org.Grouping;
 import com.cloud.projects.Project.ListProjectResourcesCriteria;
 import com.cloud.resource.ResourceManager;
 import com.cloud.server.ResourceTag.TaggedResourceType;
-import com.cloud.storage.Snapshot;
-import com.cloud.storage.Snapshot.Status;
+import com.cloud.storage.*;
 import com.cloud.storage.Snapshot.Type;
-import com.cloud.storage.SnapshotPolicyVO;
-import com.cloud.storage.SnapshotScheduleVO;
-import com.cloud.storage.SnapshotVO;
-import com.cloud.storage.Storage;
 import com.cloud.storage.Storage.StoragePoolType;
-import com.cloud.storage.StorageManager;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.StoragePoolVO;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.Volume;
-import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.DiskOfferingDao;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.storage.dao.SnapshotPolicyDao;
-import com.cloud.storage.dao.SnapshotScheduleDao;
-import com.cloud.storage.dao.StoragePoolDao;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.*;
+import com.cloud.storage.listener.SnapshotStateListener;
 import com.cloud.storage.s3.S3Manager;
 import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.storage.swift.SwiftManager;
 import com.cloud.tags.ResourceTagVO;
 import com.cloud.tags.dao.ResourceTagDao;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.AccountVO;
-import com.cloud.user.DomainManager;
-import com.cloud.user.ResourceLimitService;
-import com.cloud.user.User;
-import com.cloud.user.UserContext;
+import com.cloud.user.*;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.DateUtil;
 import com.cloud.utils.DateUtil.IntervalType;
@@ -116,18 +64,24 @@
 import com.cloud.utils.component.ComponentLocator;
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
-import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.*;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.fsm.NoTransitionException;
+import com.cloud.utils.fsm.StateMachine2;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.UserVmDao;
+import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd;
+import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd;
+import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd;
+import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.util.*;
 
 @Local(value = { SnapshotManager.class, SnapshotService.class })
 public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Manager {
@@ -195,6 +149,8 @@
     private int _deltaSnapshotMax;
     private int _backupsnapshotwait;
 
+    private StateMachine2<Snapshot.State, Snapshot.Event, Snapshot> _snapshotFsm;
+
     protected SearchBuilder<SnapshotVO> PolicySnapshotSearch;
     protected SearchBuilder<SnapshotPolicyVO> PoliciesForSnapSearch;
 
@@ -259,6 +215,13 @@
         if (snapshot == null) {
             throw new CloudRuntimeException("Can not find snapshot " + snapshotId);
         }
+
+        try {
+            stateTransitTo(snapshot, Snapshot.Event.CreateRequested);
+        } catch (NoTransitionException nte) {
+            s_logger.debug("Failed to update snapshot state due to " + nte.getMessage());
+        }
+
         // Send a ManageSnapshotCommand to the agent
         String vmName = _storageMgr.getVmNameOnVolume(volume);
         long volumeId = volume.getId();
@@ -289,14 +252,16 @@
             if (preSnapshotPath != null && preSnapshotPath.equals(answer.getSnapshotPath())) {
                 // empty snapshot
                 s_logger.debug("CreateSnapshot: this is empty snapshot ");
-                snapshot.setPath(preSnapshotPath);
-                snapshot.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId());
-                snapshot.setSwiftId(preSnapshotVO.getSwiftId());
-
-                snapshot.setStatus(Snapshot.Status.BackedUp);
-                snapshot.setPrevSnapshotId(preId);
-                snapshot.setSecHostId(preSnapshotVO.getSecHostId());
-                _snapshotDao.update(snapshotId, snapshot);
+                try {
+                    snapshot.setPath(preSnapshotPath);
+                    snapshot.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId());
+                    snapshot.setSwiftId(preSnapshotVO.getSwiftId());
+                    snapshot.setPrevSnapshotId(preId);
+                    snapshot.setSecHostId(preSnapshotVO.getSecHostId());
+                    stateTransitTo(snapshot, Snapshot.Event.OperationNotPerformed);
+                }  catch (NoTransitionException nte) {
+                    s_logger.debug("CreateSnapshot: failed to update state of snapshot due to " + nte.getMessage());
+                }
             } else {
                 long preSnapshotId = 0;
 
@@ -346,6 +311,11 @@
             if (answer != null) {
                 s_logger.error(answer.getDetails());
             }
+            try {
+                stateTransitTo(snapshot, Snapshot.Event.OperationFailed);
+            } catch (NoTransitionException nte) {
+                s_logger.debug("Failed to update snapshot state due to " + nte.getMessage());
+            }
             throw new CloudRuntimeException("Creating snapshot for volume " + volumeId + " on primary storage failed.");
         }
 
@@ -410,7 +380,7 @@
                     }
 
                     if(userVm.getHypervisorType() == HypervisorType.VMware || userVm.getHypervisorType() == HypervisorType.KVM) {
-                        List<SnapshotVO> activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.Status.Creating,  Snapshot.Status.CreatedOnPrimary,  Snapshot.Status.BackingUp);
+                        List<SnapshotVO> activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.State.Creating,  Snapshot.State.CreatedOnPrimary,  Snapshot.State.BackingUp);
                         if(activeSnapshots.size() > 1)
                             throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later");
                     }
@@ -419,19 +389,15 @@
 
             snapshot = createSnapshotOnPrimary(volume, policyId, snapshotId);
             if (snapshot != null) {
-                if (snapshot.getStatus() == Snapshot.Status.CreatedOnPrimary) {
+                if (snapshot.getState() == Snapshot.State.CreatedOnPrimary) {
                     backedUp = backupSnapshotToSecondaryStorage(snapshot);
-                } else if (snapshot.getStatus() == Snapshot.Status.BackedUp) {
+                } else if (snapshot.getState() == Snapshot.State.BackedUp) {
                     // For empty snapshot we set status to BackedUp in createSnapshotOnPrimary
                     backedUp = true;
                 } else {
-                    snapshot.setStatus(Status.Error);
-                    _snapshotDao.update(snapshot.getId(), snapshot);
                     throw new CloudRuntimeException("Failed to create snapshot: " + snapshot + " on primary storage");
                 }
                 if (!backedUp) {
-                    snapshot.setStatus(Status.Error);
-                    _snapshotDao.update(snapshot.getId(), snapshot);
                     throw new CloudRuntimeException("Created snapshot: " + snapshot + " on primary but failed to backup on secondary");
                 }
             } else {
@@ -444,23 +410,15 @@
                 //Check if the snapshot was removed while backingUp. If yes, do not log snapshot create usage event
                 SnapshotVO freshSnapshot = _snapshotDao.findById(snapshot.getId());
                 if ((freshSnapshot != null) && backedUp) {
-                    UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null,
-                            volume.getSize());
-                    _usageEventDao.persist(usageEvent);
+                    UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(),
+                            snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null,
+                            volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid());
                 }
                 if( !backedUp ) {
 
-                    snapshot.setStatus(Status.Error);
-                    _snapshotDao.update(snapshot.getId(), snapshot);
                 } else {
                     _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot);
                 }
-            } else {
-            	snapshot = _snapshotDao.findById(snapshotId);
-            	if (snapshot != null) {
-            		snapshot.setStatus(Status.Error);
-            		_snapshotDao.update(snapshotId, snapshot);
-            	}
             }
 
             /*
@@ -478,9 +436,12 @@
     private SnapshotVO updateDBOnCreate(Long id, String snapshotPath, long preSnapshotId) {
         SnapshotVO createdSnapshot = _snapshotDao.findByIdIncludingRemoved(id);
         createdSnapshot.setPath(snapshotPath);
-        createdSnapshot.setStatus(Snapshot.Status.CreatedOnPrimary);
         createdSnapshot.setPrevSnapshotId(preSnapshotId);
-        _snapshotDao.update(id, createdSnapshot);
+        try {
+            stateTransitTo(createdSnapshot, Snapshot.Event.OperationSucceeded);
+        } catch (NoTransitionException nte) {
+            s_logger.debug("Faile to update state of snapshot due to " + nte.getMessage());
+        }
         return createdSnapshot;
     }
 
@@ -622,9 +583,11 @@
             throw new CloudRuntimeException("Can not acquire lock for snapshot: " + ss);
         }
         try {
-
-            snapshot.setStatus(Snapshot.Status.BackingUp);
-            _snapshotDao.update(snapshot.getId(), snapshot);
+            try {
+                stateTransitTo(snapshot, Snapshot.Event.BackupToSecondary);
+            } catch (NoTransitionException nte) {
+                s_logger.debug("Failed to update the state of snapshot while backing up snapshot");
+            }
 
             long volumeId = snapshot.getVolumeId();
             VolumeVO volume = _volsDao.lockRow(volumeId, true);
@@ -705,10 +668,18 @@
                 if (answer.isFull()) {
                     snapshot.setPrevSnapshotId(0);
                 }
-                snapshot.setStatus(Snapshot.Status.BackedUp);
-                _snapshotDao.update(snapshotId, snapshot);
+                try {
+                    stateTransitTo(snapshot, Snapshot.Event.OperationSucceeded);
+                } catch (NoTransitionException nte) {
+                    s_logger.debug("Failed to update the state of snapshot while backing up snapshot");
+                }
 
             } else {
+                try {
+                    stateTransitTo(snapshot, Snapshot.Event.OperationFailed);
+                } catch (NoTransitionException nte) {
+                    s_logger.debug("Failed to update the state of snapshot while backing up snapshot");
+                }
                 s_logger.warn("Failed to back up snapshot on secondary storage, deleting the record from the DB");
                 _snapshotDao.remove(snapshotId);
             }
@@ -766,8 +737,8 @@
             long oldSnapId = oldestSnapshot.getId();
             s_logger.debug("Max snaps: " + policy.getMaxSnaps() + " exceeded for snapshot policy with Id: " + policyId + ". Deleting oldest snapshot: " + oldSnapId);
             if(deleteSnapshotInternal(oldSnapId)){
-            	//log Snapshot delete event
-            	EventUtils.saveEvent(User.UID_SYSTEM, oldestSnapshot.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_SNAPSHOT_DELETE, "Successfully deleted oldest snapshot: " + oldSnapId, 0);
+                //log Snapshot delete event
+                ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, oldestSnapshot.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_SNAPSHOT_DELETE, "Successfully deleted oldest snapshot: " + oldSnapId, 0);
             }
             snaps.remove(oldestSnapshot);
         }
@@ -787,7 +758,7 @@
 
         _accountMgr.checkAccess(caller, null, true, snapshotCheck);
 
-        if( !Status.BackedUp.equals(snapshotCheck.getStatus() ) ) {
+        if( !Snapshot.State.BackedUp.equals(snapshotCheck.getState() ) ) {
             throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is not in BackedUp Status");
         }
 
@@ -812,9 +783,10 @@
         Transaction txn = Transaction.currentTxn();
         txn.start();
         _snapshotDao.remove(snapshotId);
-        if (snapshot.getStatus() == Snapshot.Status.BackedUp) {
-        	UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SNAPSHOT_DELETE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, 0L);
-        	_usageEventDao.persist(usageEvent);
+        if (snapshot.getState() == Snapshot.State.BackedUp) {
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshot.getAccountId(),
+                    snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, 0L,
+                    snapshot.getClass().getName(), snapshot.getUuid());
         }
         _resourceLimitMgr.decrementResourceCount(snapshot.getAccountId(), ResourceType.snapshot);
         txn.commit();
@@ -966,7 +938,7 @@
         SearchBuilder<SnapshotVO> sb = _snapshotDao.createSearchBuilder();
         _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
 
-        sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ);
+        sb.and("status", sb.entity().getState(), SearchCriteria.Op.EQ);
         sb.and("volumeId", sb.entity().getVolumeId(), SearchCriteria.Op.EQ);
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
         sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
@@ -1115,9 +1087,9 @@
                     }
 
                     // Log event after successful deletion
-                    UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SNAPSHOT_DELETE, snapshot.getAccountId(), volume.getDataCenterId(), snapshot.getId(), snapshot.getName(), null, null,
-                            volume.getSize());
-                    _usageEventDao.persist(usageEvent);
+                    UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshot.getAccountId(),
+                            volume.getDataCenterId(), snapshot.getId(), snapshot.getName(), null, null,
+                            volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid());
                 }
             }
         }
@@ -1452,6 +1424,9 @@
 
         s_logger.info("Snapshot Manager is configured.");
 
+        _snapshotFsm = Snapshot.State.getStateMachine();
+        _snapshotFsm.registerListener(new SnapshotStateListener());
+
         return true;
     }
 
@@ -1538,11 +1513,15 @@
 
     @Override
     public boolean canOperateOnVolume(VolumeVO volume) {
-    	List<SnapshotVO> snapshots = _snapshotDao.listByStatus(volume.getId(), Status.Creating, Status.CreatedOnPrimary, Status.BackingUp);
+        List<SnapshotVO> snapshots = _snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating,
+                Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp);
     	if (snapshots.size() > 0) {
     		return false;
     	}
     	return true;
     }
 
+    protected boolean stateTransitTo(Snapshot snapshot, Snapshot.Event e) throws NoTransitionException {
+        return _snapshotFsm.transitTo(snapshot, e, null, _snapshotDao);
+    }
 }
diff --git a/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java
index d7deb6f..9389893 100644
--- a/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java
+++ b/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java
@@ -26,6 +26,7 @@
 import javax.ejb.Local;
 import javax.naming.ConfigurationException;
 
+import com.cloud.event.ActionEventUtils;
 import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotCmd;
 import org.apache.log4j.Logger;
 
@@ -39,7 +40,6 @@
 import com.cloud.async.dao.AsyncJobDao;
 import com.cloud.configuration.dao.ConfigurationDao;
 import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
 import com.cloud.storage.Snapshot;
 import com.cloud.storage.SnapshotPolicyVO;
 import com.cloud.storage.SnapshotScheduleVO;
@@ -232,8 +232,8 @@
 
 
                 tmpSnapshotScheduleVO = _snapshotScheduleDao.acquireInLockTable(snapshotScheId);
-                Long eventId = EventUtils.saveScheduledEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
-                        EventTypes.EVENT_SNAPSHOT_CREATE, "creating snapshot for volume Id:"+volumeId,0);
+                Long eventId = ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
+                        EventTypes.EVENT_SNAPSHOT_CREATE, "creating snapshot for volume Id:" + volumeId, 0);
 
                 Map<String, String> params = new HashMap<String, String>();
                 params.put(ApiConstants.VOLUME_ID, "" + volumeId);
diff --git a/server/src/com/cloud/template/HyervisorTemplateAdapter.java b/server/src/com/cloud/template/HyervisorTemplateAdapter.java
index c80d1de..81a482b 100755
--- a/server/src/com/cloud/template/HyervisorTemplateAdapter.java
+++ b/server/src/com/cloud/template/HyervisorTemplateAdapter.java
@@ -16,28 +16,13 @@
 // under the License.
 package com.cloud.template;
 
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.UnknownHostException;
-import java.util.List;
-
-import javax.ejb.Local;
-
-import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
-import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
-import org.apache.log4j.Logger;
-
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.storage.DeleteTemplateCommand;
-import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd;
-import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.host.HostVO;
@@ -53,6 +38,15 @@
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
+import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
+import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd;
+import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import java.net.*;
+import java.util.List;
 
 @Local(value=TemplateAdapter.class)
 public class HyervisorTemplateAdapter extends TemplateAdapterBase implements TemplateAdapter {
@@ -202,8 +196,7 @@
 						success = false;
 						break;
 					}
-					UsageEventVO usageEvent = new UsageEventVO(eventType, account.getId(), sZoneId, templateId, null);
-					_usageEventDao.persist(usageEvent);					
+					UsageEventUtils.publishUsageEvent(eventType, account.getId(), sZoneId, templateId, null, null, null);
                     templateHostVO.setDestroyed(true);
 					_tmpltHostDao.update(templateHostVO.getId(), templateHostVO);
                     String installPath = templateHostVO.getInstallPath();
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index 1372111..42106b3 100755
--- a/server/src/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/com/cloud/template/TemplateManagerImpl.java
@@ -16,41 +16,14 @@
 // under the License.
 package com.cloud.template;
 
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import javax.ejb.Local;
-import javax.naming.ConfigurationException;
-
-import org.apache.cloudstack.api.BaseListTemplateOrIsoPermissionsCmd;
-import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoPermissionsCmd;
-import org.apache.cloudstack.api.command.user.iso.*;
-import org.apache.cloudstack.api.command.user.template.*;
-import org.apache.log4j.Logger;
-
-import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.downloadTemplateFromSwiftToSecondaryStorageCommand;
-import com.cloud.agent.api.uploadTemplateToSwiftFromSecondaryStorageCommand;
 import com.cloud.agent.api.storage.DestroyCommand;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
 import com.cloud.agent.api.to.SwiftTO;
-import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
-import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
+import com.cloud.agent.api.uploadTemplateToSwiftFromSecondaryStorageCommand;
 import com.cloud.async.AsyncJobManager;
 import com.cloud.async.AsyncJobVO;
 import com.cloud.configuration.Config;
@@ -63,7 +36,7 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.EventDao;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.InvalidParameterValueException;
@@ -77,51 +50,19 @@
 import com.cloud.hypervisor.HypervisorGuruManager;
 import com.cloud.projects.Project;
 import com.cloud.projects.ProjectManager;
-import com.cloud.storage.LaunchPermissionVO;
-import com.cloud.storage.SnapshotVO;
-import com.cloud.storage.Storage;
+import com.cloud.storage.*;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Storage.TemplateType;
-import com.cloud.storage.StorageManager;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.StoragePoolHostVO;
-import com.cloud.storage.StoragePoolStatus;
-import com.cloud.storage.StoragePoolVO;
-import com.cloud.storage.Upload;
 import com.cloud.storage.Upload.Type;
-import com.cloud.storage.UploadVO;
-import com.cloud.storage.VMTemplateHostVO;
-import com.cloud.storage.VMTemplateStoragePoolVO;
-import com.cloud.storage.VMTemplateStorageResourceAssoc;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
-import com.cloud.storage.VMTemplateSwiftVO;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.VMTemplateZoneVO;
-import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.LaunchPermissionDao;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.storage.dao.StoragePoolDao;
-import com.cloud.storage.dao.StoragePoolHostDao;
-import com.cloud.storage.dao.UploadDao;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VMTemplateHostDao;
-import com.cloud.storage.dao.VMTemplatePoolDao;
-import com.cloud.storage.dao.VMTemplateS3Dao;
-import com.cloud.storage.dao.VMTemplateSwiftDao;
-import com.cloud.storage.dao.VMTemplateZoneDao;
-import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.*;
 import com.cloud.storage.download.DownloadMonitor;
 import com.cloud.storage.s3.S3Manager;
 import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.storage.swift.SwiftManager;
 import com.cloud.storage.upload.UploadMonitor;
 import com.cloud.template.TemplateAdapter.TemplateAdapterType;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.AccountService;
-import com.cloud.user.AccountVO;
-import com.cloud.user.ResourceLimitService;
-import com.cloud.user.UserContext;
+import com.cloud.user.*;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.user.dao.UserAccountDao;
 import com.cloud.user.dao.UserDao;
@@ -132,12 +73,7 @@
 import com.cloud.utils.component.Inject;
 import com.cloud.utils.component.Manager;
 import com.cloud.utils.concurrency.NamedThreadFactory;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.db.GlobalLock;
-import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.SearchBuilder;
-import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.*;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.UserVmManager;
 import com.cloud.vm.UserVmVO;
@@ -145,6 +81,21 @@
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.cloudstack.api.BaseListTemplateOrIsoPermissionsCmd;
+import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoPermissionsCmd;
+import org.apache.cloudstack.api.command.user.iso.*;
+import org.apache.cloudstack.api.command.user.template.*;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 
 @Local(value={TemplateManager.class, TemplateService.class})
@@ -823,8 +774,8 @@
                 _tmpltDao.addTemplateToZone(template, dstZoneId);
             	
             	if(account.getId() != Account.ACCOUNT_ID_SYSTEM){
-            	    UsageEventVO usageEvent = new UsageEventVO(copyEventType, account.getId(), dstZoneId, tmpltId, null, null, null, srcTmpltHost.getSize());
-            	    _usageEventDao.persist(usageEvent);
+                    UsageEventUtils.publishUsageEvent(copyEventType, account.getId(), dstZoneId, tmpltId, null, null, null, srcTmpltHost.getSize(),
+                            template.getClass().getName(), template.getUuid());
             	}
             	return true;
             }
diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java
index 919b7c6..c5a8c75 100755
--- a/server/src/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/com/cloud/user/AccountManagerImpl.java
@@ -36,6 +36,7 @@
 import javax.ejb.Local;
 import javax.naming.ConfigurationException;
 
+import com.cloud.event.ActionEventUtils;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.acl.SecurityChecker;
@@ -66,7 +67,6 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.EventUtils;
 import com.cloud.exception.AgentUnavailableException;
 import com.cloud.exception.CloudAuthenticationException;
 import com.cloud.exception.ConcurrentOperationException;
@@ -143,7 +143,7 @@
 import com.cloud.vm.dao.VMInstanceDao;
 
 @Local(value = { AccountManager.class, AccountService.class })
-public class AccountManagerImpl implements AccountManager, AccountService, Manager {
+public class AccountManagerImpl implements AccountManager, Manager {
     public static final Logger s_logger = Logger.getLogger(AccountManagerImpl.class);
 
     private String _name;
@@ -1788,7 +1788,7 @@
     public void logoutUser(Long userId) {
         UserAccount userAcct = _userAccountDao.findById(userId);
         if (userAcct != null) {
-            EventUtils.saveEvent(userId, userAcct.getAccountId(), userAcct.getDomainId(), EventTypes.EVENT_USER_LOGOUT, "user has logged out");
+            ActionEventUtils.onActionEvent(userId, userAcct.getAccountId(), userAcct.getDomainId(), EventTypes.EVENT_USER_LOGOUT, "user has logged out");
         } // else log some kind of error event? This likely means the user doesn't exist, or has been deleted...
     }
 
@@ -1919,10 +1919,10 @@
                 s_logger.debug("User: " + username + " in domain " + domainId + " has successfully logged in");
             }
             if (NetUtils.isValidIp(loginIpAddress)) {
-                EventUtils.saveEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN,
+                ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN,
                         "user has logged in from IP Address " + loginIpAddress);
             } else {
-                EventUtils.saveEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN,
+                ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN,
                         "user has logged in. The IP Address cannot be determined");
             }
             return user;
diff --git a/server/src/com/cloud/vm/NicVO.java b/server/src/com/cloud/vm/NicVO.java
index 64b9153..8e2edda 100644
--- a/server/src/com/cloud/vm/NicVO.java
+++ b/server/src/com/cloud/vm/NicVO.java
@@ -99,6 +99,12 @@
     @Column(name = "default_nic")
     boolean defaultNic;
 
+    @Column(name = "ip6_gateway")
+    String ip6Gateway;
+
+    @Column(name = "ip6_cidr")
+    String ip6Cidr;
+
     @Column(name = "strategy")
     @Enumerated(value = EnumType.STRING)
     ReservationStrategy reservationStrategy;
@@ -156,6 +162,7 @@
         this.defaultNic = defaultNic;
     }
 
+    @Override
     public String getIp6Address() {
         return ip6Address;
     }
@@ -324,4 +331,22 @@
     public void setUuid(String uuid) {
     	this.uuid = uuid;
     }
+
+    @Override
+	public String getIp6Gateway() {
+		return ip6Gateway;
+	}
+
+	public void setIp6Gateway(String ip6Gateway) {
+		this.ip6Gateway = ip6Gateway;
+	}
+
+    @Override
+	public String getIp6Cidr() {
+		return ip6Cidr;
+	}
+
+	public void setIp6Cidr(String ip6Cidr) {
+		this.ip6Cidr = ip6Cidr;
+	}
 }
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 3737450..a53e132 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -54,7 +54,7 @@
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.UsageEventDao;
 import com.cloud.exception.*;
 import com.cloud.ha.HighAvailabilityManager;
@@ -64,6 +64,7 @@
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
 import com.cloud.network.*;
+import com.cloud.network.Network.IpAddresses;
 import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
 import com.cloud.network.Networks.TrafficType;
@@ -245,8 +246,6 @@
     @Inject
     protected LoadBalancingRulesManager _lbMgr;
     @Inject
-    protected UsageEventDao _usageEventDao;
-    @Inject
     protected SSHKeyPairDao _sshKeyPairDao;
     @Inject
     protected UserVmDetailsDao _vmDetailsDao;
@@ -280,6 +279,8 @@
     PhysicalNetworkDao _physicalNetworkDao;
     @Inject
     VpcManager _vpcMgr;
+    @Inject
+    UsageEventDao _usageEventDao;
 
     protected ScheduledExecutorService _executor = null;
     protected int _expungeInterval;
@@ -841,7 +842,7 @@
     }
 
     @Override
-    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true)
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume",  async = true)
     public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) {
         Account caller = UserContext.current().getCaller();
         if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null))
@@ -1046,9 +1047,9 @@
         if(network == null) {
             throw new InvalidParameterValueException("unable to find a network with id " + networkId);
         }
-        NicProfile profile = new NicProfile(null);
+        NicProfile profile = new NicProfile(null, null);
         if(ipAddress != null) {
-          profile = new NicProfile(ipAddress);
+          profile = new NicProfile(ipAddress, null);
         }
 
         // Perform permission check on VM
@@ -1372,9 +1373,9 @@
                         offeringId = offering.getId();
                     }
                 }
-                UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), offeringId, templateId,
-                        volume.getSize());
-                _usageEventDao.persist(usageEvent);
+                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(),
+                        volume.getDataCenterId(), volume.getId(), volume.getName(), offeringId, templateId,
+                        volume.getSize(), Volume.class.getName(), volume.getUuid());
             }
         }
 
@@ -1640,8 +1641,8 @@
             //check permissions
             _accountMgr.checkAccess(caller, null, true, snapshot);
 
-            if (snapshot.getStatus() != Snapshot.Status.BackedUp) {
-                throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.Status.BackedUp + " state yet and can't be used for template creation");
+            if (snapshot.getState() != Snapshot.State.BackedUp) {
+                throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.State.BackedUp + " state yet and can't be used for template creation");
             }
 
             /*
@@ -1888,9 +1889,10 @@
                 templateHostVO.setPhysicalSize(answer.getphysicalSize());
                 _templateHostDao.persist(templateHostVO);
 
-                UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_TEMPLATE_CREATE, privateTemplate.getAccountId(), secondaryStorageHost.getDataCenterId(), privateTemplate.getId(),
-                        privateTemplate.getName(), null, privateTemplate.getSourceTemplateId(), templateHostVO.getSize());
-                _usageEventDao.persist(usageEvent);
+                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_TEMPLATE_CREATE, privateTemplate.getAccountId(),
+                        secondaryStorageHost.getDataCenterId(), privateTemplate.getId(),
+                        privateTemplate.getName(), null, privateTemplate.getSourceTemplateId(),
+                        templateHostVO.getSize(), VirtualMachineTemplate.class.getName(), privateTemplate.getUuid());
                 txn.commit();
             }
         } finally {
@@ -2336,7 +2338,7 @@
 
     @Override
     public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList, Account owner,
-            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps, String defaultIp, String keyboard)
+            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard)
                     throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = UserContext.current().getCaller();
@@ -2379,13 +2381,13 @@
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId,
-                diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard);
+                diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, keyboard);
     }
 
     @Override
     public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
             List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData,
-            String sshKeyPair, Map<Long, String> requestedIps, String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
+            String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
             ResourceAllocationException {
 
         Account caller = UserContext.current().getCaller();
@@ -2484,12 +2486,12 @@
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId,
-                diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard);
+                diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, keyboard);
     }
 
     @Override
     public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner, String hostName,
-            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps, String defaultIp, String keyboard)
+            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard)
                     throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = UserContext.current().getCaller();
@@ -2526,7 +2528,7 @@
                     s_logger.debug("Creating network for account " + owner + " from the network offering id=" +requiredOfferings.get(0).getId() + " as a part of deployVM process");
                     Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(),
                             owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null,
-                            null, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null);
+                            null, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null);
                     defaultNetwork = _networkDao.findById(newNetwork.getId());
                 } else if (virtualNetworks.size() > 1) {
                     throw new InvalidParameterValueException("More than 1 default Isolated networks are found for account " + owner + "; please specify networkIds");
@@ -2570,12 +2572,13 @@
             }
         }
 
-        return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard);
+        return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, keyboard);
     }
 
     @DB @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true)
     protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, String hostName, String displayName, Account owner, Long diskOfferingId,
-            Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, String> requestedIps, String defaultNetworkIp, String keyboard) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
+            Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard)
+            		throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
 
         _accountMgr.checkAccess(caller, null, true, owner);
 
@@ -2585,7 +2588,7 @@
 
         long accountId = owner.getId();
 
-        assert !(requestedIps != null && defaultNetworkIp != null) : "requestedIp list and defaultNetworkIp should never be specified together";
+        assert !(requestedIps != null && (defaultIps.getIp4Address() != null || defaultIps.getIp6Address() != null)) : "requestedIp list and defaultNetworkIp should never be specified together";
 
         if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
             throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getId());
@@ -2709,18 +2712,25 @@
                 throw new InvalidParameterValueException("Network id=" + network.getId() + " doesn't belong to zone " + zone.getId());
             }
 
-            String requestedIp = null;
+            IpAddresses requestedIpPair = null;
             if (requestedIps != null && !requestedIps.isEmpty()) {
-                requestedIp = requestedIps.get(network.getId());
+                requestedIpPair = requestedIps.get(network.getId());
             }
-
-            NicProfile profile = new NicProfile(requestedIp);
+            
+            if (requestedIpPair == null) {
+            	requestedIpPair = new IpAddresses(null, null);
+            } else {
+            	checkRequestedIpAddresses(requestedIpPair.getIp4Address(), requestedIpPair.getIp6Address());
+            }
+            
+            NicProfile profile = new NicProfile(requestedIpPair.getIp4Address(), requestedIpPair.getIp6Address());
 
             if (defaultNetworkNumber == 0) {
                 defaultNetworkNumber++;
                 // if user requested specific ip for default network, add it
-                if (defaultNetworkIp != null) {
-                    profile = new NicProfile(defaultNetworkIp);
+                if (defaultIps.getIp4Address() != null || defaultIps.getIp6Address() != null) {
+                	checkRequestedIpAddresses(defaultIps.getIp4Address(), defaultIps.getIp6Address());
+                    profile = new NicProfile(defaultIps.getIp4Address(), defaultIps.getIp6Address());
                 }
 
                 profile.setDefaultNic(true);
@@ -2835,8 +2845,9 @@
         }
         UserContext.current().setEventDetails("Vm Id: " + vm.getId());
 
-        UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString());
-        _usageEventDao.persist(usageEvent);
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(),
+                vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString(),
+                VirtualMachine.class.getName(), vm.getUuid());
 
         _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm);
         txn.commit();
@@ -2857,7 +2868,20 @@
         return vm;
     }
 
-    private void validateUserData(String userData) {
+    private void checkRequestedIpAddresses(String ip4, String ip6) throws InvalidParameterValueException {
+    	if (ip4 != null) {
+    		if (!NetUtils.isValidIp(ip4)) {
+    			throw new InvalidParameterValueException("Invalid specified IPv4 address " + ip4);
+    		}
+    	}
+    	if (ip6 != null) {
+    		if (!NetUtils.isValidIpv6(ip6)) {
+    			throw new InvalidParameterValueException("Invalid specified IPv6 address " + ip6);
+    		}
+    	}
+	}
+
+	private void validateUserData(String userData) {
         byte[] decodedUserData = null;
         if (userData != null) {
             if (!Base64.isBase64(userData)) {
@@ -3006,8 +3030,9 @@
         for (NicVO nic : nics) {
             NetworkVO network = _networkDao.findById(nic.getNetworkId());
             long isDefault = (nic.isDefaultNic()) ? 1 : 0;
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm.getHostName(), network.getNetworkOfferingId(), null, isDefault);
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(),
+                    vm.getDataCenterIdToDeployIn(), vm.getId(), vm.getHostName(), network.getNetworkOfferingId(),
+                    null, isDefault, VirtualMachine.class.getName(), vm.getUuid());
             if (network.getTrafficType() == TrafficType.Guest) {
                 originalIp = nic.getIp4Address();
                 guestNic = nic;
@@ -3270,8 +3295,9 @@
             List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
             for (VolumeVO volume : volumes) {
                 if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
-                    UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName());
-                    _usageEventDao.persist(usageEvent);
+                    UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(),
+                            volume.getDataCenterId(), volume.getId(), volume.getName(), Volume.class.getName(),
+                            volume.getUuid());
                 }
             }
 
@@ -3721,8 +3747,9 @@
         Transaction txn = Transaction.currentTxn();
         txn.start();
         //generate destroy vm event for usage
-        _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(),
-                vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString()));
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(),
+                vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString(),
+                VirtualMachine.class.getName(), vm.getUuid());
         // update resource counts
         _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.user_vm);
 
@@ -3734,13 +3761,16 @@
         // OS 2: update volume
         List<VolumeVO> volumes = _volsDao.findByInstance(cmd.getVmId());
         for (VolumeVO volume : volumes) {
-            _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName()));
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(),
+                    volume.getDataCenterId(), volume.getId(), volume.getName(), Volume.class.getName(), volume.getUuid());
             _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.volume);
             volume.setAccountId(newAccount.getAccountId());
             _volsDao.persist(volume);
             _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.volume);
-            _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(),
-                    volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize()));
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(),
+                    volume.getDataCenterId(), volume.getId(), volume.getName(),
+                    volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(),
+                    volume.getUuid());
             //snapshots: mark these removed in db
             List<SnapshotVO> snapshots = _snapshotDao.listByVolumeIdIncludingRemoved(volume.getId());
             for (SnapshotVO snapshot: snapshots){
@@ -3750,8 +3780,9 @@
 
         _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.user_vm);
         //generate usage events to account for this change
-        _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_CREATE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(),
-                vm.getHostName(), vm.getServiceOfferingId(),  vm.getTemplateId(), vm.getHypervisorType().toString()));
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(),
+                vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString(),
+                VirtualMachine.class.getName(), vm.getUuid());
 
         txn.commit();
 
@@ -3885,7 +3916,7 @@
                         requiredOfferings.get(0).getId() + " as a part of deployVM process");
                             Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(),
                                     newAccount.getAccountName() + "-network", newAccount.getAccountName() + "-network", null, null,
-                                    null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null);
+                                    null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null);
                             defaultNetwork = _networkDao.findById(newNetwork.getId());
                         } else if (virtualNetworks.size() > 1) {
                             throw new InvalidParameterValueException("More than 1 default Isolated networks are found " +
diff --git a/server/src/com/cloud/vm/UserVmStateListener.java b/server/src/com/cloud/vm/UserVmStateListener.java
index 4d9bcc5..786387f 100644
--- a/server/src/com/cloud/vm/UserVmStateListener.java
+++ b/server/src/com/cloud/vm/UserVmStateListener.java
@@ -16,24 +16,48 @@
 // under the License.
 package com.cloud.vm;
 
-import java.util.List;
-
+import com.cloud.event.EventCategory;
 import com.cloud.event.EventTypes;
-import com.cloud.event.UsageEventVO;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.event.dao.UsageEventDao;
+import com.cloud.network.Network;
 import com.cloud.network.NetworkVO;
 import com.cloud.network.dao.NetworkDao;
+import com.cloud.server.ManagementServer;
+import com.cloud.utils.component.Adapters;
+import com.cloud.utils.component.ComponentLocator;
 import com.cloud.utils.fsm.StateListener;
 import com.cloud.vm.VirtualMachine.Event;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.NicDao;
+import org.apache.cloudstack.framework.events.EventBus;
+import org.apache.cloudstack.framework.events.EventBusException;
+import org.apache.log4j.Logger;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 public class UserVmStateListener implements StateListener<State, VirtualMachine.Event, VirtualMachine> {
 
     protected UsageEventDao _usageEventDao;
     protected NetworkDao _networkDao;
     protected NicDao _nicDao;
-    
+    private static final Logger s_logger = Logger.getLogger(UserVmStateListener.class);
+
+    // get the event bus provider if configured
+    protected static EventBus _eventBus = null;
+    static {
+        Adapters<EventBus> eventBusImpls = ComponentLocator.getLocator(ManagementServer.Name).getAdapters(EventBus.class);
+        if (eventBusImpls != null) {
+            Enumeration<EventBus> eventBusenum = eventBusImpls.enumeration();
+            if (eventBusenum != null && eventBusenum.hasMoreElements()) {
+                _eventBus = eventBusenum.nextElement(); // configure event bus if configured
+            }
+        }
+    }
+
     public UserVmStateListener(UsageEventDao usageEventDao, NetworkDao networkDao, NicDao nicDao) {
         this._usageEventDao = usageEventDao;
         this._networkDao = networkDao;
@@ -42,6 +66,7 @@
     
     @Override
     public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) {
+        pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
         return true;
     }
 
@@ -54,29 +79,62 @@
         if(vo.getType() != VirtualMachine.Type.User){
             return true;
         }
-        
+
+        pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState);
+
         if (VirtualMachine.State.isVmCreated(oldState, event, newState)) {
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName(), vo.getServiceOfferingId(), 
+            UsageEventUtils.saveUsageEvent(EventTypes.EVENT_VM_CREATE, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName(), vo.getServiceOfferingId(),
                     vo.getTemplateId(), vo.getHypervisorType().toString());
-            _usageEventDao.persist(usageEvent);
         } else if (VirtualMachine.State.isVmStarted(oldState, event, newState)) {
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_START, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName(), vo.getServiceOfferingId(), 
+            UsageEventUtils.saveUsageEvent(EventTypes.EVENT_VM_START, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName(), vo.getServiceOfferingId(),
                     vo.getTemplateId(), vo.getHypervisorType().toString());
-            _usageEventDao.persist(usageEvent);
         } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) {
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_STOP, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName());
-            _usageEventDao.persist(usageEvent);
+            UsageEventUtils.saveUsageEvent(EventTypes.EVENT_VM_STOP, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName());
             List<NicVO> nics = _nicDao.listByVmId(vo.getId());
             for (NicVO nic : nics) {
                 NetworkVO network = _networkDao.findById(nic.getNetworkId());
-                usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), null, network.getNetworkOfferingId(), null, 0L);
-                _usageEventDao.persist(usageEvent);
+                UsageEventUtils.saveUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), null, network.getNetworkOfferingId(), null, 0L);
             }
         } else if (VirtualMachine.State.isVmDestroyed(oldState, event, newState)) {
-            UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_DESTROY, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName(), vo.getServiceOfferingId(), 
+            UsageEventUtils.saveUsageEvent(EventTypes.EVENT_VM_DESTROY, vo.getAccountId(), vo.getDataCenterIdToDeployIn(), vo.getId(), vo.getHostName(), vo.getServiceOfferingId(),
                     vo.getTemplateId(), vo.getHypervisorType().toString());
-            _usageEventDao.persist(usageEvent);
         } 
         return true;
     }
+
+    private void pubishOnEventBus(String event, String status, VirtualMachine vo, VirtualMachine.State oldState, VirtualMachine.State newState) {
+
+        if (_eventBus == null) {
+            return; // no provider is configured to provide events bus, so just return
+        }
+
+        String resourceName = getEntityFromClassName(Network.class.getName());
+        org.apache.cloudstack.framework.events.Event eventMsg =  new org.apache.cloudstack.framework.events.Event(
+                ManagementServer.Name,
+                EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(),
+                event,
+                resourceName,
+                vo.getUuid());
+        Map<String, String> eventDescription = new HashMap<String, String>();
+        eventDescription.put("resource", resourceName);
+        eventDescription.put("id", vo.getUuid());
+        eventDescription.put("old-state", oldState.name());
+        eventDescription.put("new-state", newState.name());
+        eventMsg.setDescription(eventDescription);
+        try {
+            _eventBus.publish(eventMsg);
+        } catch (EventBusException e) {
+            s_logger.warn("Failed to publish state change event on the the event bus.");
+        }
+
+    }
+
+    private String getEntityFromClassName(String entityClassName) {
+        int index = entityClassName.lastIndexOf(".");
+        String entityName = entityClassName;
+        if (index != -1) {
+            entityName = entityClassName.substring(index+1);
+        }
+        return entityName;
+    }
 }
diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java
index e628033..af0b03f 100755
--- a/server/test/com/cloud/network/MockNetworkManagerImpl.java
+++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java
@@ -247,7 +247,7 @@
 
     @Override
     public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, Long domainId,
-            PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
+            PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String gatewayv6, String cidrv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
     }
@@ -661,7 +661,7 @@
      */
     @Override
     public void allocateDirectIp(NicProfile nic, DataCenter dc, VirtualMachineProfile<? extends VirtualMachine> vm,
-            Network network, String requestedIp) throws InsufficientVirtualNetworkCapcityException,
+            Network network, String requestedIpv4, String requestedIpv6) throws InsufficientVirtualNetworkCapcityException,
             InsufficientAddressCapacityException {
         // TODO Auto-generated method stub
         
diff --git a/server/test/com/cloud/network/MockNetworkModelImpl.java b/server/test/com/cloud/network/MockNetworkModelImpl.java
index 1771660..07c6247 100644
--- a/server/test/com/cloud/network/MockNetworkModelImpl.java
+++ b/server/test/com/cloud/network/MockNetworkModelImpl.java
@@ -806,4 +806,10 @@
         return false;
     }
 
+	@Override
+	public Vlan getVlanForNetwork(long networkId) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
 }
diff --git a/server/test/com/cloud/snapshot/SnapshotDaoTest.java b/server/test/com/cloud/snapshot/SnapshotDaoTest.java
index c412f49..5dc9b91 100644
--- a/server/test/com/cloud/snapshot/SnapshotDaoTest.java
+++ b/server/test/com/cloud/snapshot/SnapshotDaoTest.java
@@ -16,24 +16,23 @@
 // under the License.
 package com.cloud.snapshot;
 
-import java.util.List;
-
 import com.cloud.storage.Snapshot;
 import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.dao.SnapshotDaoImpl;
 import com.cloud.utils.component.ComponentLocator;
-
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
+import java.util.List;
+
 public class SnapshotDaoTest extends TestCase {
 	
     public void testListBy() {
         SnapshotDaoImpl dao = ComponentLocator.inject(SnapshotDaoImpl.class);
         
-        List<SnapshotVO> snapshots = dao.listByInstanceId(3, Snapshot.Status.BackedUp);
+        List<SnapshotVO> snapshots = dao.listByInstanceId(3, Snapshot.State.BackedUp);
         for(SnapshotVO snapshot : snapshots) {
-            Assert.assertTrue(snapshot.getStatus() == Snapshot.Status.BackedUp);
+            Assert.assertTrue(snapshot.getState() == Snapshot.State.BackedUp);
         }
     }
 }
diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java
index 2b22e7b..b5433ba 100644
--- a/server/test/com/cloud/user/MockAccountManagerImpl.java
+++ b/server/test/com/cloud/user/MockAccountManagerImpl.java
@@ -45,7 +45,7 @@
 
 
 @Local(value = { AccountManager.class, AccountService.class })
-public class MockAccountManagerImpl implements Manager, AccountManager, AccountService {
+public class MockAccountManagerImpl implements Manager, AccountManager {
 
 
     @Override
diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
index 0425773..3e70153 100644
--- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java
+++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
@@ -59,6 +59,7 @@
 import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Network;
+import com.cloud.network.Network.IpAddresses;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.projects.Project.ListProjectResourcesCriteria;
 import com.cloud.server.Criteria;
@@ -377,8 +378,8 @@
 
     @Override
     public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList, Account owner,
-            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps,
-            String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
+            String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps,
+            IpAddresses defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException,
             ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
@@ -387,7 +388,7 @@
     @Override
     public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
             List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData,
-            String sshKeyPair, Map<Long, String> requestedIps, String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
+            String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
             StorageUnavailableException, ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
@@ -395,7 +396,7 @@
 
     @Override
     public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner, String hostName,
-            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, String> requestedIps, String defaultIp,
+            String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps,
             String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
index 1056f6a..17ad2e1 100644
--- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -492,7 +492,7 @@
      */
     @Override
     public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, String vlanGateway, String vlanNetmask, String vlanId,
-            Account vlanOwner) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException {
+            Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException {
         // TODO Auto-generated method stub
         return null;
     }
diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
index a0f9d00..5d9c436 100644
--- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
@@ -56,6 +56,7 @@
 import com.cloud.network.PhysicalNetworkServiceProvider;
 import com.cloud.network.PhysicalNetworkTrafficType;
 import com.cloud.network.PublicIpAddress;
+import com.cloud.network.UserIpv6Address;
 import com.cloud.network.addr.PublicIp;
 import com.cloud.network.dao.NetworkServiceMapDao;
 import com.cloud.network.element.LoadBalancingServiceProvider;
@@ -846,7 +847,7 @@
     @Override
     public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway,
             String cidr, String vlanId, String networkDomain, Account owner, Long domainId,
-            PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId)
+            PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String gatewayv6, String cidrv6)
             throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
@@ -1010,10 +1011,9 @@
      */
     @Override
     public void allocateDirectIp(NicProfile nic, DataCenter dc, VirtualMachineProfile<? extends VirtualMachine> vm,
-            Network network, String requestedIp) throws InsufficientVirtualNetworkCapcityException,
+            Network network, String requestedIpv4, String requestedIpv6) throws InsufficientVirtualNetworkCapcityException,
             InsufficientAddressCapacityException {
         // TODO Auto-generated method stub
-        
     }
 
 
@@ -1312,10 +1312,6 @@
         return null;
     }
 
-
-
-
-
     /* (non-Javadoc)
      * @see com.cloud.network.NetworkService#getNetwork(java.lang.String)
      */
diff --git a/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java b/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java
index 7c9a582..f0e4b54 100644
--- a/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java
+++ b/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java
@@ -16,12 +16,7 @@
 // under the License.
 package com.cloud.vpc.dao;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import javax.ejb.Local;
-
+import com.cloud.network.Network;
 import com.cloud.network.Network.GuestType;
 import com.cloud.network.NetworkAccountVO;
 import com.cloud.network.NetworkVO;
@@ -31,6 +26,11 @@
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 
+import javax.ejb.Local;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 @Local(value = NetworkDao.class)
 @DB(txn = false)
 public class MockNetworkDaoImpl extends GenericDaoBase<NetworkVO, Long> implements NetworkDao{
@@ -342,6 +342,11 @@
         return 0;
     }
 
+    @Override
+    public boolean updateState(Network.State currentState, Network.Event event, Network.State nextState, Network vo, Object data) {
+        return true;
+    }
+
     /* (non-Javadoc)
      * @see com.cloud.network.dao.NetworkDao#listNetworksByAccount(long, long, com.cloud.network.Network.GuestType, boolean)
      */
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index d50d1fa..ad19e0f 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -208,6 +208,8 @@
   `broadcast_uri` varchar(255) COMMENT 'broadcast domain specifier',
   `gateway` varchar(15) COMMENT 'gateway for this network configuration',
   `cidr` varchar(18) COMMENT 'network cidr', 
+  `ip6_gateway` varchar(50) COMMENT 'IPv6 gateway for this network', 
+  `ip6_cidr` varchar(50) COMMENT 'IPv6 cidr for this network',
   `mode` varchar(32) COMMENT 'How to retrieve ip address in this network',
   `network_offering_id` bigint unsigned NOT NULL COMMENT 'network offering id that this configuration is created from',
   `physical_network_id` bigint unsigned COMMENT 'physical network id that this configuration is based on',
@@ -271,6 +273,8 @@
   `update_time` timestamp NOT NULL COMMENT 'time the state was changed',
   `isolation_uri` varchar(255) COMMENT 'id for isolation',
   `ip6_address` char(40) COMMENT 'ip6 address',
+  `ip6_gateway` varchar(50) COMMENT 'gateway for ip6 address',
+  `ip6_cidr` varchar(50) COMMENT 'cidr for ip6 address',
   `default_nic` tinyint NOT NULL COMMENT "None", 
   `vm_type` varchar(32) COMMENT 'type of vm: System or User vm',
   `created` datetime NOT NULL COMMENT 'date created',
@@ -501,7 +505,10 @@
   `vlan_id` varchar(255),
   `vlan_gateway` varchar(255),
   `vlan_netmask` varchar(255),
+  `ip6_gateway` varchar(255),
+  `ip6_cidr` varchar(255),
   `description` varchar(255),
+  `ip6_range` varchar(255),
   `vlan_type` varchar(255),
   `data_center_id` bigint unsigned NOT NULL,
   `network_id` bigint unsigned NOT NULL COMMENT 'id of corresponding network offering',
@@ -664,7 +671,7 @@
     PRIMARY KEY (`id`),
     UNIQUE `i_op_dc_vnet_alloc__vnet__data_center_id__account_id`(`vnet`, `data_center_id`, `account_id`),
     INDEX `i_op_dc_vnet_alloc__dc_taken`(`data_center_id`, `taken`),
-    UNIQUE `i_op_dc_vnet_alloc__vnet__data_center_id`(`vnet`, `data_center_id`),
+    UNIQUE `i_op_dc_vnet_alloc__vnet__data_center_id`(`vnet`, `physical_network_id`, `data_center_id`),
 	CONSTRAINT `fk_op_dc_vnet_alloc__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE,
 	CONSTRAINT `fk_op_dc_vnet_alloc__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -2566,5 +2573,30 @@
 INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (3, UUID(), 'snmp','Linux CPU Idle - percentage', '1.3.6.1.4.1.2021.11.11.0', now());
 INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (100, UUID(), 'netscaler','Response Time - microseconds', 'RESPTIME', now());
 
+CREATE TABLE  `cloud`.`user_ipv6_address` (
+  `id` bigint unsigned NOT NULL UNIQUE auto_increment,
+  `uuid` varchar(40),
+  `account_id` bigint unsigned NULL,
+  `domain_id` bigint unsigned NULL,
+  `ip_address` char(50) NOT NULL,
+  `data_center_id` bigint unsigned NOT NULL COMMENT 'zone that it belongs to',
+  `vlan_id` bigint unsigned NOT NULL,
+  `state` char(32) NOT NULL default 'Free' COMMENT 'state of the ip address',
+  `mac_address` varchar(40) NOT NULL COMMENT 'mac address of this ip',
+  `source_network_id` bigint unsigned NOT NULL COMMENT 'network id ip belongs to',
+  `network_id` bigint unsigned COMMENT 'network this public ip address is associated with',
+  `physical_network_id` bigint unsigned NOT NULL COMMENT 'physical network id that this configuration is based on',
+  `created` datetime NULL COMMENT 'Date this ip was allocated to someone',
+  PRIMARY KEY (`id`),
+  UNIQUE (`ip_address`, `source_network_id`),
+  CONSTRAINT `fk_user_ipv6_address__source_network_id` FOREIGN KEY (`source_network_id`) REFERENCES `networks`(`id`),
+  CONSTRAINT `fk_user_ipv6_address__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`),
+  CONSTRAINT `fk_user_ipv6_address__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`),
+  CONSTRAINT `fk_user_ipv6_address__vlan_id` FOREIGN KEY (`vlan_id`) REFERENCES `vlan`(`id`) ON DELETE CASCADE,
+  CONSTRAINT `fk_user_ipv6_address__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE,
+  CONSTRAINT `uc_user_ipv6_address__uuid` UNIQUE (`uuid`),
+  CONSTRAINT `fk_user_ipv6_address__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 SET foreign_key_checks = 1;
 
diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql
index ed4946e..304e12c 100644
--- a/setup/db/db/schema-40to410.sql
+++ b/setup/db/db/schema-40to410.sql
@@ -1264,3 +1264,7 @@
         `cloud`.`domain` ON data_center.domain_id = domain.id;               
         
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'direct.agent.pool.size', '500', 'Default size for DirectAgentPool');
+
+ALTER TABLE `cloud`.`op_dc_vnet_alloc` DROP INDEX i_op_dc_vnet_alloc__vnet__data_center_id;
+
+ALTER TABLE `cloud`.`op_dc_vnet_alloc` ADD CONSTRAINT UNIQUE `i_op_dc_vnet_alloc__vnet__data_center_id`(`vnet`, `physical_network_id`, `data_center_id`);
diff --git a/tools/whisker/LICENSE b/tools/whisker/LICENSE
index 7efac5c..025cb33 100644
--- a/tools/whisker/LICENSE
+++ b/tools/whisker/LICENSE
@@ -748,7 +748,7 @@
             
             The Covered Software is a "commercial item," as that term is
             defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
-            computer software" (as that term is defined at 48 C.F.R. ¤
+            computer software" (as that term is defined at 48 C.F.R. �
             252.227-7014(a)(1)) and "commercial computer software
             documentation" as such terms are used in 48 C.F.R. 12.212 (Sept.
             1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1
@@ -1138,7 +1138,7 @@
             
             The Covered Software is a "commercial item," as that term is
             defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
-            computer software" (as that term is defined at 48 C.F.R. ¤
+            computer software" (as that term is defined at 48 C.F.R. �
             252.227-7014(a)(1)) and "commercial computer software
             documentation" as such terms are used in 48 C.F.R. 12.212 (Sept.
             1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1
@@ -1526,7 +1526,7 @@
             
             The Covered Software is a "commercial item," as that term is
             defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
-            computer software" (as that term is defined at 48 C.F.R. ¤
+            computer software" (as that term is defined at 48 C.F.R. �
             252.227-7014(a)(1)) and "commercial computer software
             documentation" as such terms are used in 48 C.F.R. 12.212 (Sept.
             1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1
@@ -1867,7 +1867,9 @@
             slf4j-api-1.5.11.jar  from https://github.com/qos-ch/slf4j
             slf4j-jdk14-1.5.11.jar  from https://github.com/qos-ch/slf4j
 
-    licensed under the Mozilla Public License, Version 1.0 http://www.mozilla.org/MPL/1.1/  (as follows)
+
+    licensed under the Mozilla Public License, Version 1.1 http://www.mozilla.org/MPL/1.1/  (as follows)
+
  
             
                                       MOZILLA PUBLIC LICENSE
@@ -2344,6 +2346,486 @@
         from Shigeru Chiba  http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/ 
             javassist-3.9.0.GA.jar  from http://sourceforge.net/projects/jboss/files/Javassist/
 
+
+    licensed under the Mozilla Public License, Version 1.1 http://www.mozilla.org/MPL/1.1/  (as follows)
+
+            Copyright (c) 2007-2012 VMware, Inc.  All Rights Reserved.
+
+                                      MOZILLA PUBLIC LICENSE
+                                            Version 1.1
+
+                                          ---------------
+
+            1. Definitions.
+
+                 1.0.1. "Commercial Use" means distribution or otherwise making the
+                 Covered Code available to a third party.
+
+                 1.1. "Contributor" means each entity that creates or contributes to
+                 the creation of Modifications.
+
+                 1.2. "Contributor Version" means the combination of the Original
+                 Code, prior Modifications used by a Contributor, and the Modifications
+                 made by that particular Contributor.
+
+                 1.3. "Covered Code" means the Original Code or Modifications or the
+                 combination of the Original Code and Modifications, in each case
+                 including portions thereof.
+
+                 1.4. "Electronic Distribution Mechanism" means a mechanism generally
+                 accepted in the software development community for the electronic
+                 transfer of data.
+
+                 1.5. "Executable" means Covered Code in any form other than Source
+                 Code.
+
+                 1.6. "Initial Developer" means the individual or entity identified
+                 as the Initial Developer in the Source Code notice required by Exhibit
+                 A.
+
+                 1.7. "Larger Work" means a work which combines Covered Code or
+                 portions thereof with code not governed by the terms of this License.
+
+                 1.8. "License" means this document.
+
+                 1.8.1. "Licensable" means having the right to grant, to the maximum
+                 extent possible, whether at the time of the initial grant or
+                 subsequently acquired, any and all of the rights conveyed herein.
+
+                 1.9. "Modifications" means any addition to or deletion from the
+                 substance or structure of either the Original Code or any previous
+                 Modifications. When Covered Code is released as a series of files, a
+                 Modification is:
+                      A. Any addition to or deletion from the contents of a file
+                      containing Original Code or previous Modifications.
+
+                      B. Any new file that contains any part of the Original Code or
+                      previous Modifications.
+
+                 1.10. "Original Code" means Source Code of computer software code
+                 which is described in the Source Code notice required by Exhibit A as
+                 Original Code, and which, at the time of its release under this
+                 License is not already Covered Code governed by this License.
+
+                 1.10.1. "Patent Claims" means any patent claim(s), now owned or
+                 hereafter acquired, including without limitation,  method, process,
+                 and apparatus claims, in any patent Licensable by grantor.
+
+                 1.11. "Source Code" means the preferred form of the Covered Code for
+                 making modifications to it, including all modules it contains, plus
+                 any associated interface definition files, scripts used to control
+                 compilation and installation of an Executable, or source code
+                 differential comparisons against either the Original Code or another
+                 well known, available Covered Code of the Contributor's choice. The
+                 Source Code can be in a compressed or archival form, provided the
+                 appropriate decompression or de-archiving software is widely available
+                 for no charge.
+
+                 1.12. "You" (or "Your")  means an individual or a legal entity
+                 exercising rights under, and complying with all of the terms of, this
+                 License or a future version of this License issued under Section 6.1.
+                 For legal entities, "You" includes any entity which controls, is
+                 controlled by, or is under common control with You. For purposes of
+                 this definition, "control" means (a) the power, direct or indirect,
+                 to cause the direction or management of such entity, whether by
+                 contract or otherwise, or (b) ownership of more than fifty percent
+                 (50%) of the outstanding shares or beneficial ownership of such
+                 entity.
+
+            2. Source Code License.
+
+                 2.1. The Initial Developer Grant.
+                 The Initial Developer hereby grants You a world-wide, royalty-free,
+                 non-exclusive license, subject to third party intellectual property
+                 claims:
+                      (a)  under intellectual property rights (other than patent or
+                      trademark) Licensable by Initial Developer to use, reproduce,
+                      modify, display, perform, sublicense and distribute the Original
+                      Code (or portions thereof) with or without Modifications, and/or
+                      as part of a Larger Work; and
+
+                      (b) under Patents Claims infringed by the making, using or
+                      selling of Original Code, to make, have made, use, practice,
+                      sell, and offer for sale, and/or otherwise dispose of the
+                      Original Code (or portions thereof).
+
+                      (c) the licenses granted in this Section 2.1(a) and (b) are
+                      effective on the date Initial Developer first distributes
+                      Original Code under the terms of this License.
+
+                      (d) Notwithstanding Section 2.1(b) above, no patent license is
+                      granted: 1) for code that You delete from the Original Code; 2)
+                      separate from the Original Code;  or 3) for infringements caused
+                      by: i) the modification of the Original Code or ii) the
+                      combination of the Original Code with other software or devices.
+
+                 2.2. Contributor Grant.
+                 Subject to third party intellectual property claims, each Contributor
+                 hereby grants You a world-wide, royalty-free, non-exclusive license
+
+                      (a)  under intellectual property rights (other than patent or
+                      trademark) Licensable by Contributor, to use, reproduce, modify,
+                      display, perform, sublicense and distribute the Modifications
+                      created by such Contributor (or portions thereof) either on an
+                      unmodified basis, with other Modifications, as Covered Code
+                      and/or as part of a Larger Work; and
+
+                      (b) under Patent Claims infringed by the making, using, or
+                      selling of  Modifications made by that Contributor either alone
+                      and/or in combination with its Contributor Version (or portions
+                      of such combination), to make, use, sell, offer for sale, have
+                      made, and/or otherwise dispose of: 1) Modifications made by that
+                      Contributor (or portions thereof); and 2) the combination of
+                      Modifications made by that Contributor with its Contributor
+                      Version (or portions of such combination).
+
+                      (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+                      effective on the date Contributor first makes Commercial Use of
+                      the Covered Code.
+
+                      (d)    Notwithstanding Section 2.2(b) above, no patent license is
+                      granted: 1) for any code that Contributor has deleted from the
+                      Contributor Version; 2)  separate from the Contributor Version;
+                      3)  for infringements caused by: i) third party modifications of
+                      Contributor Version or ii)  the combination of Modifications made
+                      by that Contributor with other software  (except as part of the
+                      Contributor Version) or other devices; or 4) under Patent Claims
+                      infringed by Covered Code in the absence of Modifications made by
+                      that Contributor.
+
+            3. Distribution Obligations.
+
+                 3.1. Application of License.
+                 The Modifications which You create or to which You contribute are
+                 governed by the terms of this License, including without limitation
+                 Section 2.2. The Source Code version of Covered Code may be
+                 distributed only under the terms of this License or a future version
+                 of this License released under Section 6.1, and You must include a
+                 copy of this License with every copy of the Source Code You
+                 distribute. You may not offer or impose any terms on any Source Code
+                 version that alters or restricts the applicable version of this
+                 License or the recipients' rights hereunder. However, You may include
+                 an additional document offering the additional rights described in
+                 Section 3.5.
+
+                 3.2. Availability of Source Code.
+                 Any Modification which You create or to which You contribute must be
+                 made available in Source Code form under the terms of this License
+                 either on the same media as an Executable version or via an accepted
+                 Electronic Distribution Mechanism to anyone to whom you made an
+                 Executable version available; and if made available via Electronic
+                 Distribution Mechanism, must remain available for at least twelve (12)
+                 months after the date it initially became available, or at least six
+                 (6) months after a subsequent version of that particular Modification
+                 has been made available to such recipients. You are responsible for
+                 ensuring that the Source Code version remains available even if the
+                 Electronic Distribution Mechanism is maintained by a third party.
+
+                 3.3. Description of Modifications.
+                 You must cause all Covered Code to which You contribute to contain a
+                 file documenting the changes You made to create that Covered Code and
+                 the date of any change. You must include a prominent statement that
+                 the Modification is derived, directly or indirectly, from Original
+                 Code provided by the Initial Developer and including the name of the
+                 Initial Developer in (a) the Source Code, and (b) in any notice in an
+                 Executable version or related documentation in which You describe the
+                 origin or ownership of the Covered Code.
+
+                 3.4. Intellectual Property Matters
+                      (a) Third Party Claims.
+                      If Contributor has knowledge that a license under a third party's
+                      intellectual property rights is required to exercise the rights
+                      granted by such Contributor under Sections 2.1 or 2.2,
+                      Contributor must include a text file with the Source Code
+                      distribution titled "LEGAL" which describes the claim and the
+                      party making the claim in sufficient detail that a recipient will
+                      know whom to contact. If Contributor obtains such knowledge after
+                      the Modification is made available as described in Section 3.2,
+                      Contributor shall promptly modify the LEGAL file in all copies
+                      Contributor makes available thereafter and shall take other steps
+                      (such as notifying appropriate mailing lists or newsgroups)
+                      reasonably calculated to inform those who received the Covered
+                      Code that new knowledge has been obtained.
+
+                      (b) Contributor APIs.
+                      If Contributor's Modifications include an application programming
+                      interface and Contributor has knowledge of patent licenses which
+                      are reasonably necessary to implement that API, Contributor must
+                      also include this information in the LEGAL file.
+
+                           (c)    Representations.
+                      Contributor represents that, except as disclosed pursuant to
+                      Section 3.4(a) above, Contributor believes that Contributor's
+                      Modifications are Contributor's original creation(s) and/or
+                      Contributor has sufficient rights to grant the rights conveyed by
+                      this License.
+
+                 3.5. Required Notices.
+                 You must duplicate the notice in Exhibit A in each file of the Source
+                 Code.  If it is not possible to put such notice in a particular Source
+                 Code file due to its structure, then You must include such notice in a
+                 location (such as a relevant directory) where a user would be likely
+                 to look for such a notice.  If You created one or more Modification(s)
+                 You may add your name as a Contributor to the notice described in
+                 Exhibit A.  You must also duplicate this License in any documentation
+                 for the Source Code where You describe recipients' rights or ownership
+                 rights relating to Covered Code.  You may choose to offer, and to
+                 charge a fee for, warranty, support, indemnity or liability
+                 obligations to one or more recipients of Covered Code. However, You
+                 may do so only on Your own behalf, and not on behalf of the Initial
+                 Developer or any Contributor. You must make it absolutely clear than
+                 any such warranty, support, indemnity or liability obligation is
+                 offered by You alone, and You hereby agree to indemnify the Initial
+                 Developer and every Contributor for any liability incurred by the
+                 Initial Developer or such Contributor as a result of warranty,
+                 support, indemnity or liability terms You offer.
+
+                 3.6. Distribution of Executable Versions.
+                 You may distribute Covered Code in Executable form only if the
+                 requirements of Section 3.1-3.5 have been met for that Covered Code,
+                 and if You include a notice stating that the Source Code version of
+                 the Covered Code is available under the terms of this License,
+                 including a description of how and where You have fulfilled the
+                 obligations of Section 3.2. The notice must be conspicuously included
+                 in any notice in an Executable version, related documentation or
+                 collateral in which You describe recipients' rights relating to the
+                 Covered Code. You may distribute the Executable version of Covered
+                 Code or ownership rights under a license of Your choice, which may
+                 contain terms different from this License, provided that You are in
+                 compliance with the terms of this License and that the license for the
+                 Executable version does not attempt to limit or alter the recipient's
+                 rights in the Source Code version from the rights set forth in this
+                 License. If You distribute the Executable version under a different
+                 license You must make it absolutely clear that any terms which differ
+                 from this License are offered by You alone, not by the Initial
+                 Developer or any Contributor. You hereby agree to indemnify the
+                 Initial Developer and every Contributor for any liability incurred by
+                 the Initial Developer or such Contributor as a result of any such
+                 terms You offer.
+
+                 3.7. Larger Works.
+                 You may create a Larger Work by combining Covered Code with other code
+                 not governed by the terms of this License and distribute the Larger
+                 Work as a single product. In such a case, You must make sure the
+                 requirements of this License are fulfilled for the Covered Code.
+
+            4. Inability to Comply Due to Statute or Regulation.
+
+                 If it is impossible for You to comply with any of the terms of this
+                 License with respect to some or all of the Covered Code due to
+                 statute, judicial order, or regulation then You must: (a) comply with
+                 the terms of this License to the maximum extent possible; and (b)
+                 describe the limitations and the code they affect. Such description
+                 must be included in the LEGAL file described in Section 3.4 and must
+                 be included with all distributions of the Source Code. Except to the
+                 extent prohibited by statute or regulation, such description must be
+                 sufficiently detailed for a recipient of ordinary skill to be able to
+                 understand it.
+
+            5. Application of this License.
+
+                 This License applies to code to which the Initial Developer has
+                 attached the notice in Exhibit A and to related Covered Code.
+
+            6. Versions of the License.
+
+                 6.1. New Versions.
+                 Netscape Communications Corporation ("Netscape") may publish revised
+                 and/or new versions of the License from time to time. Each version
+                 will be given a distinguishing version number.
+
+                 6.2. Effect of New Versions.
+                 Once Covered Code has been published under a particular version of the
+                 License, You may always continue to use it under the terms of that
+                 version. You may also choose to use such Covered Code under the terms
+                 of any subsequent version of the License published by Netscape. No one
+                 other than Netscape has the right to modify the terms applicable to
+                 Covered Code created under this License.
+
+                 6.3. Derivative Works.
+                 If You create or use a modified version of this License (which you may
+                 only do in order to apply it to code which is not already Covered Code
+                 governed by this License), You must (a) rename Your license so that
+                 the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+                 "MPL", "NPL" or any confusingly similar phrase do not appear in your
+                 license (except to note that your license differs from this License)
+                 and (b) otherwise make it clear that Your version of the license
+                 contains terms which differ from the Mozilla Public License and
+                 Netscape Public License. (Filling in the name of the Initial
+                 Developer, Original Code or Contributor in the notice described in
+                 Exhibit A shall not of themselves be deemed to be modifications of
+                 this License.)
+
+            7. DISCLAIMER OF WARRANTY.
+
+                 COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+                 WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+                 WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+                 DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+                 THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+                 IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+                 YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+                 COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+                 OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+                 ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+            8. TERMINATION.
+
+                 8.1.  This License and the rights granted hereunder will terminate
+                 automatically if You fail to comply with terms herein and fail to cure
+                 such breach within 30 days of becoming aware of the breach. All
+                 sublicenses to the Covered Code which are properly granted shall
+                 survive any termination of this License. Provisions which, by their
+                 nature, must remain in effect beyond the termination of this License
+                 shall survive.
+
+                 8.2.  If You initiate litigation by asserting a patent infringement
+                 claim (excluding declatory judgment actions) against Initial Developer
+                 or a Contributor (the Initial Developer or Contributor against whom
+                 You file such action is referred to as "Participant")  alleging that:
+
+                 (a)  such Participant's Contributor Version directly or indirectly
+                 infringes any patent, then any and all rights granted by such
+                 Participant to You under Sections 2.1 and/or 2.2 of this License
+                 shall, upon 60 days notice from Participant terminate prospectively,
+                 unless if within 60 days after receipt of notice You either: (i)
+                 agree in writing to pay Participant a mutually agreeable reasonable
+                 royalty for Your past and future use of Modifications made by such
+                 Participant, or (ii) withdraw Your litigation claim with respect to
+                 the Contributor Version against such Participant.  If within 60 days
+                 of notice, a reasonable royalty and payment arrangement are not
+                 mutually agreed upon in writing by the parties or the litigation claim
+                 is not withdrawn, the rights granted by Participant to You under
+                 Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+                 the 60 day notice period specified above.
+
+                 (b)  any software, hardware, or device, other than such Participant's
+                 Contributor Version, directly or indirectly infringes any patent, then
+                 any rights granted to You by such Participant under Sections 2.1(b)
+                 and 2.2(b) are revoked effective as of the date You first made, used,
+                 sold, distributed, or had made, Modifications made by that
+                 Participant.
+
+                 8.3.  If You assert a patent infringement claim against Participant
+                 alleging that such Participant's Contributor Version directly or
+                 indirectly infringes any patent where such claim is resolved (such as
+                 by license or settlement) prior to the initiation of patent
+                 infringement litigation, then the reasonable value of the licenses
+                 granted by such Participant under Sections 2.1 or 2.2 shall be taken
+                 into account in determining the amount or value of any payment or
+                 license.
+
+                 8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+                 all end user license agreements (excluding distributors and resellers)
+                 which have been validly granted by You or any distributor hereunder
+                 prior to termination shall survive termination.
+
+            9. LIMITATION OF LIABILITY.
+
+                 UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+                 (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+                 DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+                 OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+                 ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+                 CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+                 WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+                 COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+                 INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+                 LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+                 RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+                 PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+                 EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+                 THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+            10. U.S. GOVERNMENT END USERS.
+
+                 The Covered Code is a "commercial item," as that term is defined in
+                 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+                 software" and "commercial computer software documentation," as such
+                 terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+                 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+                 all U.S. Government End Users acquire Covered Code with only those
+                 rights set forth herein.
+
+            11. MISCELLANEOUS.
+
+                 This License represents the complete agreement concerning subject
+                 matter hereof. If any provision of this License is held to be
+                 unenforceable, such provision shall be reformed only to the extent
+                 necessary to make it enforceable. This License shall be governed by
+                 California law provisions (except to the extent applicable law, if
+                 any, provides otherwise), excluding its conflict-of-law provisions.
+                 With respect to disputes in which at least one party is a citizen of,
+                 or an entity chartered or registered to do business in the United
+                 States of America, any litigation relating to this License shall be
+                 subject to the jurisdiction of the Federal Courts of the Northern
+                 District of California, with venue lying in Santa Clara County,
+                 California, with the losing party responsible for costs, including
+                 without limitation, court costs and reasonable attorneys' fees and
+                 expenses. The application of the United Nations Convention on
+                 Contracts for the International Sale of Goods is expressly excluded.
+                 Any law or regulation which provides that the language of a contract
+                 shall be construed against the drafter shall not apply to this
+                 License.
+
+            12. RESPONSIBILITY FOR CLAIMS.
+
+                 As between Initial Developer and the Contributors, each party is
+                 responsible for claims and damages arising, directly or indirectly,
+                 out of its utilization of rights under this License and You agree to
+                 work with Initial Developer and Contributors to distribute such
+                 responsibility on an equitable basis. Nothing herein is intended or
+                 shall be deemed to constitute any admission of liability.
+
+            13. MULTIPLE-LICENSED CODE.
+
+                 Initial Developer may designate portions of the Covered Code as
+                 "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+                 Developer permits you to utilize portions of the Covered Code under
+                 Your choice of the NPL or the alternative licenses, if any, specified
+                 by the Initial Developer in the file described in Exhibit A.
+
+            EXHIBIT A -Mozilla Public License.
+
+                 ``The contents of this file are subject to the Mozilla Public License
+                 Version 1.1 (the "License"); you may not use this file except in
+                 compliance with the License. You may obtain a copy of the License at
+                 http://www.mozilla.org/MPL/
+
+                 Software distributed under the License is distributed on an "AS IS"
+                 basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+                 License for the specific language governing rights and limitations
+                 under the License.
+
+                 The Original Code is RabbitMQ.
+
+                 The Initial Developer of the Original Code is VMware, Ltd..
+                 Portions created by VMware, Ltd. are Copyright (C)
+                 2007-2012 VMware, Inc.. All Rights Reserved.
+
+                 Contributor(s): .
+
+                 Alternatively, the contents of this file may be used under the terms
+                 of the GNU General Public License Version 2 license (the  "[GPL] License"), in which case the
+                 provisions of [GPL] License are applicable instead of those
+                 above.  If you wish to allow use of your version of this file only
+                 under the terms of the [GPL] License and not to allow others to use
+                 your version of this file under the MPL, indicate your decision by
+                 deleting  the provisions above and replace  them with the notice and
+                 other provisions required by the [GPL] License.  If you do not delete
+                 the provisions above, a recipient may use your version of this file
+                 under either the MPL or the [GPL] License."
+
+                 [NOTE: The text of this Exhibit A may differ slightly from the text of
+                 the notices in the Source Code files of the Original Code. You should
+                 use the text of this Exhibit A rather than the text found in the
+                 Original Code Source Code for Your Modifications.]
+
+
+        from VMware, Inc  http://www.vmware.com/
+            rabbitmq-client.jar  from http://www.rabbitmq.com/java-client.html
+
+
 Within the patches/systemvm/debian/config/etc directory
     placed in the public domain
         by Adiscon GmbH  http://www.adiscon.com/ 
@@ -2980,7 +3462,7 @@
             
             The Covered Software is a "commercial item," as that term is
             defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
-            computer software" (as that term is defined at 48 C.F.R. ¤
+            computer software" (as that term is defined at 48 C.F.R. �
             252.227-7014(a)(1)) and "commercial computer software
             documentation" as such terms are used in 48 C.F.R. 12.212 (Sept.
             1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1
@@ -3369,7 +3851,7 @@
             
             The Covered Software is a "commercial item," as that term is
             defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
-            computer software" (as that term is defined at 48 C.F.R. ¤
+            computer software" (as that term is defined at 48 C.F.R. �
             252.227-7014(a)(1)) and "commercial computer software
             documentation" as such terms are used in 48 C.F.R. 12.212 (Sept.
             1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1
@@ -3895,7 +4377,7 @@
 
     licensed under the MIT License http://www.opensource.org/licenses/mit-license.php  (as follows)
 
-            Copyright (C) 2008      Tóth István <stoty@tvnet.hu>
+            Copyright (C) 2008      T�th Istv�n <stoty@tvnet.hu>
                           2008-2012 Daniel Veillard <veillard@redhat.com>
                           2009-2011 Bryan Kearney <bkearney@redhat.com> 
             
@@ -4026,7 +4508,7 @@
 
     licensed under the MIT License http://www.opensource.org/licenses/mit-license.php  (as follows)
 
-            Copyright (c) 2006 - 2011 Jörn Zaefferer 
+            Copyright (c) 2006 - 2011 J�rn Zaefferer
             
             Permission is hereby granted, free  of charge, to any person obtaining
             a  copy  of this  software  and  associated  documentation files  (the
@@ -4221,7 +4703,7 @@
 Within the ui/lib/qunit directory
     licensed under the MIT License http://www.opensource.org/licenses/mit-license.php  (as follows)
 
-            Copyright (c) 2012 John Resig, Jörn Zaefferer 
+            Copyright (c) 2012 John Resig, J�rn Zaefferer
             
             Permission is hereby granted, free  of charge, to any person obtaining
             a  copy  of this  software  and  associated  documentation files  (the
diff --git a/tools/whisker/descriptor-for-packaging.xml b/tools/whisker/descriptor-for-packaging.xml
index 0144dbc..29a8284 100644
--- a/tools/whisker/descriptor-for-packaging.xml
+++ b/tools/whisker/descriptor-for-packaging.xml
@@ -2423,6 +2423,10 @@
             id='adiscon.com'
             name='Adiscon GmbH'
             url='http://www.adiscon.com/' />
+        <organisation
+            id='vmware.com'
+            name='VMware, Inc'
+            url='http://www.vmware.com/' />
     </organisations>
     <primary-license id='ApacheLicenseVersion2'>
         <copyright-notice>Copyright (c) 2013 The Apache Software Foundation</copyright-notice>
@@ -2948,5 +2952,19 @@
                    <resource name='javassist-3.9.0.GA.jar' source='http://sourceforge.net/projects/jboss/files/Javassist/' />
                </by-organisation>
         </with-license>
+        <with-license id='MPL1'>
+            <copyright-notice>Copyright (c) 2007-2012 VMware, Inc.  All Rights Reserved.</copyright-notice>
+            <license-parameters>
+                <parameter><name>PROJECT</name><value>RabbitMQ</value></parameter>
+                <parameter><name>INITIAL_DEVELOPER</name><value>VMware, Ltd.</value></parameter>
+                <parameter><name>INITIAL_DEVELOPER_COPYRIGHT</name><value>2007-2012 VMware, Inc.</value></parameter>
+                <parameter><name>CONTRIBUTORS</name><value></value></parameter>
+                <parameter><name>ALT_LIC_NAME</name><value>GNU General Public License Version 2</value></parameter>
+                <parameter><name>ALT_LIC_SHORT</name><value>GPL</value></parameter>
+               </license-parameters>
+               <by-organisation id='vmware.com'>
+                   <resource name='rabbitmq-client.jar' source='http://www.rabbitmq.com/java-client.html' />
+               </by-organisation>
+        </with-license>
     </within>
 </manifest>
diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js
index a30b619..8e4ed7e 100644
--- a/ui/scripts/ui-custom/instanceWizard.js
+++ b/ui/scripts/ui-custom/instanceWizard.js
@@ -875,6 +875,9 @@
             if (index) {
               if (index == $steps.size() - 1 && $networkStep.hasClass('next-use-security-groups')) {
                 showStep(5);
+              } else if ($activeStep.find('.select-security-group:visible').size() &&
+                         $activeStep.find('.select-network.no-add-network').size()) {
+                showStep(5);
               } else {
                 showStep(index);
               }
diff --git a/utils/pom.xml b/utils/pom.xml
index 87c078a..6f45044 100644
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -129,6 +129,11 @@
       <version>${cs.ejb.version}</version>
     </dependency>
     <dependency>
+        <groupId>com.googlecode.java-ipv6</groupId>
+        <artifactId>java-ipv6</artifactId>
+        <version>${cs.java-ipv6.version}</version>
+    </dependency>
+    <dependency>
       <groupId>commons-configuration</groupId>
       <artifactId>commons-configuration</artifactId>
       <version>${cs.configuration.version}</version>
diff --git a/utils/src/com/cloud/utils/net/NetUtils.java b/utils/src/com/cloud/utils/net/NetUtils.java
index c456cdc..fdda5b8 100755
--- a/utils/src/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/com/cloud/utils/net/NetUtils.java
@@ -41,6 +41,10 @@
 import org.apache.log4j.Logger;
 import org.apache.log4j.xml.DOMConfigurator;
 
+import com.googlecode.ipv6.IPv6Address;
+import com.googlecode.ipv6.IPv6AddressRange;
+import com.googlecode.ipv6.IPv6Network;
+
 import com.cloud.utils.IteratorUtil;
 import com.cloud.utils.Pair;
 import com.cloud.utils.script.Script;
@@ -1142,4 +1146,114 @@
         
         return true;
     }
+
+	public static boolean isValidIpv6(String ip) {
+		try {
+			IPv6Address address = IPv6Address.fromString(ip);
+		} catch (IllegalArgumentException ex) {
+			return false;
+		}
+		return true;
+	}
+
+	public static boolean isValidIp6Cidr(String ip6Cidr) {
+		try {
+			IPv6Network network = IPv6Network.fromString(ip6Cidr);
+		} catch (IllegalArgumentException ex) {
+			return false;
+		}
+		return true;
+	}
+
+	public static int getIp6CidrSize(String ip6Cidr) {
+		IPv6Network network = null;
+		try {
+			network = IPv6Network.fromString(ip6Cidr);
+		} catch (IllegalArgumentException ex) {
+			return 0;
+		}
+		return network.getNetmask().asPrefixLength();
+	}
+
+	//FIXME: only able to cover lower 32 bits
+	public static String getIp6FromRange(String ip6Range) {
+    	String[] ips = ip6Range.split("-");
+    	String startIp = ips[0];
+    	IPv6Address start = IPv6Address.fromString(startIp);
+    	// Find a random number based on lower 32 bits
+    	long gap = countIp6InRange(ip6Range);
+    	if (gap > Integer.MAX_VALUE) {
+    		gap = Integer.MAX_VALUE;
+    	}
+    	int next = _rand.nextInt((int)(gap));
+    	// And a number based on the difference of lower 32 bits
+    	IPv6Address ip = start.add(next);
+    	return ip.toString();
+	}
+
+	//RFC3315, section 9.4
+	public static String getDuidLL(String macAddress) {
+		String duid = "00:03:00:01:" + macAddress;
+		return duid;
+	}
+	
+	//FIXME: only able to cover lower 64 bits
+	public static long countIp6InRange(String ip6Range) {
+    	String[] ips = ip6Range.split("-");
+    	String startIp = ips[0];
+    	String endIp = null;
+    	if (ips.length > 1) {
+    		endIp = ips[1];
+    	}
+    	IPv6Address start, end;
+    	try {
+    		start = IPv6Address.fromString(startIp);
+    		end = IPv6Address.fromString(endIp);
+		} catch (IllegalArgumentException ex) {
+			return 0;
+		}
+    	long startLow = start.getLowBits(), endLow = end.getLowBits();
+    	if (startLow > endLow) {
+    		return 0;
+    	}
+    	return endLow - startLow + 1;
+	}
+
+	public static boolean isIp6InRange(String ip6, String ip6Range) {
+    	String[] ips = ip6Range.split("-");
+    	String startIp = ips[0];
+    	String endIp = null;
+    	if (ips.length > 1) {
+    		endIp = ips[1];
+    	}
+    	IPv6Address start = IPv6Address.fromString(startIp);
+    	IPv6Address end = IPv6Address.fromString(endIp);
+    	IPv6Address ip = IPv6Address.fromString(ip6);
+    	if (start.compareTo(ip) <= 0 && end.compareTo(ip) >= 0) {
+    		return true;
+    	}
+		return false;
+	}
+	
+	public static boolean isIp6RangeOverlap(String ipRange1, String ipRange2) {
+		String[] ips = ipRange1.split("-");
+    	String startIp1 = ips[0];
+    	String endIp1 = null;
+    	if (ips.length > 1) {
+    		endIp1 = ips[1];
+    	}
+    	IPv6Address start1 = IPv6Address.fromString(startIp1);
+    	IPv6Address end1 = IPv6Address.fromString(endIp1);
+    	IPv6AddressRange range1 = IPv6AddressRange.fromFirstAndLast(start1, end1);
+		ips = ipRange2.split("-");
+    	String startIp2 = ips[0];
+    	String endIp2 = null;
+    	if (ips.length > 1) {
+    		endIp2 = ips[1];
+    	}
+    	IPv6Address start2 = IPv6Address.fromString(startIp2);
+    	IPv6Address end2 = IPv6Address.fromString(endIp2);
+    	IPv6AddressRange range2 = IPv6AddressRange.fromFirstAndLast(start2, end2);
+    	return range1.overlaps(range2);
+	}
 }
diff --git a/utils/test/com/cloud/utils/net/NetUtilsTest.java b/utils/test/com/cloud/utils/net/NetUtilsTest.java
index 1eccba3..5336d93 100644
--- a/utils/test/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/test/com/cloud/utils/net/NetUtilsTest.java
@@ -16,16 +16,20 @@
 // under the License.
 package com.cloud.utils.net;
 
-import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
 import junit.framework.TestCase;
 
+import org.apache.log4j.Logger;
 import org.junit.Test;
 
+import com.googlecode.ipv6.IPv6Address;
+
 public class NetUtilsTest extends TestCase {
 
+    private static final Logger s_logger = Logger.getLogger(NetUtilsTest.class);
+    
     @Test
     public void testGetRandomIpFromCidr() {
         String cidr = "192.168.124.1";
@@ -69,4 +73,33 @@
         assertFalse(NetUtils.isValidS2SVpnPolicy(";modp1536"));
         assertFalse(NetUtils.isValidS2SVpnPolicy(",aes;modp1536,,,"));
     }
+    
+    public void testIpv6() {
+    	assertTrue(NetUtils.isValidIpv6("fc00::1"));
+    	assertFalse(NetUtils.isValidIpv6(""));
+    	assertFalse(NetUtils.isValidIpv6(null));
+    	assertFalse(NetUtils.isValidIpv6("1234:5678::1/64"));
+    	assertTrue(NetUtils.isValidIp6Cidr("1234:5678::1/64"));
+    	assertFalse(NetUtils.isValidIp6Cidr("1234:5678::1"));
+    	assertEquals(NetUtils.getIp6CidrSize("1234:5678::1/32"), 32);
+    	assertEquals(NetUtils.getIp6CidrSize("1234:5678::1"), 0);
+    	assertEquals(NetUtils.countIp6InRange("1234:5678::1-1234:5678::2"), 2);
+    	assertEquals(NetUtils.countIp6InRange("1234:5678::2-1234:5678::0"), 0);
+    	assertEquals(NetUtils.getIp6FromRange("1234:5678::1-1234:5678::1"), "1234:5678::1");
+    	String ipString = null;
+    	IPv6Address ipStart = IPv6Address.fromString("1234:5678::1");
+    	IPv6Address ipEnd = IPv6Address.fromString("1234:5678::8000:0000");
+    	for (int i = 0; i < 10; i ++) {
+    		ipString = NetUtils.getIp6FromRange(ipStart.toString() + "-" + ipEnd.toString());
+    		s_logger.info("IP is " + ipString);
+    		IPv6Address ip = IPv6Address.fromString(ipString);
+    		assertTrue(ip.compareTo(ipStart) >= 0);
+    		assertTrue(ip.compareTo(ipEnd) <= 0);
+    	}
+    	assertFalse(NetUtils.isIp6RangeOverlap("1234:5678::1-1234:5678::ffff", "1234:5678:1::1-1234:5678:1::ffff"));
+    	assertTrue(NetUtils.isIp6RangeOverlap("1234:5678::1-1234:5678::ffff", "1234:5678::2-1234:5678::f"));
+    	assertTrue(NetUtils.isIp6RangeOverlap("1234:5678::f-1234:5678::ffff", "1234:5678::2-1234:5678::f"));
+    	assertFalse(NetUtils.isIp6RangeOverlap("1234:5678::f-1234:5678::ffff", "1234:5678::2-1234:5678::e"));
+    	assertFalse(NetUtils.isIp6RangeOverlap("1234:5678::f-1234:5678::f", "1234:5678::2-1234:5678::e"));
+    }
 }