You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ra...@apache.org on 2018/04/26 13:17:28 UTC

[cloudstack] branch master updated (4c42aaf -> b3c22df)

This is an automated email from the ASF dual-hosted git repository.

rafael pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git.


    from 4c42aaf  [CLOUDSTACK-10356] Fix NPE in Cloudstack found with NPEDetector  (#2573)
     add b6d420b  CLOUDSTACK-9677: Adding storage policy support for swift as secondary storage (#2412)
     add 8520324  ConfigDrive fixes: CLOUDSTACK-10288, CLOUDSTACK-10289 (#2566)
     new b3c22df  Forward merge branch '4.11' to master

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../main/java/com/cloud/agent/api/to/SwiftTO.java  |    9 +-
 .../main/java/com/cloud/network/NetworkModel.java  |    7 +-
 .../org/apache/cloudstack/api/ApiConstants.java    |    1 +
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |    7 +-
 .../resource/XenServerStorageProcessor.java        |  115 +-
 .../resource/XenServerStorageProcessorTest.java    |  113 ++
 .../driver/SwiftImageStoreDriverImpl.java          |    2 +-
 scripts/storage/secondary/swift                    |   21 +-
 scripts/vm/hypervisor/xenserver/swift              |   21 +-
 .../java/com/cloud/network/NetworkModelImpl.java   |   48 +-
 .../network/element/ConfigDriveNetworkElement.java |   28 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |    5 +-
 .../com/cloud/network/MockNetworkModelImpl.java    |    2 +-
 .../element/ConfigDriveNetworkElementTest.java     |   91 +-
 .../java/com/cloud/vpc/MockNetworkModelImpl.java   |    2 +-
 .../resource/NfsSecondaryStorageResource.java      |   17 +-
 test/integration/component/test_configdrive.py     | 1099 ++++++++++++--------
 test/integration/plugins/nuagevsp/nuageTestCase.py |   11 +-
 test/integration/plugins/nuagevsp/nuage_lib.py     |   47 +
 .../plugins/nuagevsp/test_nuage_configdrive.py     |  649 ++----------
 tools/marvin/marvin/jsonHelper.py                  |    6 +
 ui/l10n/en.js                                      |    1 +
 ui/scripts/system.js                               |   29 +-
 utils/src/main/java/com/cloud/utils/SwiftUtil.java |  187 ++--
 .../test/java/com/cloud/utils/SwiftUtilTest.java   |  150 ++-
 25 files changed, 1485 insertions(+), 1183 deletions(-)
 create mode 100644 plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessorTest.java
 create mode 100644 test/integration/plugins/nuagevsp/nuage_lib.py

-- 
To stop receiving notification emails like this one, please contact
rafael@apache.org.

[cloudstack] 01/01: Forward merge branch '4.11' to master

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rafael pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit b3c22df71d0ec3c29fbe37dc3c9812353c1685d9
Merge: 4c42aaf 8520324
Author: Rafael Weingärtner <ra...@gmail.com>
AuthorDate: Thu Apr 26 10:14:49 2018 -0300

    Forward merge branch '4.11' to master
    
    ConfigDrive fixes: CLOUDSTACK-10288, CLOUDSTACK-10289 (#2566)
    CLOUDSTACK-9677: Adding storage policy support for swift as secondary
    storage (#2412)

 .../main/java/com/cloud/agent/api/to/SwiftTO.java  |    9 +-
 .../main/java/com/cloud/network/NetworkModel.java  |    7 +-
 .../org/apache/cloudstack/api/ApiConstants.java    |    1 +
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |    7 +-
 .../resource/XenServerStorageProcessor.java        |  115 +-
 .../resource/XenServerStorageProcessorTest.java    |  113 ++
 .../driver/SwiftImageStoreDriverImpl.java          |    2 +-
 scripts/storage/secondary/swift                    |   21 +-
 scripts/vm/hypervisor/xenserver/swift              |   21 +-
 .../java/com/cloud/network/NetworkModelImpl.java   |   48 +-
 .../network/element/ConfigDriveNetworkElement.java |   28 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |    5 +-
 .../com/cloud/network/MockNetworkModelImpl.java    |    2 +-
 .../element/ConfigDriveNetworkElementTest.java     |   91 +-
 .../java/com/cloud/vpc/MockNetworkModelImpl.java   |    2 +-
 .../resource/NfsSecondaryStorageResource.java      |   17 +-
 test/integration/component/test_configdrive.py     | 1099 ++++++++++++--------
 test/integration/plugins/nuagevsp/nuageTestCase.py |   11 +-
 test/integration/plugins/nuagevsp/nuage_lib.py     |   47 +
 .../plugins/nuagevsp/test_nuage_configdrive.py     |  649 ++----------
 tools/marvin/marvin/jsonHelper.py                  |    6 +
 ui/l10n/en.js                                      |    1 +
 ui/scripts/system.js                               |   29 +-
 utils/src/main/java/com/cloud/utils/SwiftUtil.java |  187 ++--
 .../test/java/com/cloud/utils/SwiftUtilTest.java   |  150 ++-
 25 files changed, 1485 insertions(+), 1183 deletions(-)

diff --cc api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 56d5957,0000000..3a5c16f
mode 100644,000000..100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@@ -1,730 -1,0 +1,731 @@@
 +// 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.api;
 +
 +public class ApiConstants {
 +    public static final String ACCOUNT = "account";
 +    public static final String ACCOUNTS = "accounts";
 +    public static final String ACCOUNT_TYPE = "accounttype";
 +    public static final String ACCOUNT_ID = "accountid";
 +    public static final String ACTIVITY = "activity";
 +    public static final String ADDRESS = "address";
 +    public static final String ALGORITHM = "algorithm";
 +    public static final String ALLOCATED_ONLY = "allocatedonly";
 +    public static final String ANNOTATION = "annotation";
 +    public static final String API_KEY = "apikey";
 +    public static final String ASYNC_BACKUP = "asyncbackup";
 +    public static final String USER_API_KEY = "userapikey";
 +    public static final String APPLIED = "applied";
 +    public static final String LIST_LB_VMIPS = "lbvmips";
 +    public static final String AVAILABLE = "available";
 +    public static final String BITS = "bits";
 +    public static final String BOOTABLE = "bootable";
 +    public static final String BIND_DN = "binddn";
 +    public static final String BIND_PASSWORD = "bindpass";
 +    public static final String BYTES_READ_RATE = "bytesreadrate";
 +    public static final String BYTES_WRITE_RATE = "byteswriterate";
 +    public static final String BYPASS_VLAN_OVERLAP_CHECK = "bypassvlanoverlapcheck";
 +    public static final String CATEGORY = "category";
 +    public static final String CAN_REVERT = "canrevert";
 +    public static final String CA_CERTIFICATES = "cacertificates";
 +    public static final String CERTIFICATE = "certificate";
 +    public static final String CERTIFICATE_CHAIN = "certchain";
 +    public static final String CERTIFICATE_FINGERPRINT = "fingerprint";
 +    public static final String CERTIFICATE_ID = "certid";
 +    public static final String CSR = "csr";
 +    public static final String PRIVATE_KEY = "privatekey";
 +    public static final String DOMAIN_SUFFIX = "domainsuffix";
 +    public static final String DNS_SEARCH_ORDER = "dnssearchorder";
 +    public static final String CHAIN_INFO = "chaininfo";
 +    public static final String CIDR = "cidr";
 +    public static final String IP6_CIDR = "ip6cidr";
 +    public static final String CIDR_LIST = "cidrlist";
 +    public static final String DEST_CIDR_LIST = "destcidrlist";
 +    public static final String CLEANUP = "cleanup";
 +    public static final String MAKEREDUNDANTE = "makeredundant";
 +    public static final String CLUSTER_ID = "clusterid";
 +    public static final String CLUSTER_NAME = "clustername";
 +    public static final String CLUSTER_TYPE = "clustertype";
 +    public static final String CN = "cn";
 +    public static final String COMMAND = "command";
 +    public static final String CMD_EVENT_TYPE = "cmdeventtype";
 +    public static final String COMPONENT = "component";
 +    public static final String CPU_NUMBER = "cpunumber";
 +    public static final String CPU_SPEED = "cpuspeed";
 +    public static final String CREATED = "created";
 +    public static final String CTX_ACCOUNT_ID = "ctxaccountid";
 +    public static final String CTX_DETAILS = "ctxDetails";
 +    public static final String CTX_USER_ID = "ctxuserid";
 +    public static final String CTXSTARTEVENTID = "ctxstarteventid";
 +    public static final String CTX_START_EVENT_ID = "ctxStartEventId";
 +    public static final String CUSTOMIZED = "customized";
 +    public static final String CUSTOMIZED_IOPS = "customizediops";
 +    public static final String CUSTOM_ID = "customid";
 +    public static final String CUSTOM_JOB_ID = "customjobid";
 +    public static final String MIN_IOPS = "miniops";
 +    public static final String MAX_IOPS = "maxiops";
 +    public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
 +    public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist";
 +    public static final String DESCRIPTION = "description";
 +    public static final String DESTINATION_ZONE_ID = "destzoneid";
 +    public static final String DETAILS = "details";
 +    public static final String DEVICE_ID = "deviceid";
 +    public static final String DIRECT_DOWNLOAD = "directdownload";
 +    public static final String DISK_OFFERING_ID = "diskofferingid";
 +    public static final String DISK_SIZE = "disksize";
 +    public static final String UTILIZATION = "utilization";
 +    public static final String DRIVER = "driver";
 +    public static final String ROOT_DISK_SIZE = "rootdisksize";
 +    public static final String DHCP_OPTIONS_NETWORK_LIST = "dhcpoptionsnetworklist";
 +    public static final String DHCP_OPTIONS = "dhcpoptions";
 +    public static final String DHCP_PREFIX = "dhcp:";
 +    public static final String DISPLAY_NAME = "displayname";
 +    public static final String DISPLAY_NETWORK = "displaynetwork";
 +    public static final String DISPLAY_NIC = "displaynic";
 +    public static final String DISPLAY_TEXT = "displaytext";
 +    public static final String DISPLAY_VM = "displayvm";
 +    public static final String DISPLAY_OFFERING = "displayoffering";
 +    public static final String DISPLAY_VOLUME = "displayvolume";
 +    public static final String DNS1 = "dns1";
 +    public static final String DNS2 = "dns2";
 +    public static final String IP6_DNS1 = "ip6dns1";
 +    public static final String IP6_DNS2 = "ip6dns2";
 +    public static final String DOMAIN = "domain";
 +    public static final String DOMAIN_ID = "domainid";
 +    public static final String DOMAIN__ID = "domainId";
 +    public static final String DURATION = "duration";
 +    public static final String ELIGIBLE = "eligible";
 +    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 EXPIRES = "expires";
 +    public static final String EXTRA_DHCP_OPTION = "extradhcpoption";
 +    public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname";
 +    public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode";
 +    public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue";
 +    public static final String FENCE = "fence";
 +    public static final String FETCH_LATEST = "fetchlatest";
 +    public static final String FIRSTNAME = "firstname";
 +    public static final String FORCED = "forced";
 +    public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
 +    public static final String FORMAT = "format";
 +    public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
 +    public static final String FOR_SYSTEM_VMS = "forsystemvms";
 +    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 GSLB_LB_METHOD = "gslblbmethod";
 +    public static final String GSLB_SERVICE_DOMAIN_NAME = "gslbdomainname";
 +    public static final String GSLB_SERVICE_TYPE = "gslbservicetype";
 +    public static final String GSLB_STICKY_SESSION_METHOD = "gslbstickysessionmethodname";
 +    public static final String GSLB_LBRULE_WEIGHT_MAP = "gslblbruleweightsmap";
 +    public static final String GUEST_CIDR_ADDRESS = "guestcidraddress";
 +    public static final String GUEST_VLAN_RANGE = "guestvlanrange";
 +    public static final String HA_ENABLE = "haenable";
 +    public static final String HA_PROVIDER = "haprovider";
 +    public static final String HA_STATE = "hastate";
 +    public static final String HEALTH = "health";
 +    public static final String HOST_ID = "hostid";
 +    public static final String HOST_NAME = "hostname";
 +    public static final String HYPERVISOR = "hypervisor";
 +    public static final String INLINE = "inline";
 +    public static final String INSTANCE = "instance";
 +    public static final String ICMP_CODE = "icmpcode";
 +    public static final String ICMP_TYPE = "icmptype";
 +    public static final String ID = "id";
 +    public static final String IDS = "ids";
 +    public static final String PREVIOUS_ACL_RULE_ID = "previousaclruleid";
 +    public static final String NEXT_ACL_RULE_ID = "nextaclruleid";
 +    public static final String INTERNAL_DNS1 = "internaldns1";
 +    public static final String INTERNAL_DNS2 = "internaldns2";
 +    public static final String INTERVAL_TYPE = "intervaltype";
 +    public static final String LOCATION_TYPE = "locationtype";
 +    public static final String IOPS_READ_RATE = "iopsreadrate";
 +    public static final String IOPS_WRITE_RATE = "iopswriterate";
 +    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";
 +    public static final String IP_LIMIT = "iplimit";
 +    public static final String IP_TOTAL = "iptotal";
 +    public static final String IS_CLEANUP_REQUIRED = "iscleanuprequired";
 +    public static final String IS_EXTRACTABLE = "isextractable";
 +    public static final String IS_FEATURED = "isfeatured";
 +    public static final String IS_PORTABLE = "isportable";
 +    public static final String IS_PUBLIC = "ispublic";
 +    public static final String IS_PERSISTENT = "ispersistent";
 +    public static final String EGRESS_DEFAULT_POLICY = "egressdefaultpolicy";
 +    public static final String IS_READY = "isready";
 +    public static final String IS_RECURSIVE = "isrecursive";
 +    public static final String ISO_FILTER = "isofilter";
 +    public static final String ISO_GUEST_OS_NONE = "None";
 +    public static final String JOB_ID = "jobid";
 +    public static final String JOB_STATUS = "jobstatus";
 +    public static final String LASTNAME = "lastname";
 +    public static final String LEVEL = "level";
 +    public static final String LENGTH = "length";
 +    public static final String LIMIT_CPU_USE = "limitcpuuse";
 +    public static final String LOCK = "lock";
 +    public static final String LUN = "lun";
 +    public static final String LBID = "lbruleid";
 +    public static final String MAX = "max";
 +    public static final String MAC_ADDRESS = "macaddress";
 +    public static final String MAX_SNAPS = "maxsnaps";
 +    public static final String MEMORY = "memory";
 +    public static final String MODE = "mode";
 +    public static final String KEEPALIVE_ENABLED = "keepaliveenabled";
 +    public static final String NAME = "name";
 +    public static final String METHOD_NAME = "methodname";
 +    public static final String NETWORK_DOMAIN = "networkdomain";
 +    public static final String NETMASK = "netmask";
 +    public static final String NEW_NAME = "newname";
 +    public static final String NUM_RETRIES = "numretries";
 +    public static final String OFFER_HA = "offerha";
 +    public static final String IS_SYSTEM_OFFERING = "issystem";
 +    public static final String IS_DEFAULT_USE = "defaultuse";
 +    public static final String OP = "op";
 +    public static final String OS_CATEGORY_ID = "oscategoryid";
 +    public static final String OS_TYPE_ID = "ostypeid";
 +    public static final String OS_DISPLAY_NAME = "osdisplayname";
 +    public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor";
 +    public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate";
 +    public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled";
 +    public static final String PARAMS = "params";
 +    public static final String PARENT_ID = "parentid";
 +    public static final String PARENT_DOMAIN_ID = "parentdomainid";
 +    public static final String PARENT_TEMPLATE_ID = "parenttemplateid";
 +    public static final String PASSWORD = "password";
 +    public static final String SHOULD_UPDATE_PASSWORD = "update_passwd_on_host";
 +    public static final String NEW_PASSWORD = "new_password";
 +    public static final String PASSWORD_ENABLED = "passwordenabled";
 +    public static final String SSHKEY_ENABLED = "sshkeyenabled";
 +    public static final String PATH = "path";
 +    public static final String POD_ID = "podid";
 +    public static final String POD_NAME = "podname";
 +    public static final String POD_IDS = "podids";
 +    public static final String POLICY_ID = "policyid";
 +    public static final String PORT = "port";
 +    public static final String PORTAL = "portal";
 +    public static final String PORTABLE_IP_ADDRESS = "portableipaddress";
 +    public static final String PORT_FORWARDING_SERVICE_ID = "portforwardingserviceid";
 +    public static final String POST_URL = "postURL";
 +    public static final String POWER_STATE = "powerstate";
 +    public static final String PRIVATE_INTERFACE = "privateinterface";
 +    public static final String PRIVATE_IP = "privateip";
 +    public static final String PRIVATE_PORT = "privateport";
 +    public static final String PRIVATE_START_PORT = "privateport";
 +    public static final String PRIVATE_END_PORT = "privateendport";
 +    public static final String PRIVATE_ZONE = "privatezone";
 +    public static final String PROTOCOL = "protocol";
 +    public static final String PROVISIONINGTYPE = "provisioningtype";
 +    public static final String PUBLIC_INTERFACE = "publicinterface";
 +    public static final String PUBLIC_IP_ID = "publicipid";
 +    public static final String PUBLIC_IP = "publicip";
 +    public static final String PUBLIC_PORT = "publicport";
 +    public static final String PUBLIC_START_PORT = "publicport";
 +    public static final String PUBLIC_END_PORT = "publicendport";
 +    public static final String PUBLIC_ZONE = "publiczone";
 +    public static final String RECEIVED_BYTES = "receivedbytes";
 +    public static final String RECONNECT = "reconnect";
 +    public static final String RECOVER = "recover";
 +    public static final String REQUIRES_HVM = "requireshvm";
 +    public static final String RESOURCE_TYPE = "resourcetype";
 +    public static final String RESOURCE_TYPE_NAME = "resourcetypename";
 +    public static final String RESPONSE = "response";
 +    public static final String REVERTABLE = "revertable";
 +    public static final String REGISTERED = "registered";
 +    public static final String QUERY_FILTER = "queryfilter";
 +    public static final String SCHEDULE = "schedule";
 +    public static final String SCOPE = "scope";
 +    public static final String SECRET_KEY = "usersecretkey";
 +    public static final String SECONDARY_IP = "secondaryip";
 +    public static final String SINCE = "since";
 +    public static final String KEY = "key";
 +    public static final String SEARCH_BASE = "searchbase";
 +    public static final String SECURITY_GROUP_IDS = "securitygroupids";
 +    public static final String SECURITY_GROUP_NAMES = "securitygroupnames";
 +    public static final String SECURITY_GROUP_NAME = "securitygroupname";
 +    public static final String SECURITY_GROUP_ID = "securitygroupid";
 +    public static final String SENT = "sent";
 +    public static final String SENT_BYTES = "sentbytes";
 +    public static final String SERIAL = "serial";
 +    public static final String SERVICE_OFFERING_ID = "serviceofferingid";
 +    public static final String SESSIONKEY = "sessionkey";
 +    public static final String SHOW_CAPACITIES = "showcapacities";
 +    public static final String SHOW_REMOVED = "showremoved";
 +    public static final String SIGNATURE = "signature";
 +    public static final String SIGNATURE_VERSION = "signatureversion";
 +    public static final String SIZE = "size";
 +    public static final String SNAPSHOT = "snapshot";
 +    public static final String SNAPSHOT_ID = "snapshotid";
 +    public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid";
 +    public static final String SNAPSHOT_TYPE = "snapshottype";
 +    public static final String SNAPSHOT_QUIESCEVM = "quiescevm";
 +    public static final String SOURCE_ZONE_ID = "sourcezoneid";
 +    public static final String START_DATE = "startdate";
 +    public static final String START_ID = "startid";
 +    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";
 +    public static final String STORAGE_TYPE = "storagetype";
++    public static final String STORAGE_POLICY = "storagepolicy";
 +    public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled";
 +    public static final String STORAGE_CAPABILITIES = "storagecapabilities";
 +    public static final String SYSTEM_VM_TYPE = "systemvmtype";
 +    public static final String TAGS = "tags";
 +    public static final String TARGET_IQN = "targetiqn";
 +    public static final String TEMPLATE_FILTER = "templatefilter";
 +    public static final String TEMPLATE_ID = "templateid";
 +    public static final String ISO_ID = "isoid";
 +    public static final String TIMEOUT = "timeout";
 +    public static final String TIMEZONE = "timezone";
 +    public static final String TIMEZONEOFFSET = "timezoneoffset";
 +    public static final String TYPE = "type";
 +    public static final String TRUST_STORE = "truststore";
 +    public static final String TRUST_STORE_PASSWORD = "truststorepass";
 +    public static final String URL = "url";
 +    public static final String USAGE_INTERFACE = "usageinterface";
 +    public static final String USER_DATA = "userdata";
 +    public static final String USER_ID = "userid";
 +    public static final String USE_SSL = "ssl";
 +    public static final String USERNAME = "username";
 +    public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist";
 +    public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork";
 +    public static final String Update_IN_SEQUENCE = "updateinsequence";
 +    public static final String VALUE = "value";
 +    public static final String VIRTUAL_MACHINE_ID = "virtualmachineid";
 +    public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids";
 +    public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
 +    public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
 +    public static final String USAGE_ID = "usageid";
 +    public static final String USAGE_TYPE = "usagetype";
 +    public static final String INCLUDE_TAGS = "includetags";
 +
 +    public static final String VLAN = "vlan";
 +    public static final String VLAN_RANGE = "vlanrange";
 +    public static final String REMOVE_VLAN = "removevlan";
 +    public static final String VLAN_ID = "vlanid";
 +    public static final String ISOLATED_PVLAN = "isolatedpvlan";
 +    public static final String ISOLATION_URI = "isolationuri";
 +    public static final String VM_AVAILABLE = "vmavailable";
 +    public static final String VM_LIMIT = "vmlimit";
 +    public static final String VM_TOTAL = "vmtotal";
 +    public static final String VNET = "vnet";
 +    public static final String IS_VOLATILE = "isvolatile";
 +    public static final String VOLUME_ID = "volumeid";
 +    public static final String ZONE_ID = "zoneid";
 +    public static final String ZONE_NAME = "zonename";
 +    public static final String NETWORK_TYPE = "networktype";
 +    public static final String PAGE = "page";
 +    public static final String PAGE_SIZE = "pagesize";
 +    public static final String COUNT = "count";
 +    public static final String TRAFFIC_TYPE = "traffictype";
 +    public static final String NETWORK_OFFERING_ID = "networkofferingid";
 +    public static final String TIER_NETWORK_OFFERINGS = "tiernetworkofferings";
 +    public static final String NETWORK_IDS = "networkids";
 +    public static final String NETWORK_ID = "networkid";
 +    public static final String NIC_ID = "nicid";
 +    public static final String SPECIFY_VLAN = "specifyvlan";
 +    public static final String IS_DEFAULT = "isdefault";
 +    public static final String IS_SYSTEM = "issystem";
 +    public static final String IS_USER_DEFINED = "isuserdefined";
 +    public static final String AVAILABILITY = "availability";
 +    public static final String NETWORKRATE = "networkrate";
 +    public static final String HOST_TAGS = "hosttags";
 +    public static final String SSH_KEYPAIR = "keypair";
 +    public static final String HTTPMETHOD = "httpmethod";
 +    public static final String HOST_CPU_CAPACITY = "hostcpucapacity";
 +    public static final String HOST_CPU_NUM = "hostcpunum";
 +    public static final String HOST_MEM_CAPACITY = "hostmemcapacity";
 +    public static final String HOST_MAC = "hostmac";
 +    public static final String HOST_TAG = "hosttag";
 +    public static final String PXE_SERVER_TYPE = "pxeservertype";
 +    public static final String LINMIN_USERNAME = "linminusername";
 +    public static final String LINMIN_PASSWORD = "linminpassword";
 +    public static final String LINMIN_APID = "linminapid";
 +    public static final String DHCP_SERVER_TYPE = "dhcpservertype";
 +    public static final String LINK_LOCAL_IP = "linklocalip";
 +    public static final String LINK_LOCAL_MAC_ADDRESS = "linklocalmacaddress";
 +    public static final String LINK_LOCAL_MAC_NETMASK = "linklocalnetmask";
 +    public static final String LINK_LOCAL_NETWORK_ID = "linklocalnetworkid";
 +    public static final String PRIVATE_MAC_ADDRESS = "privatemacaddress";
 +    public static final String PRIVATE_NETMASK = "privatenetmask";
 +    public static final String PRIVATE_NETWORK_ID = "privatenetworkid";
 +    public static final String ALLOCATION_STATE = "allocationstate";
 +    public static final String MANAGED_STATE = "managedstate";
 +    public static final String STORAGE_ID = "storageid";
 +    public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip";
 +    public static final String PING_DIR = "pingdir";
 +    public static final String TFTP_DIR = "tftpdir";
 +    public static final String PING_CIFS_USERNAME = "pingcifsusername";
 +    public static final String PING_CIFS_PASSWORD = "pingcifspassword";
 +    public static final String CHECKSUM = "checksum";
 +    public static final String NETWORK_DEVICE_TYPE = "networkdevicetype";
 +    public static final String NETWORK_DEVICE_PARAMETER_LIST = "networkdeviceparameterlist";
 +    public static final String ZONE_TOKEN = "zonetoken";
 +    public static final String DHCP_PROVIDER = "dhcpprovider";
 +    public static final String RESULT = "success";
 +    public static final String RESUME = "resume";
 +    public static final String LUN_ID = "lunId";
 +    public static final String IQN = "iqn";
 +    public static final String AGGREGATE_NAME = "aggregatename";
 +    public static final String POOL_NAME = "poolname";
 +    public static final String VOLUME_NAME = "volumename";
 +    public static final String SNAPSHOT_POLICY = "snapshotpolicy";
 +    public static final String SNAPSHOT_RESERVATION = "snapshotreservation";
 +    public static final String IP_NETWORK_LIST = "iptonetworklist";
 +    public static final String PARAM_LIST = "param";
 +    public static final String FOR_LOAD_BALANCING = "forloadbalancing";
 +    public static final String KEYBOARD = "keyboard";
 +    public static final String OPEN_FIREWALL = "openfirewall";
 +    public static final String TEMPLATE_TAG = "templatetag";
 +    public static final String HYPERVISOR_VERSION = "hypervisorversion";
 +    public static final String MAX_GUESTS_LIMIT = "maxguestslimit";
 +    public static final String MAX_DATA_VOLUMES_LIMIT = "maxdatavolumeslimit";
 +    public static final String MAX_HOSTS_PER_CLUSTER = "maxhostspercluster";
 +    public static final String PROJECT_ID = "projectid";
 +    public static final String PROJECT_IDS = "projectids";
 +    public static final String PROJECT = "project";
 +    public static final String ROLE = "role";
 +    public static final String ROLE_ID = "roleid";
 +    public static final String ROLE_TYPE = "roletype";
 +    public static final String ROLE_NAME = "rolename";
 +    public static final String PERMISSION = "permission";
 +    public static final String RULE = "rule";
 +    public static final String RULE_ID = "ruleid";
 +    public static final String RULE_ORDER = "ruleorder";
 +    public static final String USER = "user";
 +    public static final String ACTIVE_ONLY = "activeonly";
 +    public static final String TOKEN = "token";
 +    public static final String ACCEPT = "accept";
 +    public static final String SORT_KEY = "sortkey";
 +    public static final String ACCOUNT_DETAILS = "accountdetails";
 +    public static final String SERVICE_OFFERING_DETAILS = "serviceofferingdetails";
 +    public static final String SERVICE_PROVIDER_LIST = "serviceproviderlist";
 +    public static final String SERVICE_CAPABILITY_LIST = "servicecapabilitylist";
 +    public static final String CAN_CHOOSE_SERVICE_CAPABILITY = "canchooseservicecapability";
 +    public static final String PROVIDER = "provider";
 +    public static final String MANAGED = "managed";
 +    public static final String CAPACITY_BYTES = "capacitybytes";
 +    public static final String CAPACITY_IOPS = "capacityiops";
 +    public static final String NETWORK_SPEED = "networkspeed";
 +    public static final String BROADCAST_DOMAIN_RANGE = "broadcastdomainrange";
 +    public static final String BROADCAST_URI = "broadcasturi";
 +    public static final String ISOLATION_METHOD = "isolationmethod";
 +    public static final String ISOLATION_METHODS = "isolationmethods";
 +    public static final String PHYSICAL_NETWORK_ID = "physicalnetworkid";
 +    public static final String DEST_PHYSICAL_NETWORK_ID = "destinationphysicalnetworkid";
 +    public static final String ENABLE = "enable";
 +    public static final String ENABLED = "enabled";
 +    public static final String SERVICE_NAME = "servicename";
 +    public static final String DHCP_RANGE = "dhcprange";
 +    public static final String UUID = "uuid";
 +    public static final String SECURITY_GROUP_EANBLED = "securitygroupenabled";
 +    public static final String LOCAL_STORAGE_ENABLED = "localstorageenabled";
 +    public static final String GUEST_IP_TYPE = "guestiptype";
 +    public static final String XENSERVER_NETWORK_LABEL = "xennetworklabel";
 +    public static final String KVM_NETWORK_LABEL = "kvmnetworklabel";
 +    public static final String VMWARE_NETWORK_LABEL = "vmwarenetworklabel";
 +    public static final String HYPERV_NETWORK_LABEL = "hypervnetworklabel";
 +    public static final String OVM3_NETWORK_LABEL = "ovm3networklabel";
 +    public static final String NETWORK_SERVICE_PROVIDER_ID = "nspid";
 +    public static final String SERVICE_LIST = "servicelist";
 +    public static final String CAN_ENABLE_INDIVIDUAL_SERVICE = "canenableindividualservice";
 +    public static final String SUPPORTED_SERVICES = "supportedservices";
 +    public static final String NSP_ID = "nspid";
 +    public static final String ACL_TYPE = "acltype";
 +    public static final String ACL_REASON = "reason";
 +    public static final String ACL_RULE_PARTIAL_UPGRADE = "partialupgrade";
 +    public static final String SUBDOMAIN_ACCESS = "subdomainaccess";
 +    public static final String LOAD_BALANCER_DEVICE_ID = "lbdeviceid";
 +    public static final String LOAD_BALANCER_DEVICE_NAME = "lbdevicename";
 +    public static final String LOAD_BALANCER_DEVICE_STATE = "lbdevicestate";
 +    public static final String LOAD_BALANCER_DEVICE_CAPACITY = "lbdevicecapacity";
 +    public static final String LOAD_BALANCER_DEVICE_DEDICATED = "lbdevicededicated";
 +    public static final String LOAD_BALANCER_RULE = "loadbalancerrule";
 +    public static final String LOAD_BALANCER_RULE_LIST = "loadbalancerrulelist";
 +    public static final String FIREWALL_DEVICE_ID = "fwdeviceid";
 +    public static final String FIREWALL_DEVICE_NAME = "fwdevicename";
 +    public static final String FIREWALL_DEVICE_STATE = "fwdevicestate";
 +    public static final String FIREWALL_DEVICE_CAPACITY = "fwdevicecapacity";
 +    public static final String FIREWALL_DEVICE_DEDICATED = "fwdevicededicated";
 +    public static final String SERVICE = "service";
 +    public static final String ASSOCIATED_NETWORK_ID = "associatednetworkid";
 +    public static final String ASSOCIATED_NETWORK_NAME = "associatednetworkname";
 +    public static final String SOURCE_NAT_SUPPORTED = "sourcenatsupported";
 +    public static final String RESOURCE_STATE = "resourcestate";
 +    public static final String PROJECT_INVITE_REQUIRED = "projectinviterequired";
 +    public static final String REQUIRED = "required";
 +    public static final String RESTART_REQUIRED = "restartrequired";
 +    public static final String ALLOW_USER_CREATE_PROJECTS = "allowusercreateprojects";
 +    public static final String CONSERVE_MODE = "conservemode";
 +    public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor";
 +    public static final String KEYWORD = "keyword";
 +    public static final String LIST_ALL = "listall";
 +    public static final String SPECIFY_IP_RANGES = "specifyipranges";
 +    public static final String IS_SOURCE_NAT = "issourcenat";
 +    public static final String IS_STATIC_NAT = "isstaticnat";
 +    public static final String SORT_BY = "sortby";
 +    public static final String CHANGE_CIDR = "changecidr";
 +    public static final String PURPOSE = "purpose";
 +    public static final String IS_TAGGED = "istagged";
 +    public static final String INSTANCE_NAME = "instancename";
 +    public static final String START_VM = "startvm";
 +    public static final String HA_HOST = "hahost";
 +    public static final String CUSTOM_DISK_OFF_MIN_SIZE = "customdiskofferingminsize";
 +    public static final String CUSTOM_DISK_OFF_MAX_SIZE = "customdiskofferingmaxsize";
 +    public static final String DEFAULT_ZONE_ID = "defaultzoneid";
 +    public static final String LIVE_MIGRATE = "livemigrate";
 +    public static final String MIGRATE_TO = "migrateto";
 +    public static final String GUID = "guid";
 +    public static final String VSWITCH_TYPE_GUEST_TRAFFIC = "guestvswitchtype";
 +    public static final String VSWITCH_TYPE_PUBLIC_TRAFFIC = "publicvswitchtype";
 +    public static final String VSWITCH_NAME_GUEST_TRAFFIC = "guestvswitchname";
 +    public static final String VSWITCH_NAME_PUBLIC_TRAFFIC = "publicvswitchname";
 +    // Ovs controller
 +    public static final String OVS_DEVICE_ID = "ovsdeviceid";
 +    public static final String OVS_DEVICE_NAME = "ovsdevicename";
 +    // OpenDaylight controller
 +    public static final String ODL_DEVICE_ID = "odldeviceid";
 +    public static final String ODL_DEVICE_NAME = "odldevicename";
 +    public static final String ODL_TRANSPORT_ZONE_UUID = "transportzoneuuid";
 +    public static final String ODL_GATEWAYSERVICE_UUID = "l3gatewayserviceuuid";
 +
 +    public static final String EXTERNAL_SWITCH_MGMT_DEVICE_ID = "vsmdeviceid";
 +    public static final String EXTERNAL_SWITCH_MGMT_DEVICE_NAME = "vsmdevicename";
 +    public static final String EXTERNAL_SWITCH_MGMT_DEVICE_STATE = "vsmdevicestate";
 +    // Would we need to have a capacity field for Cisco N1KV VSM? Max hosts managed by it perhaps? May remove this
 +    // later.
 +    public static final String EXTERNAL_SWITCH_MGMT_DEVICE_CAPACITY = "vsmdevicecapacity";
 +    public static final String CISCO_NEXUS_VSM_NAME = "vsmname";
 +    public static final String VSM_USERNAME = "vsmusername";
 +    public static final String VSM_PASSWORD = "vsmpassword";
 +    public static final String VSM_IPADDRESS = "vsmipaddress";
 +    public static final String VSM_MGMT_VLAN_ID = "vsmmgmtvlanid";
 +    public static final String VSM_PKT_VLAN_ID = "vsmpktvlanid";
 +    public static final String VSM_CTRL_VLAN_ID = "vsmctrlvlanid";
 +    public static final String VSM_STORAGE_VLAN_ID = "vsmstoragevlanid";
 +    public static final String VSM_DOMAIN_ID = "vsmdomainid";
 +    public static final String VSM_CONFIG_MODE = "vsmconfigmode";
 +    public static final String VSM_CONFIG_STATE = "vsmconfigstate";
 +    public static final String VSM_DEVICE_STATE = "vsmdevicestate";
 +    public static final String VCENTER = "vcenter";
 +    public static final String ADD_VSM_FLAG = "addvsmflag";
 +    public static final String END_POINT = "endpoint";
 +    public static final String REGION_ID = "regionid";
 +    public static final String VPC_OFF_ID = "vpcofferingid";
 +    public static final String NETWORK = "network";
 +    public static final String VPC_ID = "vpcid";
 +    public static final String GATEWAY_ID = "gatewayid";
 +    public static final String CAN_USE_FOR_DEPLOY = "canusefordeploy";
 +    public static final String RESOURCE_IDS = "resourceids";
 +    public static final String RESOURCE_ID = "resourceid";
 +    public static final String CUSTOMER = "customer";
 +    public static final String S2S_VPN_GATEWAY_ID = "s2svpngatewayid";
 +    public static final String S2S_CUSTOMER_GATEWAY_ID = "s2scustomergatewayid";
 +    public static final String IPSEC_PSK = "ipsecpsk";
 +    public static final String GUEST_IP = "guestip";
 +    public static final String REMOVED = "removed";
 +    public static final String IKE_POLICY = "ikepolicy";
 +    public static final String ESP_POLICY = "esppolicy";
 +    public static final String IKE_LIFETIME = "ikelifetime";
 +    public static final String ESP_LIFETIME = "esplifetime";
 +    public static final String DPD = "dpd";
 +    public static final String FORCE_ENCAP = "forceencap";
 +    public static final String FOR_VPC = "forvpc";
 +    public static final String SHRINK_OK = "shrinkok";
 +    public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid";
 +    public static final String NICIRA_NVP_TRANSPORT_ZONE_UUID = "transportzoneuuid";
 +    public static final String NICIRA_NVP_DEVICE_NAME = "niciradevicename";
 +    public static final String NICIRA_NVP_GATEWAYSERVICE_UUID = "l3gatewayserviceuuid";
 +    public static final String NICIRA_NVP_L2_GATEWAYSERVICE_UUID = "l2gatewayserviceuuid";
 +    public static final String NSX_LOGICAL_SWITCH = "nsxlogicalswitch";
 +    public static final String NSX_LOGICAL_SWITCH_PORT = "nsxlogicalswitchport";
 +    public static final String S3_ACCESS_KEY = "accesskey";
 +    public static final String S3_SECRET_KEY = "secretkey";
 +    public static final String S3_END_POINT = "endpoint";
 +    public static final String S3_BUCKET_NAME = "bucket";
 +    public static final String S3_SIGNER = "s3signer";
 +    public static final String S3_V3_SIGNER = "S3SignerType";
 +    public static final String S3_V4_SIGNER = "AWSS3V4SignerType";
 +    public static final String S3_HTTPS_FLAG = "usehttps";
 +    public static final String S3_CONNECTION_TIMEOUT = "connectiontimeout";
 +    public static final String S3_CONNECTION_TTL = "connectionttl";
 +    public static final String S3_MAX_ERROR_RETRY = "maxerrorretry";
 +    public static final String S3_SOCKET_TIMEOUT = "sockettimeout";
 +    public static final String S3_USE_TCP_KEEPALIVE = "usetcpkeepalive";
 +    public static final String INCL_ZONES = "includezones";
 +    public static final String EXCL_ZONES = "excludezones";
 +    public static final String SOURCE = "source";
 +    public static final String COUNTER_ID = "counterid";
 +    public static final String AGGR_OPERATOR = "aggroperator";
 +    public static final String AGGR_FUNCTION = "aggrfunction";
 +    public static final String AGGR_VALUE = "aggrvalue";
 +    public static final String THRESHOLD = "threshold";
 +    public static final String RELATIONAL_OPERATOR = "relationaloperator";
 +    public static final String OTHER_DEPLOY_PARAMS = "otherdeployparams";
 +    public static final String MIN_MEMBERS = "minmembers";
 +    public static final String MAX_MEMBERS = "maxmembers";
 +    public static final String AUTOSCALE_VM_DESTROY_TIME = "destroyvmgraceperiod";
 +    public static final String VMPROFILE_ID = "vmprofileid";
 +    public static final String VMGROUP_ID = "vmgroupid";
 +    public static final String CS_URL = "csurl";
 +    public static final String IDP_ID = "idpid";
 +    public static final String SCALEUP_POLICY_IDS = "scaleuppolicyids";
 +    public static final String SCALEDOWN_POLICY_IDS = "scaledownpolicyids";
 +    public static final String SCALEUP_POLICIES = "scaleuppolicies";
 +    public static final String SCALEDOWN_POLICIES = "scaledownpolicies";
 +    public static final String INTERVAL = "interval";
 +    public static final String QUIETTIME = "quiettime";
 +    public static final String ACTION = "action";
 +    public static final String CONDITION_ID = "conditionid";
 +    public static final String CONDITION_IDS = "conditionids";
 +    public static final String COUNTERPARAM_LIST = "counterparam";
 +    public static final String AUTOSCALE_USER_ID = "autoscaleuserid";
 +    public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername";
 +    public static final String BAREMETAL_RCT_URL = "baremetalrcturl";
 +    public static final String UCS_DN = "ucsdn";
 +    public static final String GSLB_PROVIDER = "gslbprovider";
 +    public static final String EXCLUSIVE_GSLB_PROVIDER = "isexclusivegslbprovider";
 +    public static final String GSLB_PROVIDER_PUBLIC_IP = "gslbproviderpublicip";
 +    public static final String GSLB_PROVIDER_PRIVATE_IP = "gslbproviderprivateip";
 +    public static final String VM_SNAPSHOT_DESCRIPTION = "description";
 +    public static final String VM_SNAPSHOT_DISPLAYNAME = "name";
 +    public static final String VM_SNAPSHOT_ID = "vmsnapshotid";
 +    public static final String VM_SNAPSHOT_IDS = "vmsnapshotids";
 +    public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids";
 +    public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory";
 +    public static final String VM_SNAPSHOT_QUIESCEVM = "quiescevm";
 +    public static final String IMAGE_STORE_UUID = "imagestoreuuid";
 +    public static final String GUEST_VM_CIDR = "guestvmcidr";
 +    public static final String NETWORK_CIDR = "networkcidr";
 +    public static final String RESERVED_IP_RANGE = "reservediprange";
 +    public static final String UCS_MANAGER_ID = "ucsmanagerid";
 +    public static final String UCS_PROFILE_DN = "profiledn";
 +    public static final String UCS_BLADE_DN = "bladedn";
 +    public static final String UCS_BLADE_ID = "bladeid";
 +    public static final String VM_GUEST_IP = "vmguestip";
 +    public static final String HEALTHCHECK_RESPONSE_TIMEOUT = "responsetimeout";
 +    public static final String HEALTHCHECK_INTERVAL_TIME = "intervaltime";
 +    public static final String HEALTHCHECK_HEALTHY_THRESHOLD = "healthythreshold";
 +    public static final String HEALTHCHECK_UNHEALTHY_THRESHOLD = "unhealthythreshold";
 +    public static final String HEALTHCHECK_PINGPATH = "pingpath";
 +    public static final String SOURCE_PORT = "sourceport";
 +    public static final String INSTANCE_PORT = "instanceport";
 +    public static final String SOURCE_IP = "sourceipaddress";
 +    public static final String SOURCE_IP_NETWORK_ID = "sourceipaddressnetworkid";
 +    public static final String SCHEME = "scheme";
 +    public static final String PROVIDER_TYPE = "providertype";
 +    public static final String AFFINITY_GROUP_IDS = "affinitygroupids";
 +    public static final String AFFINITY_GROUP_NAMES = "affinitygroupnames";
 +    public static final String ASA_INSIDE_PORT_PROFILE = "insideportprofile";
 +    public static final String AFFINITY_GROUP_ID = "affinitygroupid";
 +    public static final String DEPLOYMENT_PLANNER = "deploymentplanner";
 +    public static final String ACL_ID = "aclid";
 +    public static final String NUMBER = "number";
 +    public static final String IS_DYNAMICALLY_SCALABLE = "isdynamicallyscalable";
 +    public static final String ROUTING = "isrouting";
 +    public static final String MAX_CONNECTIONS = "maxconnections";
 +    public static final String SERVICE_STATE = "servicestate";
 +
 +    public static final String IAM_ACCOUNT_IDS = "accountids";
 +    public static final String IAM_MEMBER_ACCOUNTS = "memberaccounts";
 +    public static final String IAM_PARENT_POLICY_ID = "parentpolicyid";
 +    public static final String IAM_PARENT_POLICY_NAME = "parentpolicyname";
 +    public static final String IAM_POLICY_IDS = "policyids";
 +    public static final String IAM_POLICIES = "policies";
 +    public static final String IAM_APIS = "apis";
 +    public static final String IAM_GROUPS = "groups";
 +    public static final String IAM_PERMISSIONS = "permission";
 +    public static final String IAM_ACTION = "action";
 +    public static final String IAM_SCOPE = "scope";
 +    public static final String IAM_SCOPE_ID = "scopeid";
 +    public static final String IAM_ALLOW_DENY = "permission";
 +    public static final String ENTITY_TYPE = "entitytype";
 +    public static final String ENTITY_ID = "entityid";
 +    public static final String EXTERNAL_ID = "externalid";
 +    public static final String ACCESS_TYPE = "accesstype";
 +
 +    public static final String RESOURCE_DETAILS = "resourcedetails";
 +    public static final String EXPUNGE = "expunge";
 +    public static final String FOR_DISPLAY = "fordisplay";
 +    public static final String PASSIVE = "passive";
 +    public static final String VERSION = "version";
 +    public static final String START = "start";
 +    public static final String GPUGROUP = "gpugroup";
 +    public static final String GPUGROUPNAME = "gpugroupname";
 +    public static final String VGPU = "vgpu";
 +    public static final String VGPUTYPE = "vgputype";
 +    public static final String VIDEORAM = "videoram";
 +    public static final String MAXHEADS = "maxheads";
 +    public static final String MAXRESOLUTIONX = "maxresolutionx";
 +    public static final String MAXRESOLUTIONY = "maxresolutiony";
 +    public static final String MAXVGPUPERPGPU = "maxvgpuperpgpu";
 +    public static final String REMAININGCAPACITY = "remainingcapacity";
 +    public static final String MAXCAPACITY = "maxcapacity";
 +    public static final String DISTRIBUTED_VPC_ROUTER = "distributedvpcrouter";
 +    public static final String REDUNDANT_VPC_ROUTER = "redundantvpcrouter";
 +    public static final String READ_ONLY = "readonly";
 +    public static final String SUPPORTS_REGION_LEVEL_VPC = "supportsregionLevelvpc";
 +    public static final String SUPPORTS_STRECHED_L2_SUBNET = "supportsstrechedl2subnet";
 +    public static final String SUPPORTS_PUBLIC_ACCESS = "supportspublicaccess";
 +    public static final String REGION_LEVEL_VPC = "regionlevelvpc";
 +    public static final String STRECHED_L2_SUBNET = "strechedl2subnet";
 +    public static final String NETWORK_NAME = "networkname";
 +    public static final String NETWORK_SPANNED_ZONES = "zonesnetworkspans";
 +    public static final String METADATA = "metadata";
 +    public static final String PHYSICAL_SIZE = "physicalsize";
 +    public static final String OVM3_POOL = "ovm3pool";
 +    public static final String OVM3_CLUSTER = "ovm3cluster";
 +    public static final String OVM3_VIP = "ovm3vip";
 +    public static final String CLEAN_UP_DETAILS = "cleanupdetails";
 +    public static final String VIRTUAL_SIZE = "virtualsize";
 +    public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid";
 +    public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid";
 +
 +    public static final String ZONE_ID_LIST = "zoneids";
 +    public static final String DESTINATION_ZONE_ID_LIST = "destzoneids";
 +    public static final String ADMIN = "admin";
 +    public static final String CHECKSUM_PARAMETER_PREFIX_DESCRIPTION = "The parameter containing the checksum will be considered a MD5sum if it is not prefixed\n"
 +            + " and just a plain ascii/utf8 representation of a hexadecimal string. If it is required to\n"
 +            + " use another algorithm the hexadecimal string is to be prefixed with a string of the form,\n"
 +            + " \"{<algorithm>}\", not including the double quotes. In this <algorithm> is the exact string\n"
 +            + " representing the java supported algorithm, i.e. MD5 or SHA-256. Note that java does not\n" + " contain an algorithm called SHA256 or one called sha-256, only SHA-256.";
 +
 +    public static final String HAS_ANNOTATION = "hasannotation";
 +    public static final String LAST_ANNOTATED = "lastannotated";
 +    public static final String LDAP_DOMAIN = "ldapdomain";
 +
 +    public enum HostDetails {
 +        all, capacity, events, stats, min;
 +    }
 +
 +    public enum VMDetails {
 +        all, group, nics, stats, secgrp, tmpl, servoff, diskoff, iso, volume, min, affgrp;
 +    }
 +
 +    public enum DomainDetails {
 +        all, resource, min;
 +    }
 +}
diff --cc engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index fb1c1b4,0000000..1d430c6
mode 100755,000000..100755
--- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@@ -1,5077 -1,0 +1,5076 @@@
 +// Licensed to the Apacohe 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.vm;
 +
 +import java.net.URI;
 +import java.sql.PreparedStatement;
 +import java.sql.ResultSet;
 +import java.sql.SQLException;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.Date;
 +import java.util.HashMap;
 +import java.util.LinkedHashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Map.Entry;
 +import java.util.TimeZone;
 +import java.util.UUID;
 +import java.util.concurrent.Executors;
 +import java.util.concurrent.ScheduledExecutorService;
 +import java.util.concurrent.TimeUnit;
 +
 +import javax.inject.Inject;
 +import javax.naming.ConfigurationException;
 +
 +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
 +import org.apache.cloudstack.ca.CAManager;
 +import org.apache.cloudstack.context.CallContext;
 +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
 +import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
 +import org.apache.cloudstack.framework.ca.Certificate;
 +import org.apache.cloudstack.framework.config.ConfigDepot;
 +import org.apache.cloudstack.framework.config.ConfigKey;
 +import org.apache.cloudstack.framework.config.Configurable;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.framework.jobs.AsyncJob;
 +import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext;
 +import org.apache.cloudstack.framework.jobs.AsyncJobManager;
 +import org.apache.cloudstack.framework.jobs.Outcome;
 +import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao;
 +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
 +import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper;
 +import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl;
 +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO;
 +import org.apache.cloudstack.framework.messagebus.MessageBus;
 +import org.apache.cloudstack.framework.messagebus.MessageDispatcher;
 +import org.apache.cloudstack.framework.messagebus.MessageHandler;
 +import org.apache.cloudstack.jobs.JobInfo;
 +import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 +import org.apache.cloudstack.storage.to.VolumeObjectTO;
 +import org.apache.cloudstack.utils.identity.ManagementServerNode;
 +import org.apache.commons.collections.CollectionUtils;
 +import org.apache.commons.collections.MapUtils;
 +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.AttachOrDettachConfigDriveCommand;
 +import com.cloud.agent.api.CheckVirtualMachineAnswer;
 +import com.cloud.agent.api.CheckVirtualMachineCommand;
 +import com.cloud.agent.api.ClusterVMMetaDataSyncAnswer;
 +import com.cloud.agent.api.ClusterVMMetaDataSyncCommand;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.MigrateCommand;
 +import com.cloud.agent.api.ModifyTargetsCommand;
 +import com.cloud.agent.api.PingRoutingCommand;
 +import com.cloud.agent.api.PlugNicAnswer;
 +import com.cloud.agent.api.PlugNicCommand;
 +import com.cloud.agent.api.PrepareForMigrationCommand;
 +import com.cloud.agent.api.RebootAnswer;
 +import com.cloud.agent.api.RebootCommand;
 +import com.cloud.agent.api.ReplugNicAnswer;
 +import com.cloud.agent.api.ReplugNicCommand;
 +import com.cloud.agent.api.RestoreVMSnapshotAnswer;
 +import com.cloud.agent.api.RestoreVMSnapshotCommand;
 +import com.cloud.agent.api.ScaleVmCommand;
 +import com.cloud.agent.api.StartAnswer;
 +import com.cloud.agent.api.StartCommand;
 +import com.cloud.agent.api.StartupCommand;
 +import com.cloud.agent.api.StartupRoutingCommand;
 +import com.cloud.agent.api.StopAnswer;
 +import com.cloud.agent.api.StopCommand;
 +import com.cloud.agent.api.UnPlugNicAnswer;
 +import com.cloud.agent.api.UnPlugNicCommand;
 +import com.cloud.agent.api.UnregisterVMCommand;
 +import com.cloud.agent.api.routing.NetworkElementCommand;
 +import com.cloud.agent.api.to.DiskTO;
 +import com.cloud.agent.api.to.GPUDeviceTO;
 +import com.cloud.agent.api.to.NicTO;
 +import com.cloud.agent.api.to.VirtualMachineTO;
 +import com.cloud.agent.manager.Commands;
 +import com.cloud.agent.manager.allocator.HostAllocator;
 +import com.cloud.alert.AlertManager;
 +import com.cloud.capacity.CapacityManager;
 +import com.cloud.configuration.Config;
 +import com.cloud.dc.ClusterDetailsDao;
 +import com.cloud.dc.ClusterDetailsVO;
 +import com.cloud.dc.DataCenter;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.HostPodVO;
 +import com.cloud.dc.Pod;
 +import com.cloud.dc.dao.ClusterDao;
 +import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.dc.dao.HostPodDao;
 +import com.cloud.deploy.DataCenterDeployment;
 +import com.cloud.deploy.DeployDestination;
 +import com.cloud.deploy.DeploymentPlan;
 +import com.cloud.deploy.DeploymentPlanner;
 +import com.cloud.deploy.DeploymentPlanner.ExcludeList;
 +import com.cloud.deploy.DeploymentPlanningManager;
 +import com.cloud.domain.dao.DomainDao;
 +import com.cloud.event.EventTypes;
 +import com.cloud.event.UsageEventUtils;
 +import com.cloud.exception.AffinityConflictException;
 +import com.cloud.exception.AgentUnavailableException;
 +import com.cloud.exception.ConcurrentOperationException;
 +import com.cloud.exception.ConnectionException;
 +import com.cloud.exception.InsufficientAddressCapacityException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.InsufficientServerCapacityException;
 +import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.OperationTimedoutException;
 +import com.cloud.exception.ResourceUnavailableException;
 +import com.cloud.exception.StorageUnavailableException;
 +import com.cloud.gpu.dao.VGPUTypesDao;
 +import com.cloud.ha.HighAvailabilityManager;
 +import com.cloud.ha.HighAvailabilityManager.WorkType;
 +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.hypervisor.HypervisorGuru;
 +import com.cloud.hypervisor.HypervisorGuruManager;
 +import com.cloud.network.Network;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.dao.NetworkDao;
 +import com.cloud.network.dao.NetworkVO;
 +import com.cloud.network.router.VirtualRouter;
 +import com.cloud.network.rules.RulesManager;
 +import com.cloud.offering.DiskOffering;
 +import com.cloud.offering.DiskOfferingInfo;
 +import com.cloud.offering.ServiceOffering;
 +import com.cloud.org.Cluster;
 +import com.cloud.resource.ResourceManager;
 +import com.cloud.resource.ResourceState;
 +import com.cloud.service.ServiceOfferingVO;
 +import com.cloud.service.dao.ServiceOfferingDao;
 +import com.cloud.storage.DiskOfferingVO;
 +import com.cloud.storage.ScopeType;
 +import com.cloud.storage.Storage.ImageFormat;
 +import com.cloud.storage.StoragePool;
 +import com.cloud.storage.VMTemplateVO;
 +import com.cloud.storage.Volume;
 +import com.cloud.storage.Volume.Type;
 +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.StoragePoolHostDao;
 +import com.cloud.storage.dao.VMTemplateDao;
 +import com.cloud.storage.dao.VolumeDao;
 +import com.cloud.template.VirtualMachineTemplate;
 +import com.cloud.user.Account;
 +import com.cloud.user.User;
 +import com.cloud.utils.DateUtil;
 +import com.cloud.utils.Journal;
 +import com.cloud.utils.Pair;
 +import com.cloud.utils.Predicate;
 +import com.cloud.utils.ReflectionUse;
 +import com.cloud.utils.StringUtils;
 +import com.cloud.utils.Ternary;
 +import com.cloud.utils.component.ManagerBase;
 +import com.cloud.utils.concurrency.NamedThreadFactory;
 +import com.cloud.utils.db.DB;
 +import com.cloud.utils.db.EntityManager;
 +import com.cloud.utils.db.GlobalLock;
 +import com.cloud.utils.db.Transaction;
 +import com.cloud.utils.db.TransactionCallbackWithException;
 +import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
 +import com.cloud.utils.db.TransactionLegacy;
 +import com.cloud.utils.db.TransactionStatus;
 +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.ItWorkVO.Step;
 +import com.cloud.vm.VirtualMachine.Event;
 +import com.cloud.vm.VirtualMachine.PowerState;
 +import com.cloud.vm.VirtualMachine.State;
 +import com.cloud.vm.dao.NicDao;
 +import com.cloud.vm.dao.UserVmDao;
 +import com.cloud.vm.dao.UserVmDetailsDao;
 +import com.cloud.vm.dao.VMInstanceDao;
 +import com.cloud.vm.snapshot.VMSnapshotManager;
 +import com.cloud.vm.snapshot.VMSnapshotVO;
 +import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 +import com.google.common.base.Strings;
 +
 +public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
 +    private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class);
 +
 +    public static final String VM_WORK_JOB_HANDLER = VirtualMachineManagerImpl.class.getSimpleName();
 +
 +    private static final String VM_SYNC_ALERT_SUBJECT = "VM state sync alert";
 +
 +    @Inject
 +    DataStoreManager dataStoreMgr;
 +    @Inject
 +    protected NetworkOrchestrationService _networkMgr;
 +    @Inject
 +    protected NetworkModel _networkModel;
 +    @Inject
 +    protected AgentManager _agentMgr;
 +    @Inject
 +    protected VMInstanceDao _vmDao;
 +    @Inject
 +    protected ServiceOfferingDao _offeringDao;
 +    @Inject
 +    protected DiskOfferingDao _diskOfferingDao;
 +    @Inject
 +    protected VMTemplateDao _templateDao;
 +    @Inject
 +    protected DomainDao _domainDao;
 +    @Inject
 +    protected ItWorkDao _workDao;
 +    @Inject
 +    protected UserVmDao _userVmDao;
 +    @Inject
 +    protected UserVmService _userVmService;
 +    @Inject
 +    protected CapacityManager _capacityMgr;
 +    @Inject
 +    protected NicDao _nicsDao;
 +    @Inject
 +    protected HostDao _hostDao;
 +    @Inject
 +    protected AlertManager _alertMgr;
 +    @Inject
 +    protected GuestOSCategoryDao _guestOsCategoryDao;
 +    @Inject
 +    protected GuestOSDao _guestOsDao;
 +    @Inject
 +    protected VolumeDao _volsDao;
 +    @Inject
 +    protected HighAvailabilityManager _haMgr;
 +    @Inject
 +    protected HostPodDao _podDao;
 +    @Inject
 +    protected DataCenterDao _dcDao;
 +    @Inject
 +    protected ClusterDao _clusterDao;
 +    @Inject
 +    protected PrimaryDataStoreDao _storagePoolDao;
 +    @Inject
 +    protected HypervisorGuruManager _hvGuruMgr;
 +    @Inject
 +    protected NetworkDao _networkDao;
 +    @Inject
 +    protected StoragePoolHostDao _poolHostDao;
 +    @Inject
 +    protected VMSnapshotDao _vmSnapshotDao;
 +    @Inject
 +    protected RulesManager rulesMgr;
 +    @Inject
 +    protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
 +    @Inject
 +    protected VGPUTypesDao _vgpuTypesDao;
 +    @Inject
 +    protected EntityManager _entityMgr;
 +    @Inject
 +    protected GuestOSCategoryDao _guestOSCategoryDao;
 +    @Inject
 +    protected GuestOSDao _guestOSDao = null;
 +    @Inject
 +    protected UserVmDetailsDao _vmDetailsDao;
 +    @Inject
 +    protected ServiceOfferingDao _serviceOfferingDao = null;
 +    @Inject
 +    protected CAManager caManager;
 +
 +    @Inject
 +    ConfigDepot _configDepot;
 +
 +    protected List<HostAllocator> hostAllocators;
 +
 +    public List<HostAllocator> getHostAllocators() {
 +        return hostAllocators;
 +    }
 +
 +    public void setHostAllocators(final List<HostAllocator> hostAllocators) {
 +        this.hostAllocators = hostAllocators;
 +    }
 +
 +    protected List<StoragePoolAllocator> _storagePoolAllocators;
 +
 +    @Inject
 +    protected ResourceManager _resourceMgr;
 +
 +    @Inject
 +    protected VMSnapshotManager _vmSnapshotMgr = null;
 +    @Inject
 +    protected ClusterDetailsDao _clusterDetailsDao;
 +    @Inject
 +    protected UserVmDetailsDao _uservmDetailsDao;
 +
 +    @Inject
 +    protected ConfigurationDao _configDao;
 +    @Inject
 +    VolumeOrchestrationService volumeMgr;
 +
 +    @Inject
 +    DeploymentPlanningManager _dpMgr;
 +
 +    @Inject
 +    protected MessageBus _messageBus;
 +    @Inject
 +    protected VirtualMachinePowerStateSync _syncMgr;
 +    @Inject
 +    protected VmWorkJobDao _workJobDao;
 +    @Inject
 +    protected AsyncJobManager _jobMgr;
 +
 +    VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
 +
 +    Map<VirtualMachine.Type, VirtualMachineGuru> _vmGurus = new HashMap<VirtualMachine.Type, VirtualMachineGuru>();
 +    protected StateMachine2<State, VirtualMachine.Event, VirtualMachine> _stateMachine;
 +
 +    static final ConfigKey<Integer> StartRetry = new ConfigKey<Integer>("Advanced", Integer.class, "start.retry", "10",
 +            "Number of times to retry create and start commands", true);
 +    static final ConfigKey<Integer> VmOpWaitInterval = new ConfigKey<Integer>("Advanced", Integer.class, "vm.op.wait.interval", "120",
 +            "Time (in seconds) to wait before checking if a previous operation has succeeded", true);
 +
 +    static final ConfigKey<Integer> VmOpLockStateRetry = new ConfigKey<Integer>("Advanced", Integer.class, "vm.op.lock.state.retry", "5",
 +            "Times to retry locking the state of a VM for operations, -1 means forever", true);
 +    static final ConfigKey<Long> VmOpCleanupInterval = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cleanup.interval", "86400",
 +            "Interval to run the thread that cleans up the vm operations (in seconds)", false);
 +    static final ConfigKey<Long> VmOpCleanupWait = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cleanup.wait", "3600",
 +            "Time (in seconds) to wait before cleanuping up any vm work items", true);
 +    static final ConfigKey<Long> VmOpCancelInterval = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cancel.interval", "3600",
 +            "Time (in seconds) to wait before cancelling a operation", false);
 +    static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false",
 +            "On destroy, force-stop takes this value ", true);
 +    static final ConfigKey<Integer> ClusterDeltaSyncInterval = new ConfigKey<Integer>("Advanced", Integer.class, "sync.interval", "60",
 +            "Cluster Delta sync interval in seconds",
 +            false);
 +    static final ConfigKey<Integer> ClusterVMMetaDataSyncInterval = new ConfigKey<Integer>("Advanced", Integer.class, "vmmetadata.sync.interval", "180", "Cluster VM metadata sync interval in seconds",
 +            false);
 +
 +    static final ConfigKey<Long> VmJobCheckInterval = new ConfigKey<Long>("Advanced",
 +            Long.class, "vm.job.check.interval", "3000",
 +            "Interval in milliseconds to check if the job is complete", false);
 +    static final ConfigKey<Long> VmJobTimeout = new ConfigKey<Long>("Advanced",
 +            Long.class, "vm.job.timeout", "600000",
 +            "Time in milliseconds to wait before attempting to cancel a job", false);
 +    static final ConfigKey<Integer> VmJobStateReportInterval = new ConfigKey<Integer>("Advanced",
 +            Integer.class, "vm.job.report.interval", "60",
 +            "Interval to send application level pings to make sure the connection is still working", false);
 +
 +    ScheduledExecutorService _executor = null;
 +
 +    protected long _nodeId;
 +
 +    @Override
 +    public void registerGuru(final VirtualMachine.Type type, final VirtualMachineGuru guru) {
 +        synchronized (_vmGurus) {
 +            _vmGurus.put(type, guru);
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering,
 +            final DiskOfferingInfo rootDiskOfferingInfo, final List<DiskOfferingInfo> dataDiskOfferings,
 +            final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, final DeploymentPlan plan, final HypervisorType hyperType, final Map<String, Map<Integer, String>> extraDhcpOptions, final Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap)
 +                    throws InsufficientCapacityException {
 +
 +        final VMInstanceVO vm = _vmDao.findVMByInstanceName(vmInstanceName);
 +        final Account owner = _entityMgr.findById(Account.class, vm.getAccountId());
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Allocating entries for VM: " + vm);
 +        }
 +
 +        vm.setDataCenterId(plan.getDataCenterId());
 +        if (plan.getPodId() != null) {
 +            vm.setPodIdToDeployIn(plan.getPodId());
 +        }
 +        assert plan.getClusterId() == null && plan.getPoolId() == null : "We currently don't support cluster and pool preset yet";
 +        final VMInstanceVO vmFinal = _vmDao.persist(vm);
 +
 +        final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmFinal, template, serviceOffering, null, null);
 +
 +        Transaction.execute(new TransactionCallbackWithExceptionNoReturn<InsufficientCapacityException>() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus status) throws InsufficientCapacityException {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Allocating nics for " + vmFinal);
 +                }
 +
 +                try {
 +                    if (!vmProfile.getBootArgs().contains("ExternalLoadBalancerVm")) {
 +                        _networkMgr.allocate(vmProfile, auxiliaryNetworks, extraDhcpOptions);
 +                    }
 +                } catch (final ConcurrentOperationException e) {
 +                    throw new CloudRuntimeException("Concurrent operation while trying to allocate resources for the VM", e);
 +                }
 +
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Allocating disks for " + vmFinal);
 +                }
 +
 +                if (template.getFormat() == ImageFormat.ISO) {
 +                    volumeMgr.allocateRawVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(),
 +                            rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vmFinal, template, owner, null);
 +                } else if (template.getFormat() == ImageFormat.BAREMETAL) {
 +                    // Do nothing
 +                } else {
 +                    volumeMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(),
 +                            rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vmFinal, owner);
 +                }
 +
 +                if (dataDiskOfferings != null) {
 +                    for (final DiskOfferingInfo dataDiskOfferingInfo : dataDiskOfferings) {
 +                        volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vmFinal.getId(), dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(),
 +                                dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), vmFinal, template, owner, null);
 +                    }
 +                }
 +                if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) {
 +                    int diskNumber = 1;
 +                    for (Entry<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap : datadiskTemplateToDiskOfferingMap.entrySet()) {
 +                        DiskOffering diskOffering = dataDiskTemplateToDiskOfferingMap.getValue();
 +                        long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024);
 +                        VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
 +                        volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vmFinal.getId() + "-" + String.valueOf(diskNumber), diskOffering, diskOfferingSize, null, null,
 +                                vmFinal, dataDiskTemplate, owner, Long.valueOf(diskNumber));
 +                        diskNumber++;
 +                    }
 +                }
 +            }
 +        });
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Allocation completed for VM: " + vmFinal);
 +        }
 +    }
 +
 +    @Override
 +    public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering,
 +            final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final DeploymentPlan plan, final HypervisorType hyperType) throws InsufficientCapacityException {
 +        allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), new ArrayList<DiskOfferingInfo>(), networks, plan, hyperType, null, null);
 +    }
 +
 +    private VirtualMachineGuru getVmGuru(final VirtualMachine vm) {
 +        if(vm != null) {
 +            return _vmGurus.get(vm.getType());
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public void expunge(final String vmUuid) throws ResourceUnavailableException {
 +        try {
 +            advanceExpunge(vmUuid);
 +        } catch (final OperationTimedoutException e) {
 +            throw new CloudRuntimeException("Operation timed out", e);
 +        } catch (final ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Concurrent operation ", e);
 +        }
 +    }
 +
 +    @Override
 +    public void advanceExpunge(final String vmUuid) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        advanceExpunge(vm);
 +    }
 +
 +    protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException {
 +        if (vm == null || vm.getRemoved() != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Unable to find vm or vm is destroyed: " + vm);
 +            }
 +            return;
 +        }
 +
 +        advanceStop(vm.getUuid(), false);
 +        vm = _vmDao.findByUuid(vm.getUuid());
 +
 +        try {
 +            if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) {
 +                s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm);
 +                throw new CloudRuntimeException("Unable to destroy " + vm);
 +
 +            }
 +        } catch (final NoTransitionException e) {
 +            s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm);
 +            throw new CloudRuntimeException("Unable to destroy " + vm, e);
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Destroying vm " + vm);
 +        }
 +
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
 +
 +        s_logger.debug("Cleaning up NICS");
 +        final List<Command> nicExpungeCommands = hvGuru.finalizeExpungeNics(vm, profile.getNics());
 +        _networkMgr.cleanupNics(profile);
 +
 +        s_logger.debug("Cleaning up hypervisor data structures (ex. SRs in XenServer) for managed storage");
 +
 +        final List<Command> volumeExpungeCommands = hvGuru.finalizeExpungeVolumes(vm);
 +
 +        final Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
 +
 +        List<Map<String, String>> targets = getTargets(hostId, vm.getId());
 +
 +        if (volumeExpungeCommands != null && volumeExpungeCommands.size() > 0 && hostId != null) {
 +            final Commands cmds = new Commands(Command.OnError.Stop);
 +
 +            for (final Command volumeExpungeCommand : volumeExpungeCommands) {
 +                cmds.addCommand(volumeExpungeCommand);
 +            }
 +
 +            _agentMgr.send(hostId, cmds);
 +
 +            if (!cmds.isSuccessful()) {
 +                for (final Answer answer : cmds.getAnswers()) {
 +                    if (!answer.getResult()) {
 +                        s_logger.warn("Failed to expunge vm due to: " + answer.getDetails());
 +
 +                        throw new CloudRuntimeException("Unable to expunge " + vm + " due to " + answer.getDetails());
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (hostId != null) {
 +            volumeMgr.revokeAccess(vm.getId(), hostId);
 +        }
 +
 +        // Clean up volumes based on the vm's instance id
 +        volumeMgr.cleanupVolumes(vm.getId());
 +
 +        if (hostId != null && CollectionUtils.isNotEmpty(targets)) {
 +            removeDynamicTargets(hostId, targets);
 +        }
 +
 +        final VirtualMachineGuru guru = getVmGuru(vm);
 +        guru.finalizeExpunge(vm);
 +        //remove the overcommit detials from the uservm details
 +        _uservmDetailsDao.removeDetails(vm.getId());
 +
 +        // send hypervisor-dependent commands before removing
 +        final List<Command> finalizeExpungeCommands = hvGuru.finalizeExpunge(vm);
 +        if (finalizeExpungeCommands != null && finalizeExpungeCommands.size() > 0) {
 +            if (hostId != null) {
 +                final Commands cmds = new Commands(Command.OnError.Stop);
 +                for (final Command command : finalizeExpungeCommands) {
 +                    cmds.addCommand(command);
 +                }
 +                if (nicExpungeCommands != null) {
 +                    for (final Command command : nicExpungeCommands) {
 +                        cmds.addCommand(command);
 +                    }
 +                }
 +                _agentMgr.send(hostId, cmds);
 +                if (!cmds.isSuccessful()) {
 +                    for (final Answer answer : cmds.getAnswers()) {
 +                        if (!answer.getResult()) {
 +                            s_logger.warn("Failed to expunge vm due to: " + answer.getDetails());
 +                            throw new CloudRuntimeException("Unable to expunge " + vm + " due to " + answer.getDetails());
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Expunged " + vm);
 +        }
 +
 +    }
 +
 +    private List<Map<String, String>> getTargets(Long hostId, long vmId) {
 +        List<Map<String, String>> targets = new ArrayList<>();
 +
 +        HostVO hostVO = _hostDao.findById(hostId);
 +
 +        if (hostVO == null || hostVO.getHypervisorType() != HypervisorType.VMware) {
 +            return targets;
 +        }
 +
 +        List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
 +
 +        if (CollectionUtils.isEmpty(volumes)) {
 +            return targets;
 +        }
 +
 +        for (VolumeVO volume : volumes) {
 +            StoragePoolVO storagePoolVO = _storagePoolDao.findById(volume.getPoolId());
 +
 +            if (storagePoolVO != null && storagePoolVO.isManaged()) {
 +                Map<String, String> target = new HashMap<>();
 +
 +                target.put(ModifyTargetsCommand.STORAGE_HOST, storagePoolVO.getHostAddress());
 +                target.put(ModifyTargetsCommand.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
 +                target.put(ModifyTargetsCommand.IQN, volume.get_iScsiName());
 +
 +                targets.add(target);
 +            }
 +        }
 +
 +        return targets;
 +    }
 +
 +    private void removeDynamicTargets(long hostId, List<Map<String, String>> targets) {
 +        ModifyTargetsCommand cmd = new ModifyTargetsCommand();
 +
 +        cmd.setTargets(targets);
 +        cmd.setApplyToAllHostsInCluster(true);
 +        cmd.setAdd(false);
 +        cmd.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC);
 +
 +        sendModifyTargetsCommand(cmd, hostId);
 +    }
 +
 +    private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) {
 +        Answer answer = _agentMgr.easySend(hostId, cmd);
 +
 +        if (answer == null) {
 +            String msg = "Unable to get an answer to the modify targets command";
 +
 +            s_logger.warn(msg);
 +        }
 +        else if (!answer.getResult()) {
 +            String msg = "Unable to modify target on the following host: " + hostId;
 +
 +            s_logger.warn(msg);
 +        }
 +    }
 +
 +    @Override
 +    public boolean start() {
 +        // TODO, initial delay is hardcoded
 +        _executor.scheduleAtFixedRate(new CleanupTask(), 5, VmJobStateReportInterval.value(), TimeUnit.SECONDS);
 +        _executor.scheduleAtFixedRate(new TransitionTask(),  VmOpCleanupInterval.value(), VmOpCleanupInterval.value(), TimeUnit.SECONDS);
 +        cancelWorkItems(_nodeId);
 +
 +        volumeMgr.cleanupStorageJobs();
 +        // cleanup left over place holder works
 +        _workJobDao.expungeLeftoverWorkJobs(ManagementServerNode.getManagementServerId());
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean stop() {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean configure(final String name, final Map<String, Object> xmlParams) throws ConfigurationException {
 +        ReservationContextImpl.init(_entityMgr);
 +        VirtualMachineProfileImpl.init(_entityMgr);
 +        VmWorkMigrate.init(_entityMgr);
 +
 +        _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Vm-Operations-Cleanup"));
 +        _nodeId = ManagementServerNode.getManagementServerId();
 +
 +        _agentMgr.registerForHostEvents(this, true, true, true);
 +
 +        _messageBus.subscribe(VirtualMachineManager.Topics.VM_POWER_STATE, MessageDispatcher.getDispatcher(this));
 +
 +        return true;
 +    }
 +
 +    protected VirtualMachineManagerImpl() {
 +        setStateMachine();
 +    }
 +
 +    @Override
 +    public void start(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params) {
 +        start(vmUuid, params, null, null);
 +    }
 +
 +    @Override
 +    public void start(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) {
 +        try {
 +            advanceStart(vmUuid, params, planToDeploy, planner);
 +        } catch (final ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Unable to start a VM due to concurrent operation", e).add(VirtualMachine.class, vmUuid);
 +        } catch (final InsufficientCapacityException e) {
 +            throw new CloudRuntimeException("Unable to start a VM due to insufficient capacity", e).add(VirtualMachine.class, vmUuid);
 +        } catch (final ResourceUnavailableException e) {
 +            if(e.getScope() != null && e.getScope().equals(VirtualRouter.class)){
 +                throw new CloudRuntimeException("Network is unavailable. Please contact administrator", e).add(VirtualMachine.class, vmUuid);
 +            }
 +            throw new CloudRuntimeException("Unable to start a VM due to unavailable resources", e).add(VirtualMachine.class, vmUuid);
 +        }
 +
 +    }
 +
 +    protected boolean checkWorkItems(final VMInstanceVO vm, final State state) throws ConcurrentOperationException {
 +        while (true) {
 +            final ItWorkVO vo = _workDao.findByOutstandingWork(vm.getId(), state);
 +            if (vo == null) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Unable to find work for VM: " + vm + " and state: " + state);
 +                }
 +                return true;
 +            }
 +
 +            if (vo.getStep() == Step.Done) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Work for " + vm + " is " + vo.getStep());
 +                }
 +                return true;
 +            }
 +
 +            // also check DB to get latest VM state to detect vm update from concurrent process before idle waiting to get an early exit
 +            final VMInstanceVO instance = _vmDao.findById(vm.getId());
 +            if (instance != null && instance.getState() == State.Running) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("VM is already started in DB: " + vm);
 +                }
 +                return true;
 +            }
 +
 +            if (vo.getSecondsTaskIsInactive() > VmOpCancelInterval.value()) {
 +                s_logger.warn("The task item for vm " + vm + " has been inactive for " + vo.getSecondsTaskIsInactive());
 +                return false;
 +            }
 +
 +            try {
 +                Thread.sleep(VmOpWaitInterval.value()*1000);
 +            } catch (final InterruptedException e) {
 +                s_logger.info("Waiting for " + vm + " but is interrupted");
 +                throw new ConcurrentOperationException("Waiting for " + vm + " but is interrupted");
 +            }
 +            s_logger.debug("Waiting some more to make sure there's no activity on " + vm);
 +        }
 +
 +    }
 +
 +    @DB
 +    protected Ternary<VMInstanceVO, ReservationContext, ItWorkVO> changeToStartState(final VirtualMachineGuru vmGuru, final VMInstanceVO vm, final User caller,
 +            final Account account) throws ConcurrentOperationException {
 +        final long vmId = vm.getId();
 +
 +        ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getType(), vm.getId());
 +        int retry = VmOpLockStateRetry.value();
 +        while (retry-- != 0) {
 +            try {
 +                final ItWorkVO workFinal = work;
 +                final Ternary<VMInstanceVO, ReservationContext, ItWorkVO> result =
 +                        Transaction.execute(new TransactionCallbackWithException<Ternary<VMInstanceVO, ReservationContext, ItWorkVO>, NoTransitionException>() {
 +                            @Override
 +                            public Ternary<VMInstanceVO, ReservationContext, ItWorkVO> doInTransaction(final TransactionStatus status) throws NoTransitionException {
 +                                final Journal journal = new Journal.LogJournal("Creating " + vm, s_logger);
 +                                final ItWorkVO work = _workDao.persist(workFinal);
 +                                final ReservationContextImpl context = new ReservationContextImpl(work.getId(), journal, caller, account);
 +
 +                                if (stateTransitTo(vm, Event.StartRequested, null, work.getId())) {
 +                                    if (s_logger.isDebugEnabled()) {
 +                                        s_logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId());
 +                                    }
 +                                    return new Ternary<VMInstanceVO, ReservationContext, ItWorkVO>(vm, context, work);
 +                                }
 +
 +                                return new Ternary<VMInstanceVO, ReservationContext, ItWorkVO>(null, null, work);
 +                            }
 +                        });
 +
 +                work = result.third();
 +                if (result.first() != null) {
 +                    return result;
 +                }
 +            } catch (final NoTransitionException e) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Unable to transition into Starting state due to " + e.getMessage());
 +                }
 +            }
 +
 +            final VMInstanceVO instance = _vmDao.findById(vmId);
 +            if (instance == null) {
 +                throw new ConcurrentOperationException("Unable to acquire lock on " + vm);
 +            }
 +
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Determining why we're unable to update the state to Starting for " + instance + ".  Retry=" + retry);
 +            }
 +
 +            final State state = instance.getState();
 +            if (state == State.Running) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("VM is already started: " + vm);
 +                }
 +                return null;
 +            }
 +
 +            if (state.isTransitional()) {
 +                if (!checkWorkItems(vm, state)) {
 +                    throw new ConcurrentOperationException("There are concurrent operations on " + vm);
 +                } else {
 +                    continue;
 +                }
 +            }
 +
 +            if (state != State.Stopped) {
 +                s_logger.debug("VM " + vm + " is not in a state to be started: " + state);
 +                return null;
 +            }
 +        }
 +
 +        throw new ConcurrentOperationException("Unable to change the state of " + vm);
 +    }
 +
 +    protected <T extends VMInstanceVO> boolean changeState(final T vm, final Event event, final Long hostId, final ItWorkVO work, final Step step) throws NoTransitionException {
 +        // FIXME: We should do this better.
 +        Step previousStep = null;
 +        if (work != null) {
 +            previousStep = work.getStep();
 +            _workDao.updateStep(work, step);
 +        }
 +        boolean result = false;
 +        try {
 +            result = stateTransitTo(vm, event, hostId);
 +            return result;
 +        } finally {
 +            if (!result && work != null) {
 +                _workDao.updateStep(work, previousStep);
 +            }
 +        }
 +    }
 +
 +    protected boolean areAffinityGroupsAssociated(final VirtualMachineProfile vmProfile) {
 +        final VirtualMachine vm = vmProfile.getVirtualMachine();
 +        final long vmGroupCount = _affinityGroupVMMapDao.countAffinityGroupsForVm(vm.getId());
 +
 +        if (vmGroupCount > 0) {
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public void advanceStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlanner planner)
 +            throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
 +        advanceStart(vmUuid, params, null, planner);
 +    }
 +
 +    @Override
 +    public void advanceStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner)
 +            throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if ( jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateStart(vmUuid, params, planToDeploy, planner);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = startVmThroughJobQueue(vmUuid, params, planToDeploy, planner);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobResult;
 +                } else if (jobResult instanceof InsufficientCapacityException) {
 +                    throw (InsufficientCapacityException)jobResult;
 +                } else if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void setupAgentSecurity(final Host vmHost, final Map<String, String> sshAccessDetails, final VirtualMachine vm) throws AgentUnavailableException, OperationTimedoutException {
 +        final String csr = caManager.generateKeyStoreAndCsr(vmHost, sshAccessDetails);
 +        if (!Strings.isNullOrEmpty(csr)) {
 +            final Map<String, String> ipAddressDetails = new HashMap<>(sshAccessDetails);
 +            ipAddressDetails.remove(NetworkElementCommand.ROUTER_NAME);
 +            final Certificate certificate = caManager.issueCertificate(csr, Arrays.asList(vm.getHostName(), vm.getInstanceName()),
 +                    new ArrayList<>(ipAddressDetails.values()), CAManager.CertValidityPeriod.value(), null);
 +            final boolean result = caManager.deployCertificate(vmHost, certificate, false, sshAccessDetails);
 +            if (!result) {
 +                s_logger.error("Failed to setup certificate for system vm: " + vm.getInstanceName());
 +            }
 +        } else {
 +            s_logger.error("Failed to setup keystore and generate CSR for system vm: " + vm.getInstanceName());
 +        }
 +    }
 +
 +    @Override
 +    public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner)
 +            throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
 +
 +        final CallContext cctxt = CallContext.current();
 +        final Account account = cctxt.getCallingAccount();
 +        final User caller = cctxt.getCallingUser();
 +
 +        VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final VirtualMachineGuru vmGuru = getVmGuru(vm);
 +
 +        final Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = changeToStartState(vmGuru, vm, caller, account);
 +        if (start == null) {
 +            return;
 +        }
 +
 +        vm = start.first();
 +        final ReservationContext ctx = start.second();
 +        ItWorkVO work = start.third();
 +
 +        VMInstanceVO startedVm = null;
 +        final ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
 +        final VirtualMachineTemplate template = _entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vm.getTemplateId());
 +
 +        DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodIdToDeployIn(), null, null, null, null, ctx);
 +        if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("advanceStart: DeploymentPlan is provided, using dcId:" + planToDeploy.getDataCenterId() + ", podId: " + planToDeploy.getPodId() +
 +                        ", clusterId: " + planToDeploy.getClusterId() + ", hostId: " + planToDeploy.getHostId() + ", poolId: " + planToDeploy.getPoolId());
 +            }
 +            plan =
 +                    new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(), planToDeploy.getHostId(),
 +                            planToDeploy.getPoolId(), planToDeploy.getPhysicalNetworkId(), ctx);
 +        }
 +
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
 +
 +        boolean canRetry = true;
 +        ExcludeList avoids = null;
 +        try {
 +            final Journal journal = start.second().getJournal();
 +
 +            if (planToDeploy != null) {
 +                avoids = planToDeploy.getAvoids();
 +            }
 +            if (avoids == null) {
 +                avoids = new ExcludeList();
 +            }
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid());
 +            }
 +
 +            boolean planChangedByVolume = false;
 +            boolean reuseVolume = true;
 +            final DataCenterDeployment originalPlan = plan;
 +
 +            int retry = StartRetry.value();
 +            while (retry-- != 0) { // It's != so that it can match -1.
 +
 +                if (reuseVolume) {
 +                    // edit plan if this vm's ROOT volume is in READY state already
 +                    final List<VolumeVO> vols = _volsDao.findReadyRootVolumesByInstance(vm.getId());
 +                    for (final VolumeVO vol : vols) {
 +                        // make sure if the templateId is unchanged. If it is changed,
 +                        // let planner
 +                        // reassign pool for the volume even if it ready.
 +                        final Long volTemplateId = vol.getTemplateId();
 +                        if (volTemplateId != null && volTemplateId.longValue() != template.getId()) {
 +                            if (s_logger.isDebugEnabled()) {
 +                                s_logger.debug(vol + " of " + vm + " is READY, but template ids don't match, let the planner reassign a new pool");
 +                            }
 +                            continue;
 +                        }
 +
 +                        final StoragePool pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(vol.getPoolId());
 +                        if (!pool.isInMaintenance()) {
 +                            if (s_logger.isDebugEnabled()) {
 +                                s_logger.debug("Root volume is ready, need to place VM in volume's cluster");
 +                            }
 +                            final long rootVolDcId = pool.getDataCenterId();
 +                            final Long rootVolPodId = pool.getPodId();
 +                            final Long rootVolClusterId = pool.getClusterId();
 +                            if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) {
 +                                final Long clusterIdSpecified = planToDeploy.getClusterId();
 +                                if (clusterIdSpecified != null && rootVolClusterId != null) {
 +                                    if (rootVolClusterId.longValue() != clusterIdSpecified.longValue()) {
 +                                        // cannot satisfy the plan passed in to the
 +                                        // planner
 +                                        if (s_logger.isDebugEnabled()) {
 +                                            s_logger.debug("Cannot satisfy the deployment plan passed in since the ready Root volume is in different cluster. volume's cluster: " +
 +                                                    rootVolClusterId + ", cluster specified: " + clusterIdSpecified);
 +                                        }
 +                                        throw new ResourceUnavailableException(
 +                                                "Root volume is ready in different cluster, Deployment plan provided cannot be satisfied, unable to create a deployment for " +
 +                                                        vm, Cluster.class, clusterIdSpecified);
 +                                    }
 +                                }
 +                                plan =
 +                                        new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(),
 +                                                planToDeploy.getHostId(), vol.getPoolId(), null, ctx);
 +                            } else {
 +                                plan = new DataCenterDeployment(rootVolDcId, rootVolPodId, rootVolClusterId, null, vol.getPoolId(), null, ctx);
 +                                if (s_logger.isDebugEnabled()) {
 +                                    s_logger.debug(vol + " is READY, changing deployment plan to use this pool's dcId: " + rootVolDcId + " , podId: " + rootVolPodId +
 +                                            " , and clusterId: " + rootVolClusterId);
 +                                }
 +                                planChangedByVolume = true;
 +                            }
 +                        }
 +                    }
 +                }
 +
 +                final Account owner = _entityMgr.findById(Account.class, vm.getAccountId());
 +                final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, owner, params);
 +                DeployDestination dest = null;
 +                try {
 +                    dest = _dpMgr.planDeployment(vmProfile, plan, avoids, planner);
 +                } catch (final AffinityConflictException e2) {
 +                    s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2);
 +                    throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict");
 +
 +                }
 +
 +                if (dest == null) {
 +                    if (planChangedByVolume) {
 +                        plan = originalPlan;
 +                        planChangedByVolume = false;
 +                        //do not enter volume reuse for next retry, since we want to look for resources outside the volume's cluster
 +                        reuseVolume = false;
 +                        continue;
 +                    }
 +                    throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId(),
 +                            areAffinityGroupsAssociated(vmProfile));
 +                }
 +
 +                if (dest != null) {
 +                    avoids.addHost(dest.getHost().getId());
 +                    journal.record("Deployment found ", vmProfile, dest);
 +                }
 +
 +                long destHostId = dest.getHost().getId();
 +                vm.setPodIdToDeployIn(dest.getPod().getId());
 +                final Long cluster_id = dest.getCluster().getId();
 +                final ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id, "cpuOvercommitRatio");
 +                final ClusterDetailsVO cluster_detail_ram = _clusterDetailsDao.findDetail(cluster_id, "memoryOvercommitRatio");
 +                //storing the value of overcommit in the vm_details table for doing a capacity check in case the cluster overcommit ratio is changed.
 +                if (_uservmDetailsDao.findDetail(vm.getId(), "cpuOvercommitRatio") == null &&
 +                        (Float.parseFloat(cluster_detail_cpu.getValue()) > 1f || Float.parseFloat(cluster_detail_ram.getValue()) > 1f)) {
 +                    _uservmDetailsDao.addDetail(vm.getId(), "cpuOvercommitRatio", cluster_detail_cpu.getValue(), true);
 +                    _uservmDetailsDao.addDetail(vm.getId(), "memoryOvercommitRatio", cluster_detail_ram.getValue(), true);
 +                } else if (_uservmDetailsDao.findDetail(vm.getId(), "cpuOvercommitRatio") != null) {
 +                    _uservmDetailsDao.addDetail(vm.getId(), "cpuOvercommitRatio", cluster_detail_cpu.getValue(), true);
 +                    _uservmDetailsDao.addDetail(vm.getId(), "memoryOvercommitRatio", cluster_detail_ram.getValue(), true);
 +                }
 +
 +                vmProfile.setCpuOvercommitRatio(Float.parseFloat(cluster_detail_cpu.getValue()));
 +                vmProfile.setMemoryOvercommitRatio(Float.parseFloat(cluster_detail_ram.getValue()));
 +                StartAnswer startAnswer = null;
 +
 +                try {
 +                    if (!changeState(vm, Event.OperationRetry, destHostId, work, Step.Prepare)) {
 +                        throw new ConcurrentOperationException("Unable to update the state of the Virtual Machine "+vm.getUuid()+" oldstate: "+vm.getState()+ "Event :"+Event.OperationRetry);
 +                    }
 +                } catch (final NoTransitionException e1) {
 +                    throw new ConcurrentOperationException(e1.getMessage());
 +                }
 +
 +                try {
 +                    _networkMgr.prepare(vmProfile, new DeployDestination(dest.getDataCenter(), dest.getPod(), null, null), ctx);
 +                    if (vm.getHypervisorType() != HypervisorType.BareMetal) {
 +                        volumeMgr.prepare(vmProfile, dest);
 +                    }
 +                    //since StorageMgr succeeded in volume creation, reuse Volume for further tries until current cluster has capacity
 +                    if (!reuseVolume) {
 +                        reuseVolume = true;
 +                    }
 +
 +                    Commands cmds = null;
 +                    vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx);
 +
 +                    final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
 +
 +                    handlePath(vmTO.getDisks(), vm.getHypervisorType());
 +
 +                    cmds = new Commands(Command.OnError.Stop);
 +
 +                    cmds.addCommand(new StartCommand(vmTO, dest.getHost(), getExecuteInSequence(vm.getHypervisorType())));
 +
 +                    vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx);
 +
 +                    work = _workDao.findById(work.getId());
 +                    if (work == null || work.getStep() != Step.Prepare) {
 +                        throw new ConcurrentOperationException("Work steps have been changed: " + work);
 +                    }
 +
 +                    _workDao.updateStep(work, Step.Starting);
 +
 +                    _agentMgr.send(destHostId, cmds);
 +
 +                    _workDao.updateStep(work, Step.Started);
 +
 +                    startAnswer = cmds.getAnswer(StartAnswer.class);
 +                    if (startAnswer != null && startAnswer.getResult()) {
 +                        handlePath(vmTO.getDisks(), startAnswer.getIqnToData());
 +
 +                        final String host_guid = startAnswer.getHost_guid();
 +
 +                        if (host_guid != null) {
 +                            final HostVO finalHost = _resourceMgr.findHostByGuid(host_guid);
 +                            if (finalHost == null) {
 +                                throw new CloudRuntimeException("Host Guid " + host_guid + " doesn't exist in DB, something went wrong while processing start answer: "+startAnswer);
 +                            }
 +                            destHostId = finalHost.getId();
 +                        }
 +                        if (vmGuru.finalizeStart(vmProfile, destHostId, cmds, ctx)) {
 +                            syncDiskChainChange(startAnswer);
 +
 +                            if (!changeState(vm, Event.OperationSucceeded, destHostId, work, Step.Done)) {
 +                                s_logger.error("Unable to transition to a new state. VM uuid: "+vm.getUuid()+    "VM oldstate:"+vm.getState()+"Event:"+Event.OperationSucceeded);
 +                                throw new ConcurrentOperationException("Failed to deploy VM"+ vm.getUuid());
 +                            }
 +
 +                            // Update GPU device capacity
 +                            final GPUDeviceTO gpuDevice = startAnswer.getVirtualMachine().getGpuDevice();
 +                            if (gpuDevice != null) {
 +                                _resourceMgr.updateGPUDetails(destHostId, gpuDevice.getGroupDetails());
 +                            }
 +
 +                            // Remove the information on whether it was a deploy vm request.The deployvm=true information
 +                            // is set only when the vm is being deployed. When a vm is started from a stop state the
 +                            // information isn't set,
 +                            if (_uservmDetailsDao.findDetail(vm.getId(), "deployvm") != null) {
 +                                _uservmDetailsDao.removeDetail(vm.getId(), "deployvm");
 +                            }
 +
 +                            startedVm = vm;
 +                            if (s_logger.isDebugEnabled()) {
 +                                s_logger.debug("Start completed for VM " + vm);
 +                            }
 +                            final Host vmHost = _hostDao.findById(destHostId);
 +                            if (vmHost != null && (VirtualMachine.Type.ConsoleProxy.equals(vm.getType()) ||
 +                                    VirtualMachine.Type.SecondaryStorageVm.equals(vm.getType())) && caManager.canProvisionCertificates()) {
 +                                final Map<String, String> sshAccessDetails = _networkMgr.getSystemVMAccessDetails(vm);
 +                                for (int retries = 3; retries > 0; retries--) {
 +                                    try {
 +                                        setupAgentSecurity(vmHost, sshAccessDetails, vm);
 +                                        return;
 +                                    } catch (final Exception e) {
 +                                        s_logger.error("Retrying after catching exception while trying to secure agent for systemvm id=" + vm.getId(), e);
 +                                    }
 +                                }
 +                                throw new CloudRuntimeException("Failed to setup and secure agent for systemvm id=" + vm.getId());
 +                            }
 +                            return;
 +                        } else {
 +                            if (s_logger.isDebugEnabled()) {
 +                                s_logger.info("The guru did not like the answers so stopping " + vm);
 +                            }
 +                            StopCommand stopCmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false);
 +                            stopCmd.setControlIp(getControlNicIpForVM(vm));
 +                            final StopCommand cmd = stopCmd;
 +                            final Answer answer = _agentMgr.easySend(destHostId, cmd);
 +                            if (answer != null && answer instanceof StopAnswer) {
 +                                final StopAnswer stopAns = (StopAnswer)answer;
 +                                if (vm.getType() == VirtualMachine.Type.User) {
 +                                    final String platform = stopAns.getPlatform();
 +                                    if (platform != null) {
 +                                        final Map<String,String> vmmetadata = new HashMap<String,String>();
 +                                        vmmetadata.put(vm.getInstanceName(), platform);
 +                                        syncVMMetaData(vmmetadata);
 +                                    }
 +                                }
 +                            }
 +
 +                            if (answer == null || !answer.getResult()) {
 +                                s_logger.warn("Unable to stop " + vm + " due to " + (answer != null ? answer.getDetails() : "no answers"));
 +                                _haMgr.scheduleStop(vm, destHostId, WorkType.ForceStop);
 +                                throw new ExecutionException("Unable to stop this VM, "+vm.getUuid()+" so we are unable to retry the start operation");
 +                            }
 +                            throw new ExecutionException("Unable to start  VM:"+vm.getUuid()+" due to error in finalizeStart, not retrying");
 +                        }
 +                    }
 +                    s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + (startAnswer == null ? " no start answer" : startAnswer.getDetails()));
 +                    if (startAnswer != null && startAnswer.getContextParam("stopRetry") != null) {
 +                        break;
 +                    }
 +
 +                } catch (OperationTimedoutException e) {
 +                    s_logger.debug("Unable to send the start command to host " + dest.getHost()+" failed to start VM: "+vm.getUuid());
 +                    if (e.isActive()) {
 +                        _haMgr.scheduleStop(vm, destHostId, WorkType.CheckStop);
 +                    }
 +                    canRetry = false;
 +                    throw new AgentUnavailableException("Unable to start " + vm.getHostName(), destHostId, e);
 +                } catch (final ResourceUnavailableException e) {
 +                    s_logger.info("Unable to contact resource.", e);
 +                    if (!avoids.add(e)) {
 +                        if (e.getScope() == Volume.class || e.getScope() == Nic.class) {
 +                            throw e;
 +                        } else {
 +                            s_logger.warn("unexpected ResourceUnavailableException : " + e.getScope().getName(), e);
 +                            throw e;
 +                        }
 +                    }
 +                } catch (final InsufficientCapacityException e) {
 +                    s_logger.info("Insufficient capacity ", e);
 +                    if (!avoids.add(e)) {
 +                        if (e.getScope() == Volume.class || e.getScope() == Nic.class) {
 +                            throw e;
 +                        } else {
 +                            s_logger.warn("unexpected InsufficientCapacityException : " + e.getScope().getName(), e);
 +                        }
 +                    }
 +                } catch (final ExecutionException e) {
 +                    s_logger.error("Failed to start instance " + vm, e);
 +                    throw new AgentUnavailableException("Unable to start instance due to " + e.getMessage(), destHostId, e);
 +                } catch (final NoTransitionException e) {
 +                    s_logger.error("Failed to start instance " + vm, e);
 +                    throw new AgentUnavailableException("Unable to start instance due to " + e.getMessage(), destHostId, e);
 +                } finally {
 +                    if (startedVm == null && canRetry) {
 +                        final Step prevStep = work.getStep();
 +                        _workDao.updateStep(work, Step.Release);
 +                        // If previous step was started/ing && we got a valid answer
 +                        if ((prevStep == Step.Started || prevStep == Step.Starting) && startAnswer != null && startAnswer.getResult()) {  //TODO check the response of cleanup and record it in DB for retry
 +                            cleanup(vmGuru, vmProfile, work, Event.OperationFailed, false);
 +                        } else {
 +                            //if step is not starting/started, send cleanup command with force=true
 +                            cleanup(vmGuru, vmProfile, work, Event.OperationFailed, true);
 +                        }
 +                    }
 +                }
 +            }
 +        } finally {
 +            if (startedVm == null) {
 +                if (canRetry) {
 +                    try {
 +                        changeState(vm, Event.OperationFailed, null, work, Step.Done);
 +                    } catch (final NoTransitionException e) {
 +                        throw new ConcurrentOperationException(e.getMessage());
 +                    }
 +                }
 +            }
 +
 +            if (planToDeploy != null) {
 +                planToDeploy.setAvoids(avoids);
 +            }
 +        }
 +
 +        if (startedVm == null) {
 +            throw new CloudRuntimeException("Unable to start instance '" + vm.getHostName() + "' (" + vm.getUuid() + "), see management server log for details");
 +        }
 +    }
 +
 +    // for managed storage on KVM, need to make sure the path field of the volume in question is populated with the IQN
 +    private void handlePath(final DiskTO[] disks, final HypervisorType hypervisorType) {
 +        if (hypervisorType != HypervisorType.KVM) {
 +            return;
 +        }
 +
 +        if (disks != null) {
 +            for (final DiskTO disk : disks) {
 +                final Map<String, String> details = disk.getDetails();
 +                final boolean isManaged = details != null && Boolean.parseBoolean(details.get(DiskTO.MANAGED));
 +
 +                if (isManaged && disk.getPath() == null) {
 +                    final Long volumeId = disk.getData().getId();
 +                    final VolumeVO volume = _volsDao.findById(volumeId);
 +
 +                    disk.setPath(volume.get_iScsiName());
 +
 +                    if (disk.getData() instanceof VolumeObjectTO) {
 +                        final VolumeObjectTO volTo = (VolumeObjectTO)disk.getData();
 +
 +                        volTo.setPath(volume.get_iScsiName());
 +                    }
 +
 +                    volume.setPath(volume.get_iScsiName());
 +
 +                    _volsDao.update(volumeId, volume);
 +                }
 +            }
 +        }
 +    }
 +
 +    // for managed storage on XenServer and VMware, need to update the DB with a path if the VDI/VMDK file was newly created
 +    private void handlePath(final DiskTO[] disks, final Map<String, Map<String, String>> iqnToData) {
 +        if (disks != null && iqnToData != null) {
 +            for (final DiskTO disk : disks) {
 +                final Map<String, String> details = disk.getDetails();
 +                final boolean isManaged = details != null && Boolean.parseBoolean(details.get(DiskTO.MANAGED));
 +
 +                if (isManaged) {
 +                    final Long volumeId = disk.getData().getId();
 +                    final VolumeVO volume = _volsDao.findById(volumeId);
 +                    final String iScsiName = volume.get_iScsiName();
 +
 +                    boolean update = false;
 +
 +                    final Map<String, String> data = iqnToData.get(iScsiName);
 +
 +                    if (data != null) {
 +                        final String path = data.get(StartAnswer.PATH);
 +
 +                        if (path != null) {
 +                            volume.setPath(path);
 +
 +                            update = true;
 +                        }
 +
 +                        final String imageFormat = data.get(StartAnswer.IMAGE_FORMAT);
 +
 +                        if (imageFormat != null) {
 +                            volume.setFormat(ImageFormat.valueOf(imageFormat));
 +
 +                            update = true;
 +                        }
 +
 +                        if (update) {
 +                            _volsDao.update(volumeId, volume);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    private void syncDiskChainChange(final StartAnswer answer) {
 +        final VirtualMachineTO vmSpec = answer.getVirtualMachine();
 +
 +        for (final DiskTO disk : vmSpec.getDisks()) {
 +            if (disk.getType() != Volume.Type.ISO) {
 +                final VolumeObjectTO vol = (VolumeObjectTO)disk.getData();
 +                final VolumeVO volume = _volsDao.findById(vol.getId());
 +
 +                // Use getPath() from VolumeVO to get a fresh copy of what's in the DB.
 +                // Before doing this, in a certain situation, getPath() from VolumeObjectTO
 +                // returned null instead of an actual path (because it was out of date with the DB).
 +                if(vol.getPath() != null) {
 +                    volumeMgr.updateVolumeDiskChain(vol.getId(), vol.getPath(), vol.getChainInfo());
 +                } else {
 +                    volumeMgr.updateVolumeDiskChain(vol.getId(), volume.getPath(), vol.getChainInfo());
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void stop(final String vmUuid) throws ResourceUnavailableException {
 +        try {
 +            advanceStop(vmUuid, false);
 +        } catch (final OperationTimedoutException e) {
 +            throw new AgentUnavailableException("Unable to stop vm because the operation to stop timed out", e.getAgentId(), e);
 +        } catch (final ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Unable to stop vm because of a concurrent operation", e);
 +        }
 +
 +    }
 +
 +    @Override
 +    public void stopForced(String vmUuid) throws ResourceUnavailableException {
 +        try {
 +            advanceStop(vmUuid, true);
 +        } catch (final OperationTimedoutException e) {
 +            throw new AgentUnavailableException("Unable to stop vm because the operation to stop timed out", e.getAgentId(), e);
 +        } catch (final ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Unable to stop vm because of a concurrent operation", e);
 +        }
 +    }
 +
 +    @Override
 +    public boolean getExecuteInSequence(final HypervisorType hypervisorType) {
 +        if (HypervisorType.KVM == hypervisorType || HypervisorType.XenServer == hypervisorType || HypervisorType.Hyperv == hypervisorType || HypervisorType.LXC == hypervisorType) {
 +            return false;
 +        } else if (HypervisorType.VMware == hypervisorType) {
 +            final Boolean fullClone = HypervisorGuru.VmwareFullClone.value();
 +            return fullClone;
 +        } else {
 +            return ExecuteInSequence.value();
 +        }
 +    }
 +
 +    private List<Map<String, String>> getVolumesToDisconnect(VirtualMachine vm) {
 +        List<Map<String, String>> volumesToDisconnect = new ArrayList<>();
 +
 +        List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
 +
 +        if (CollectionUtils.isEmpty(volumes)) {
 +            return volumesToDisconnect;
 +        }
 +
 +        for (VolumeVO volume : volumes) {
 +            StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId());
 +
 +            if (storagePool != null && storagePool.isManaged()) {
 +                Map<String, String> info = new HashMap<>(3);
 +
 +                info.put(DiskTO.STORAGE_HOST, storagePool.getHostAddress());
 +                info.put(DiskTO.STORAGE_PORT, String.valueOf(storagePool.getPort()));
 +                info.put(DiskTO.IQN, volume.get_iScsiName());
 +
 +                volumesToDisconnect.add(info);
 +            }
 +        }
 +
 +        return volumesToDisconnect;
 +    }
 +
 +    protected boolean sendStop(final VirtualMachineGuru guru, final VirtualMachineProfile profile, final boolean force, final boolean checkBeforeCleanup) {
 +        final VirtualMachine vm = profile.getVirtualMachine();
 +        StopCommand stpCmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), checkBeforeCleanup);
 +        stpCmd.setControlIp(getControlNicIpForVM(vm));
 +        stpCmd.setVolumesToDisconnect(getVolumesToDisconnect(vm));
 +        final StopCommand stop = stpCmd;
 +        try {
 +            Answer answer = null;
 +            if(vm.getHostId() != null) {
 +                answer = _agentMgr.send(vm.getHostId(), stop);
 +            }
 +            if (answer != null && answer instanceof StopAnswer) {
 +                final StopAnswer stopAns = (StopAnswer)answer;
 +                if (vm.getType() == VirtualMachine.Type.User) {
 +                    final String platform = stopAns.getPlatform();
 +                    if (platform != null) {
 +                        final UserVmVO userVm = _userVmDao.findById(vm.getId());
 +                        _userVmDao.loadDetails(userVm);
 +                        userVm.setDetail("platform", platform);
 +                        _userVmDao.saveDetails(userVm);
 +                    }
 +                }
 +
 +                final GPUDeviceTO gpuDevice = stop.getGpuDevice();
 +                if (gpuDevice != null) {
 +                    _resourceMgr.updateGPUDetails(vm.getHostId(), gpuDevice.getGroupDetails());
 +                }
 +                if (!answer.getResult()) {
 +                    final String details = answer.getDetails();
 +                    s_logger.debug("Unable to stop VM due to " + details);
 +                    return false;
 +                }
 +
 +                guru.finalizeStop(profile, answer);
 +            } else {
 +                s_logger.error("Invalid answer received in response to a StopCommand for " + vm.getInstanceName());
 +                return false;
 +            }
 +
 +        } catch (final AgentUnavailableException e) {
 +            if (!force) {
 +                return false;
 +            }
 +        } catch (final OperationTimedoutException e) {
 +            if (!force) {
 +                return false;
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    protected boolean cleanup(final VirtualMachineGuru guru, final VirtualMachineProfile profile, final ItWorkVO work, final Event event, final boolean cleanUpEvenIfUnableToStop) {
 +        final VirtualMachine vm = profile.getVirtualMachine();
 +        final State state = vm.getState();
 +        s_logger.debug("Cleaning up resources for the vm " + vm + " in " + state + " state");
 +        try {
 +            if (state == State.Starting) {
 +                if (work != null) {
 +                    final Step step = work.getStep();
 +                    if (step == Step.Starting && !cleanUpEvenIfUnableToStop) {
 +                        s_logger.warn("Unable to cleanup vm " + vm + "; work state is incorrect: " + step);
 +                        return false;
 +                    }
 +
 +                    if (step == Step.Started || step == Step.Starting || step == Step.Release) {
 +                        if (vm.getHostId() != null) {
 +                            if (!sendStop(guru, profile, cleanUpEvenIfUnableToStop, false)) {
 +                                s_logger.warn("Failed to stop vm " + vm + " in " + State.Starting + " state as a part of cleanup process");
 +                                return false;
 +                            }
 +                        }
 +                    }
 +
 +                    if (step != Step.Release && step != Step.Prepare && step != Step.Started && step != Step.Starting) {
 +                        s_logger.debug("Cleanup is not needed for vm " + vm + "; work state is incorrect: " + step);
 +                        return true;
 +                    }
 +                } else {
 +                    if (vm.getHostId() != null) {
 +                        if (!sendStop(guru, profile, cleanUpEvenIfUnableToStop, false)) {
 +                            s_logger.warn("Failed to stop vm " + vm + " in " + State.Starting + " state as a part of cleanup process");
 +                            return false;
 +                        }
 +                    }
 +                }
 +
 +            } else if (state == State.Stopping) {
 +                if (vm.getHostId() != null) {
 +                    if (!sendStop(guru, profile, cleanUpEvenIfUnableToStop, false)) {
 +                        s_logger.warn("Failed to stop vm " + vm + " in " + State.Stopping + " state as a part of cleanup process");
 +                        return false;
 +                    }
 +                }
 +            } else if (state == State.Migrating) {
 +                if (vm.getHostId() != null) {
 +                    if (!sendStop(guru, profile, cleanUpEvenIfUnableToStop, false)) {
 +                        s_logger.warn("Failed to stop vm " + vm + " in " + State.Migrating + " state as a part of cleanup process");
 +                        return false;
 +                    }
 +                }
 +                if (vm.getLastHostId() != null) {
 +                    if (!sendStop(guru, profile, cleanUpEvenIfUnableToStop, false)) {
 +                        s_logger.warn("Failed to stop vm " + vm + " in " + State.Migrating + " state as a part of cleanup process");
 +                        return false;
 +                    }
 +                }
 +            } else if (state == State.Running) {
 +                if (!sendStop(guru, profile, cleanUpEvenIfUnableToStop, false)) {
 +                    s_logger.warn("Failed to stop vm " + vm + " in " + State.Running + " state as a part of cleanup process");
 +                    return false;
 +                }
 +            }
 +        } finally {
 +            try {
 +                _networkMgr.release(profile, cleanUpEvenIfUnableToStop);
 +                s_logger.debug("Successfully released network resources for the vm " + vm);
 +            } catch (final Exception e) {
 +                s_logger.warn("Unable to release some network resources.", e);
 +            }
 +
 +            volumeMgr.release(profile);
 +            s_logger.debug("Successfully cleanued up resources for the vm " + vm + " in " + state + " state");
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public void advanceStop(final String vmUuid, final boolean cleanUpEvenIfUnableToStop)
 +            throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateStop(vmUuid, cleanUpEvenIfUnableToStop);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +
 +        } else {
 +            final Outcome<VirtualMachine> outcome = stopVmThroughJobQueue(vmUuid, cleanUpEvenIfUnableToStop);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof AgentUnavailableException) {
 +                    throw (AgentUnavailableException)jobResult;
 +                } else if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof OperationTimedoutException) {
 +                    throw (OperationTimedoutException)jobResult;
 +                } else if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void orchestrateStop(final String vmUuid, final boolean cleanUpEvenIfUnableToStop) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        advanceStop(vm, cleanUpEvenIfUnableToStop);
 +    }
 +
 +    private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnableToStop) throws AgentUnavailableException, OperationTimedoutException,
 +    ConcurrentOperationException {
 +        final State state = vm.getState();
 +        if (state == State.Stopped) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("VM is already stopped: " + vm);
 +            }
 +            return;
 +        }
 +
 +        if (state == State.Destroyed || state == State.Expunging || state == State.Error) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Stopped called on " + vm + " but the state is " + state);
 +            }
 +            return;
 +        }
 +        // grab outstanding work item if any
 +        final ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState());
 +        if (work != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Found an outstanding work item for this vm " + vm + " with state:" + vm.getState() + ", work id:" + work.getId());
 +            }
 +        }
 +        final Long hostId = vm.getHostId();
 +        if (hostId == null) {
 +            if (!cleanUpEvenIfUnableToStop) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("HostId is null but this is not a forced stop, cannot stop vm " + vm + " with state:" + vm.getState());
 +                }
 +                throw new CloudRuntimeException("Unable to stop " + vm);
 +            }
 +            try {
 +                stateTransitTo(vm, Event.AgentReportStopped, null, null);
 +            } catch (final NoTransitionException e) {
 +                s_logger.warn(e.getMessage());
 +            }
 +            // mark outstanding work item if any as done
 +            if (work != null) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Updating work item to Done, id:" + work.getId());
 +                }
 +                work.setStep(Step.Done);
 +                _workDao.update(work.getId(), work);
 +            }
 +            return;
 +        } else {
 +            HostVO host = _hostDao.findById(hostId);
 +            if (!cleanUpEvenIfUnableToStop && vm.getState() == State.Running && host.getResourceState() == ResourceState.PrepareForMaintenance) {
 +                s_logger.debug("Host is in PrepareForMaintenance state - Stop VM operation on the VM id: " + vm.getId() + " is not allowed");
 +                throw new CloudRuntimeException("Stop VM operation on the VM id: " + vm.getId() + " is not allowed as host is preparing for maintenance mode");
 +            }
 +        }
 +
 +        final VirtualMachineGuru vmGuru = getVmGuru(vm);
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +
 +        try {
 +            if (!stateTransitTo(vm, Event.StopRequested, vm.getHostId())) {
 +                throw new ConcurrentOperationException("VM is being operated on.");
 +            }
 +        } catch (final NoTransitionException e1) {
 +            if (!cleanUpEvenIfUnableToStop) {
 +                throw new CloudRuntimeException("We cannot stop " + vm + " when it is in state " + vm.getState());
 +            }
 +            final boolean doCleanup = true;
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Unable to transition the state but we're moving on because it's forced stop");
 +            }
 +
 +            if (doCleanup) {
 +                if (cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.StopRequested, cleanUpEvenIfUnableToStop)) {
 +                    try {
 +                        if (s_logger.isDebugEnabled() && work != null) {
 +                            s_logger.debug("Updating work item to Done, id:" + work.getId());
 +                        }
 +                        if (!changeState(vm, Event.AgentReportStopped, null, work, Step.Done)) {
 +                            throw new CloudRuntimeException("Unable to stop " + vm);
 +                        }
 +
 +                    } catch (final NoTransitionException e) {
 +                        s_logger.warn("Unable to cleanup " + vm);
 +                        throw new CloudRuntimeException("Unable to stop " + vm, e);
 +                    }
 +                } else {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Failed to cleanup VM: " + vm);
 +                    }
 +                    throw new CloudRuntimeException("Failed to cleanup " + vm + " , current state " + vm.getState());
 +                }
 +            }
 +        }
 +
 +        if (vm.getState() != State.Stopping) {
 +            throw new CloudRuntimeException("We cannot proceed with stop VM " + vm + " since it is not in 'Stopping' state, current state: " + vm.getState());
 +        }
 +
 +        vmGuru.prepareStop(profile);
 +
 +        final StopCommand stop = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false, cleanUpEvenIfUnableToStop);
 +        stop.setControlIp(getControlNicIpForVM(vm));
 +
 +        boolean stopped = false;
 +        Answer answer = null;
 +        try {
 +            answer = _agentMgr.send(vm.getHostId(), stop);
 +            if (answer != null) {
 +                if (answer instanceof StopAnswer) {
 +                    final StopAnswer stopAns = (StopAnswer)answer;
 +                    if (vm.getType() == VirtualMachine.Type.User) {
 +                        final String platform = stopAns.getPlatform();
 +                        if (platform != null) {
 +                            final UserVmVO userVm = _userVmDao.findById(vm.getId());
 +                            _userVmDao.loadDetails(userVm);
 +                            userVm.setDetail("platform", platform);
 +                            _userVmDao.saveDetails(userVm);
 +                        }
 +                    }
 +                }
 +                stopped = answer.getResult();
 +                if (!stopped) {
 +                    throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails());
 +                }
 +                vmGuru.finalizeStop(profile, answer);
 +                final GPUDeviceTO gpuDevice = stop.getGpuDevice();
 +                if (gpuDevice != null) {
 +                    _resourceMgr.updateGPUDetails(vm.getHostId(), gpuDevice.getGroupDetails());
 +                }
 +            } else {
 +                throw new CloudRuntimeException("Invalid answer received in response to a StopCommand on " + vm.instanceName);
 +            }
 +
 +        } catch (final AgentUnavailableException e) {
 +            s_logger.warn("Unable to stop vm, agent unavailable: " + e.toString());
 +        } catch (final OperationTimedoutException e) {
 +            s_logger.warn("Unable to stop vm, operation timed out: " + e.toString());
 +        } finally {
 +            if (!stopped) {
 +                if (!cleanUpEvenIfUnableToStop) {
 +                    s_logger.warn("Unable to stop vm " + vm);
 +                    try {
 +                        stateTransitTo(vm, Event.OperationFailed, vm.getHostId());
 +                    } catch (final NoTransitionException e) {
 +                        s_logger.warn("Unable to transition the state " + vm);
 +                    }
 +                    throw new CloudRuntimeException("Unable to stop " + vm);
 +                } else {
 +                    s_logger.warn("Unable to actually stop " + vm + " but continue with release because it's a force stop");
 +                    vmGuru.finalizeStop(profile, answer);
 +                }
 +            }
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug(vm + " is stopped on the host.  Proceeding to release resource held.");
 +        }
 +
 +        try {
 +            _networkMgr.release(profile, cleanUpEvenIfUnableToStop);
 +            s_logger.debug("Successfully released network resources for the vm " + vm);
 +        } catch (final Exception e) {
 +            s_logger.warn("Unable to release some network resources.", e);
 +        }
 +
 +        try {
 +            if (vm.getHypervisorType() != HypervisorType.BareMetal) {
 +                volumeMgr.release(profile);
 +                s_logger.debug("Successfully released storage resources for the vm " + vm);
 +            }
 +        } catch (final Exception e) {
 +            s_logger.warn("Unable to release storage resources.", e);
 +        }
 +
 +        try {
 +            if (work != null) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Updating the outstanding work item to Done, id:" + work.getId());
 +                }
 +                work.setStep(Step.Done);
 +                _workDao.update(work.getId(), work);
 +            }
 +
 +            if (!stateTransitTo(vm, Event.OperationSucceeded, null)) {
 +                throw new CloudRuntimeException("unable to stop " + vm);
 +            }
 +        } catch (final NoTransitionException e) {
 +            s_logger.warn(e.getMessage());
 +            throw new CloudRuntimeException("Unable to stop " + vm);
 +        }
 +    }
 +
 +    private void setStateMachine() {
 +        _stateMachine = VirtualMachine.State.getStateMachine();
 +    }
 +
 +    protected boolean stateTransitTo(final VMInstanceVO vm, final VirtualMachine.Event e, final Long hostId, final String reservationId) throws NoTransitionException {
 +        // if there are active vm snapshots task, state change is not allowed
 +
 +        // Disable this hacking thing, VM snapshot task need to be managed by its orchestartion flow istelf instead of
 +        // hacking it here at general VM manager
 +        /*
 +                if (_vmSnapshotMgr.hasActiveVMSnapshotTasks(vm.getId())) {
 +                    s_logger.error("State transit with event: " + e + " failed due to: " + vm.getInstanceName() + " has active VM snapshots tasks");
 +                    return false;
 +                }
 +         */
 +        vm.setReservationId(reservationId);
 +        return _stateMachine.transitTo(vm, e, new Pair<Long, Long>(vm.getHostId(), hostId), _vmDao);
 +    }
 +
 +    @Override
 +    public boolean stateTransitTo(final VirtualMachine vm1, final VirtualMachine.Event e, final Long hostId) throws NoTransitionException {
 +        final VMInstanceVO vm = (VMInstanceVO)vm1;
 +
 +        /*
 +         *  Remove the hacking logic here.
 +                // if there are active vm snapshots task, state change is not allowed
 +                if (_vmSnapshotMgr.hasActiveVMSnapshotTasks(vm.getId())) {
 +                    s_logger.error("State transit with event: " + e + " failed due to: " + vm.getInstanceName() + " has active VM snapshots tasks");
 +                    return false;
 +                }
 +         */
 +
 +        final State oldState = vm.getState();
 +        if (oldState == State.Starting) {
 +            if (e == Event.OperationSucceeded) {
 +                vm.setLastHostId(hostId);
 +            }
 +        } else if (oldState == State.Stopping) {
 +            if (e == Event.OperationSucceeded) {
 +                vm.setLastHostId(vm.getHostId());
 +            }
 +        }
 +        return _stateMachine.transitTo(vm, e, new Pair<Long, Long>(vm.getHostId(), hostId), _vmDao);
 +    }
 +
 +    @Override
 +    public void destroy(final String vmUuid, final boolean expunge) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException {
 +        VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Unable to find vm or vm is destroyed: " + vm);
 +            }
 +            return;
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Destroying vm " + vm + ", expunge flag " + (expunge ? "on" : "off"));
 +        }
 +
 +        advanceStop(vmUuid, VmDestroyForcestop.value());
 +
 +        deleteVMSnapshots(vm, expunge);
 +
 +        Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus status) throws CloudRuntimeException {
 +                VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +                try {
 +                    if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) {
 +                        s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm);
 +                        throw new CloudRuntimeException("Unable to destroy " + vm);
 +                    }
 +                } catch (final NoTransitionException e) {
 +                    s_logger.debug(e.getMessage());
 +                    throw new CloudRuntimeException("Unable to destroy " + vm, e);
 +                }
 +            }
 +        });
 +    }
 +
 +    /**
 +     * Delete vm snapshots depending on vm's hypervisor type. For Vmware, vm snapshots removal is delegated to vm cleanup thread
 +     * to reduce tasks sent to hypervisor (one tasks to delete vm snapshots and vm itself
 +     * instead of one task for each vm snapshot plus another for the vm)
 +     * @param vm vm
 +     * @param expunge indicates if vm should be expunged
 +     */
 +    private void deleteVMSnapshots(VMInstanceVO vm, boolean expunge) {
 +        if (! vm.getHypervisorType().equals(HypervisorType.VMware)) {
 +            if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(), null)) {
 +                s_logger.debug("Unable to delete all snapshots for " + vm);
 +                throw new CloudRuntimeException("Unable to delete vm snapshots for " + vm);
 +            }
 +        }
 +        else {
 +            if (expunge) {
 +                _vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId());
 +            }
 +        }
 +    }
 +
 +    protected boolean checkVmOnHost(final VirtualMachine vm, final long hostId) throws AgentUnavailableException, OperationTimedoutException {
 +        final Answer answer = _agentMgr.send(hostId, new CheckVirtualMachineCommand(vm.getInstanceName()));
 +        if (answer == null || !answer.getResult()) {
 +            return false;
 +        }
 +        if (answer instanceof CheckVirtualMachineAnswer) {
 +            final CheckVirtualMachineAnswer vmAnswer = (CheckVirtualMachineAnswer)answer;
 +            if (vmAnswer.getState() == PowerState.PowerOff) {
 +                return false;
 +            }
 +        }
 +
 +        UserVmVO userVm = _userVmDao.findById(vm.getId());
 +        if (userVm != null) {
 +            List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vm.getId());
 +            RestoreVMSnapshotCommand command = _vmSnapshotMgr.createRestoreCommand(userVm, vmSnapshots);
 +            if (command != null) {
 +                RestoreVMSnapshotAnswer restoreVMSnapshotAnswer = (RestoreVMSnapshotAnswer) _agentMgr.send(hostId, command);
 +                if (restoreVMSnapshotAnswer == null || !restoreVMSnapshotAnswer.getResult()) {
 +                    s_logger.warn("Unable to restore the vm snapshot from image file after live migration of vm with vmsnapshots: " + restoreVMSnapshotAnswer.getDetails());
 +                }
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public void storageMigration(final String vmUuid, final StoragePool destPool) {
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateStorageMigration(vmUuid, destPool);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = migrateVmStorageThroughJobQueue(vmUuid, destPool);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void orchestrateStorageMigration(final String vmUuid, final StoragePool destPool) {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        if (destPool == null) {
 +            throw new CloudRuntimeException("Unable to migrate vm: missing destination storage pool");
 +        }
 +
 +        try {
 +            stateTransitTo(vm, VirtualMachine.Event.StorageMigrationRequested, null);
 +        } catch (final NoTransitionException e) {
 +            s_logger.debug("Unable to migrate vm: " + e.toString());
 +            throw new CloudRuntimeException("Unable to migrate vm: " + e.toString());
 +        }
 +
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +        boolean migrationResult = false;
 +        try {
 +            migrationResult = volumeMgr.storageMigration(profile, destPool);
 +
 +            if (migrationResult) {
 +                //if the vm is migrated to different pod in basic mode, need to reallocate ip
 +
 +                if (destPool.getPodId() != null && !destPool.getPodId().equals(vm.getPodIdToDeployIn())) {
 +                    final DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), destPool.getPodId(), null, null, null, null);
 +                    final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, null, null, null, null);
 +                    _networkMgr.reallocate(vmProfile, plan);
 +                }
 +
 +                //when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool
 +                vm.setLastHostId(null);
 +                vm.setPodIdToDeployIn(destPool.getPodId());
 +
 +                // If VM was cold migrated between clusters belonging to two different VMware DCs,
 +                // unregister the VM from the source host and cleanup the associated VM files.
 +                if (vm.getHypervisorType().equals(HypervisorType.VMware)) {
 +                    Long srcClusterId = null;
 +                    Long srcHostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
 +                    if (srcHostId != null) {
 +                        HostVO srcHost = _hostDao.findById(srcHostId);
 +                        srcClusterId = srcHost.getClusterId();
 +                    }
 +
 +                    final Long destClusterId = destPool.getClusterId();
 +                    if (srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId)) {
 +                        final String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
 +                        final String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId);
 +                        if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
 +                            s_logger.debug("Since VM's storage was successfully migrated across VMware Datacenters, unregistering VM: " + vm.getInstanceName() +
 +                                    " from source host: " + srcHostId);
 +                            final UnregisterVMCommand uvc = new UnregisterVMCommand(vm.getInstanceName());
 +                            uvc.setCleanupVmFiles(true);
 +                            try {
 +                                _agentMgr.send(srcHostId, uvc);
 +                            } catch (final AgentUnavailableException | OperationTimedoutException e) {
 +                                throw new CloudRuntimeException("Failed to unregister VM: " + vm.getInstanceName() + " from source host: " + srcHostId +
 +                                        " after successfully migrating VM's storage across VMware Datacenters");
 +                            }
 +                        }
 +                    }
 +                }
 +
 +            } else {
 +                s_logger.debug("Storage migration failed");
 +            }
 +        } catch (final ConcurrentOperationException e) {
 +            s_logger.debug("Failed to migration: " + e.toString());
 +            throw new CloudRuntimeException("Failed to migration: " + e.toString());
 +        } catch (final InsufficientVirtualNetworkCapacityException e) {
 +            s_logger.debug("Failed to migration: " + e.toString());
 +            throw new CloudRuntimeException("Failed to migration: " + e.toString());
 +        } catch (final InsufficientAddressCapacityException e) {
 +            s_logger.debug("Failed to migration: " + e.toString());
 +            throw new CloudRuntimeException("Failed to migration: " + e.toString());
 +        } catch (final InsufficientCapacityException e) {
 +            s_logger.debug("Failed to migration: " + e.toString());
 +            throw new CloudRuntimeException("Failed to migration: " + e.toString());
 +        } catch (final StorageUnavailableException e) {
 +            s_logger.debug("Failed to migration: " + e.toString());
 +            throw new CloudRuntimeException("Failed to migration: " + e.toString());
 +        } finally {
 +            try {
 +                stateTransitTo(vm, VirtualMachine.Event.AgentReportStopped, null);
 +            } catch (final NoTransitionException e) {
 +                s_logger.debug("Failed to change vm state: " + e.toString());
 +                throw new CloudRuntimeException("Failed to change vm state: " + e.toString());
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void migrate(final String vmUuid, final long srcHostId, final DeployDestination dest)
 +            throws ResourceUnavailableException, ConcurrentOperationException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateMigrate(vmUuid, srcHostId, dest);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = migrateVmThroughJobQueue(vmUuid, srcHostId, dest);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobResult;
 +                } else if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                }
 +
 +            }
 +        }
 +    }
 +
 +    private void orchestrateMigrate(final String vmUuid, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        if (vm == null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Unable to find the vm " + vmUuid);
 +            }
 +            throw new CloudRuntimeException("Unable to find a virtual machine with id " + vmUuid);
 +        }
 +        migrate(vm, srcHostId, dest);
 +    }
 +
 +    protected void migrate(final VMInstanceVO vm, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException {
 +        s_logger.info("Migrating " + vm + " to " + dest);
 +
 +        final long dstHostId = dest.getHost().getId();
 +        final Host fromHost = _hostDao.findById(srcHostId);
 +        if (fromHost == null) {
 +            s_logger.info("Unable to find the host to migrate from: " + srcHostId);
 +            throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId);
 +        }
 +
 +        if (fromHost.getClusterId().longValue() != dest.getCluster().getId()) {
 +            final List<VolumeVO> volumes = _volsDao.findCreatedByInstance(vm.getId());
 +            for (final VolumeVO volume : volumes) {
 +                if (!_storagePoolDao.findById(volume.getPoolId()).getScope().equals(ScopeType.ZONE)) {
 +                    s_logger.info("Source and destination host are not in same cluster and all volumes are not on zone wide primary store, unable to migrate to host: "
 +                            + dest.getHost().getId());
 +                    throw new CloudRuntimeException(
 +                            "Source and destination host are not in same cluster and all volumes are not on zone wide primary store, unable to migrate to host: "
 +                                    + dest.getHost().getId());
 +                }
 +            }
 +        }
 +
 +        final VirtualMachineGuru vmGuru = getVmGuru(vm);
 +
 +        if (vm.getState() != State.Running) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
 +            }
 +            throw new CloudRuntimeException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString());
 +        }
 +
 +        AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE;
 +        if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
 +            alertType = AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE;
 +        } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
 +            alertType = AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY_MIGRATE;
 +        }
 +
 +        final VirtualMachineProfile vmSrc = new VirtualMachineProfileImpl(vm);
 +        for (final NicProfile nic : _networkMgr.getNicProfiles(vm)) {
 +            vmSrc.addNic(nic);
 +        }
 +
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()), null, null);
 +        _networkMgr.prepareNicForMigration(profile, dest);
 +        volumeMgr.prepareForMigration(profile, dest);
 +        profile.setConfigDriveLabel(VmConfigDriveLabel.value());
 +
 +        final VirtualMachineTO to = toVmTO(profile);
 +        final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
 +
 +        ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
 +        work.setStep(Step.Prepare);
 +        work.setResourceType(ItWorkVO.ResourceType.Host);
 +        work.setResourceId(dstHostId);
 +        work = _workDao.persist(work);
 +
 +        Answer pfma = null;
 +        try {
 +            pfma = _agentMgr.send(dstHostId, pfmc);
 +            if (pfma == null || !pfma.getResult()) {
 +                final String details = pfma != null ? pfma.getDetails() : "null answer returned";
 +                final String msg = "Unable to prepare for migration due to " + details;
 +                pfma = null;
 +                throw new AgentUnavailableException(msg, dstHostId);
 +            }
 +        } catch (final OperationTimedoutException e1) {
 +            throw new AgentUnavailableException("Operation timed out", dstHostId);
 +        } finally {
 +            if (pfma == null) {
 +                _networkMgr.rollbackNicForMigration(vmSrc, profile);
 +                work.setStep(Step.Done);
 +                _workDao.update(work.getId(), work);
 +            }
 +        }
 +
 +        vm.setLastHostId(srcHostId);
 +        try {
 +            if (vm == null || vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) {
 +                _networkMgr.rollbackNicForMigration(vmSrc, profile);
 +                s_logger.info("Migration cancelled because state has changed: " + vm);
 +                throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
 +            }
 +        } catch (final NoTransitionException e1) {
 +            _networkMgr.rollbackNicForMigration(vmSrc, profile);
 +            s_logger.info("Migration cancelled because " + e1.getMessage());
 +            throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage());
 +        }
 +
 +        boolean migrated = false;
 +        try {
 +            final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
 +            final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows, to, getExecuteInSequence(vm.getHypervisorType()));
 +
 +            String autoConvergence = _configDao.getValue(Config.KvmAutoConvergence.toString());
 +            boolean kvmAutoConvergence = Boolean.parseBoolean(autoConvergence);
 +
 +            mc.setAutoConvergence(kvmAutoConvergence);
 +
 +            mc.setHostGuid(dest.getHost().getGuid());
 +
 +            try {
 +                final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
 +                if (ma == null || !ma.getResult()) {
 +                    final String details = ma != null ? ma.getDetails() : "null answer returned";
 +                    throw new CloudRuntimeException(details);
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                if (e.isActive()) {
 +                    s_logger.warn("Active migration command so scheduling a restart for " + vm);
 +                    _haMgr.scheduleRestart(vm, true);
 +                }
 +                throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId);
 +            }
 +
 +            try {
 +                if (!changeState(vm, VirtualMachine.Event.OperationSucceeded, dstHostId, work, Step.Started)) {
 +                    throw new ConcurrentOperationException("Unable to change the state for " + vm);
 +                }
 +            } catch (final NoTransitionException e1) {
 +                throw new ConcurrentOperationException("Unable to change state due to " + e1.getMessage());
 +            }
 +
 +            try {
 +                if (!checkVmOnHost(vm, dstHostId)) {
 +                    s_logger.error("Unable to complete migration for " + vm);
 +                    try {
 +                        _agentMgr.send(srcHostId, new Commands(cleanup(vm)), null);
 +                    } catch (final AgentUnavailableException e) {
 +                        s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId);
 +                    }
 +                    cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true);
 +                    throw new CloudRuntimeException("Unable to complete migration for " + vm);
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                s_logger.debug("Error while checking the vm " + vm + " on host " + dstHostId, e);
 +            }
 +
 +            migrated = true;
 +        } finally {
 +            if (!migrated) {
 +                s_logger.info("Migration was unsuccessful.  Cleaning up: " + vm);
 +                _networkMgr.rollbackNicForMigration(vmSrc, profile);
 +
 +                _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(),
 +                        "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone " + dest.getDataCenter().getName() + " and pod " +
 +                                dest.getPod().getName(), "Migrate Command failed.  Please check logs.");
 +                try {
 +                    _agentMgr.send(dstHostId, new Commands(cleanup(vm)), null);
 +                } catch (final AgentUnavailableException ae) {
 +                    s_logger.info("Looks like the destination Host is unavailable for cleanup");
 +                }
 +
 +                try {
 +                    stateTransitTo(vm, Event.OperationFailed, srcHostId);
 +                } catch (final NoTransitionException e) {
 +                    s_logger.warn(e.getMessage());
 +                }
 +            } else {
 +                _networkMgr.commitNicForMigration(vmSrc, profile);
 +            }
 +
 +            work.setStep(Step.Done);
 +            _workDao.update(work.getId(), work);
 +        }
 +    }
 +
 +    /**
 +     * Create the mapping of volumes and storage pools. If the user did not enter a mapping on her/his own, we create one using {@link #getDefaultMappingOfVolumesAndStoragePoolForMigration(VirtualMachineProfile, Host)}.
 +     * If the user provided a mapping, we use whatever the user has provided (check the method {@link #createMappingVolumeAndStoragePoolEnteredByUser(VirtualMachineProfile, Host, Map)}).
 +     */
 +    private Map<Volume, StoragePool> getPoolListForVolumesForMigration(VirtualMachineProfile profile, Host targetHost, Map<Long, Long> volumeToPool) {
 +        if (MapUtils.isEmpty(volumeToPool)) {
 +            return getDefaultMappingOfVolumesAndStoragePoolForMigration(profile, targetHost);
 +        }
 +
 +        return createMappingVolumeAndStoragePoolEnteredByUser(profile, targetHost, volumeToPool);
 +    }
 +
 +    /**
 +     * We create the mapping of volumes and storage pool to migrate the VMs according to the information sent by the user.
 +     */
 +    private Map<Volume, StoragePool> createMappingVolumeAndStoragePoolEnteredByUser(VirtualMachineProfile profile, Host host, Map<Long, Long> volumeToPool) {
 +        Map<Volume, StoragePool> volumeToPoolObjectMap = new HashMap<Volume, StoragePool>();
 +        for(Long volumeId: volumeToPool.keySet()) {
 +            VolumeVO volume = _volsDao.findById(volumeId);
 +
 +            Long poolId = volumeToPool.get(volumeId);
 +            StoragePoolVO targetPool = _storagePoolDao.findById(poolId);
 +            StoragePoolVO currentPool = _storagePoolDao.findById(volume.getPoolId());
 +
 +            if (_poolHostDao.findByPoolHost(targetPool.getId(), host.getId()) == null) {
 +                throw new CloudRuntimeException(String.format("Cannot migrate the volume [%s] to the storage pool [%s] while migrating VM [%s] to target host [%s]. The host does not have access to the storage pool entered.", volume.getUuid(), targetPool.getUuid(), profile.getUuid(), host.getUuid()));
 +            }
 +            if (currentPool.getId() == targetPool.getId()) {
 +                s_logger.info(String.format("The volume [%s] is already allocated in storage pool [%s].", volume.getUuid(), targetPool.getUuid()));
 +            }
 +            volumeToPoolObjectMap.put(volume, targetPool);
 +        }
 +        return volumeToPoolObjectMap;
 +    }
 +
 +    /**
 +     * We create the default mapping of volumes and storage pools for the migration of the VM to the target host.
 +     * If the current storage pool of one of the volumes is using local storage in the host, it then needs to be migrated to a local storage in the target host.
 +     * Otherwise, we do not need to migrate, and the volume can be kept in its current storage pool.
 +     */
 +    private Map<Volume, StoragePool> getDefaultMappingOfVolumesAndStoragePoolForMigration(VirtualMachineProfile profile, Host targetHost) {
 +        Map<Volume, StoragePool> volumeToPoolObjectMap = new HashMap<Volume, StoragePool>();
 +        List<VolumeVO> allVolumes = _volsDao.findUsableVolumesForInstance(profile.getId());
 +        for (VolumeVO volume : allVolumes) {
 +            StoragePoolVO currentPool = _storagePoolDao.findById(volume.getPoolId());
 +            if (ScopeType.HOST.equals(currentPool.getScope())) {
 +                createVolumeToStoragePoolMappingIfNeeded(profile, targetHost, volumeToPoolObjectMap, volume, currentPool);
 +            } else {
 +                volumeToPoolObjectMap.put(volume, currentPool);
 +            }
 +        }
 +        return volumeToPoolObjectMap;
 +    }
 +
 +    /**
 +     * We will add a mapping of volume to storage pool if needed. The conditions to add a mapping are the following:
 +     * <ul>
 +     *  <li> The current storage pool where the volume is allocated can be accessed by the target host
 +     *  <li> If not storage pool is found to allocate the volume we throw an exception.
 +     * </ul>
 +     *
 +     */
 +    private void createVolumeToStoragePoolMappingIfNeeded(VirtualMachineProfile profile, Host targetHost, Map<Volume, StoragePool> volumeToPoolObjectMap, VolumeVO volume, StoragePoolVO currentPool) {
 +        List<StoragePool> poolList = getCandidateStoragePoolsToMigrateLocalVolume(profile, targetHost, volume);
 +
 +        Collections.shuffle(poolList);
 +        boolean canTargetHostAccessVolumeStoragePool = false;
 +        for (StoragePool storagePool : poolList) {
 +            if (storagePool.getId() == currentPool.getId()) {
 +                canTargetHostAccessVolumeStoragePool = true;
 +                break;
 +            }
 +
 +        }
 +        if(!canTargetHostAccessVolumeStoragePool && CollectionUtils.isEmpty(poolList)) {
 +            throw new CloudRuntimeException(String.format("There is not storage pools avaliable at the target host [%s] to migrate volume [%s]", targetHost.getUuid(), volume.getUuid()));
 +        }
 +        if (!canTargetHostAccessVolumeStoragePool) {
 +            volumeToPoolObjectMap.put(volume, _storagePoolDao.findByUuid(poolList.get(0).getUuid()));
 +        }
 +        if (!canTargetHostAccessVolumeStoragePool && !volumeToPoolObjectMap.containsKey(volume)) {
 +            throw new CloudRuntimeException(String.format("Cannot find a storage pool which is available for volume [%s] while migrating virtual machine [%s] to host [%s]", volume.getUuid(),
 +                    profile.getUuid(), targetHost.getUuid()));
 +        }
 +    }
 +
 +    /**
 +     * We use {@link StoragePoolAllocator} objects to find local storage pools connected to the targetHost where we would be able to allocate the given volume.
 +     */
 +    private List<StoragePool> getCandidateStoragePoolsToMigrateLocalVolume(VirtualMachineProfile profile, Host targetHost, VolumeVO volume) {
 +        List<StoragePool> poolList = new ArrayList<>();
 +
 +        DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
 +        DiskProfile diskProfile = new DiskProfile(volume, diskOffering, profile.getHypervisorType());
 +        DataCenterDeployment plan = new DataCenterDeployment(targetHost.getDataCenterId(), targetHost.getPodId(), targetHost.getClusterId(), targetHost.getId(), null, null);
 +        ExcludeList avoid = new ExcludeList();
 +
 +        StoragePoolVO volumeStoragePool = _storagePoolDao.findById(volume.getPoolId());
 +        if (volumeStoragePool.isLocal()) {
 +            diskProfile.setUseLocalStorage(true);
 +        }
 +        for (StoragePoolAllocator allocator : _storagePoolAllocators) {
 +            List<StoragePool> poolListFromAllocator = allocator.allocateToPool(diskProfile, profile, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL);
 +            if (CollectionUtils.isEmpty(poolListFromAllocator)) {
 +                continue;
 +            }
 +            for (StoragePool pool : poolListFromAllocator) {
 +                if (pool.isLocal()) {
 +                    poolList.add(pool);
 +                }
 +            }
 +        }
 +        return poolList;
 +    }
 +
 +    private <T extends VMInstanceVO> void moveVmToMigratingState(final T vm, final Long hostId, final ItWorkVO work) throws ConcurrentOperationException {
 +        // Put the vm in migrating state.
 +        try {
 +            if (!changeState(vm, Event.MigrationRequested, hostId, work, Step.Migrating)) {
 +                s_logger.info("Migration cancelled because state has changed: " + vm);
 +                throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
 +            }
 +        } catch (final NoTransitionException e) {
 +            s_logger.info("Migration cancelled because " + e.getMessage());
 +            throw new ConcurrentOperationException("Migration cancelled because " + e.getMessage());
 +        }
 +    }
 +
 +    private <T extends VMInstanceVO> void moveVmOutofMigratingStateOnSuccess(final T vm, final Long hostId, final ItWorkVO work) throws ConcurrentOperationException {
 +        // Put the vm in running state.
 +        try {
 +            if (!changeState(vm, Event.OperationSucceeded, hostId, work, Step.Started)) {
 +                s_logger.error("Unable to change the state for " + vm);
 +                throw new ConcurrentOperationException("Unable to change the state for " + vm);
 +            }
 +        } catch (final NoTransitionException e) {
 +            s_logger.error("Unable to change state due to " + e.getMessage());
 +            throw new ConcurrentOperationException("Unable to change state due to " + e.getMessage());
 +        }
 +    }
 +
 +    @Override
 +    public void migrateWithStorage(final String vmUuid, final long srcHostId, final long destHostId, final Map<Long, Long> volumeToPool)
 +            throws ResourceUnavailableException, ConcurrentOperationException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateMigrateWithStorage(vmUuid, srcHostId, destHostId, volumeToPool);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +
 +        } else {
 +            final Outcome<VirtualMachine> outcome = migrateVmWithStorageThroughJobQueue(vmUuid, srcHostId, destHostId, volumeToPool);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobException = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobException != null) {
 +                if (jobException instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobException;
 +                } else if (jobException instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobException;
 +                } else if (jobException instanceof RuntimeException) {
 +                    throw (RuntimeException)jobException;
 +                } else if (jobException instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobException);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void orchestrateMigrateWithStorage(final String vmUuid, final long srcHostId, final long destHostId, final Map<Long, Long> volumeToPool) throws ResourceUnavailableException,
 +    ConcurrentOperationException {
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final HostVO srcHost = _hostDao.findById(srcHostId);
 +        final HostVO destHost = _hostDao.findById(destHostId);
 +        final VirtualMachineGuru vmGuru = getVmGuru(vm);
 +
 +        final DataCenterVO dc = _dcDao.findById(destHost.getDataCenterId());
 +        final HostPodVO pod = _podDao.findById(destHost.getPodId());
 +        final Cluster cluster = _clusterDao.findById(destHost.getClusterId());
 +        final DeployDestination destination = new DeployDestination(dc, pod, cluster, destHost);
 +
 +        // Create a map of which volume should go in which storage pool.
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +        final Map<Volume, StoragePool> volumeToPoolMap = getPoolListForVolumesForMigration(profile, destHost, volumeToPool);
 +
 +        // If none of the volumes have to be migrated, fail the call. Administrator needs to make a call for migrating
 +        // a vm and not migrating a vm with storage.
 +        if (volumeToPoolMap == null || volumeToPoolMap.isEmpty()) {
 +            throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost + " to destination host " + destHost +
 +                    " doesn't involve migrating the volumes.");
 +        }
 +
 +        AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE;
 +        if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
 +            alertType = AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE;
 +        } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
 +            alertType = AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY_MIGRATE;
 +        }
 +
 +        _networkMgr.prepareNicForMigration(profile, destination);
 +        volumeMgr.prepareForMigration(profile, destination);
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
 +        final VirtualMachineTO to = hvGuru.implement(profile);
 +
 +        ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
 +        work.setStep(Step.Prepare);
 +        work.setResourceType(ItWorkVO.ResourceType.Host);
 +        work.setResourceId(destHostId);
 +        work = _workDao.persist(work);
 +
 +
 +        // Put the vm in migrating state.
 +        vm.setLastHostId(srcHostId);
 +        vm.setPodIdToDeployIn(destHost.getPodId());
 +        moveVmToMigratingState(vm, destHostId, work);
 +
 +        boolean migrated = false;
 +        try {
 +
 +            // config drive: Detach the config drive at source host
 +            // After migration successful attach the config drive in destination host
 +            // On migration failure VM will be stopped, So configIso will be deleted
 +
 +            Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
 +
 +            List<String[]> vmData = null;
 +            if (defaultNic != null) {
 +                UserVmVO userVm = _userVmDao.findById(vm.getId());
 +                Map<String, String> details = _vmDetailsDao.listDetailsKeyPairs(vm.getId());
-                 vm.setDetails(details);
++                userVm.setDetails(details);
 +
 +                Network network = _networkModel.getNetwork(defaultNic.getNetworkId());
 +                if (_networkModel.isSharedNetworkWithoutServices(network.getId())) {
 +                    final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText();
-                     final String zoneName = _dcDao.findById(vm.getDataCenterId()).getName();
 +                    boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
 +
-                     vmData = _networkModel.generateVmData(userVm.getUserData(), serviceOffering, zoneName, vm.getInstanceName(), vm.getId(),
-                             (String) profile.getParameter(VirtualMachineProfile.Param.VmSshPubKey), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
++                    vmData = _networkModel.generateVmData(userVm.getUserData(), serviceOffering, vm.getDataCenterId(), vm.getInstanceName(), vm.getId(),
++                            vm.getUuid(), defaultNic.getMacAddress(), userVm.getDetail("SSH.PublicKey"), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
 +                    String vmName = vm.getInstanceName();
 +                    String configDriveIsoRootFolder = "/tmp";
 +                    String isoFile = configDriveIsoRootFolder + "/" + vmName + "/configDrive/" + vmName + ".iso";
 +                    profile.setVmData(vmData);
 +                    profile.setConfigDriveLabel(VmConfigDriveLabel.value());
 +                    profile.setConfigDriveIsoRootFolder(configDriveIsoRootFolder);
 +                    profile.setConfigDriveIsoFile(isoFile);
 +
 +                    // At source host detach the config drive iso.
 +                    AttachOrDettachConfigDriveCommand dettachCommand = new AttachOrDettachConfigDriveCommand(vm.getInstanceName(), vmData, VmConfigDriveLabel.value(), false);
 +                    try {
 +                        _agentMgr.send(srcHost.getId(), dettachCommand);
 +                        s_logger.debug("Deleted config drive ISO for  vm " + vm.getInstanceName() + " In host " + srcHost);
 +                    } catch (OperationTimedoutException e) {
 +                        s_logger.debug("TIme out occured while exeuting command AttachOrDettachConfigDrive " + e.getMessage());
 +
 +                    }
 +
 +                }
 +            }
 +
 +            // Migrate the vm and its volume.
 +            volumeMgr.migrateVolumes(vm, to, srcHost, destHost, volumeToPoolMap);
 +
 +            // Put the vm back to running state.
 +            moveVmOutofMigratingStateOnSuccess(vm, destHost.getId(), work);
 +
 +            try {
 +                if (!checkVmOnHost(vm, destHostId)) {
 +                    s_logger.error("Vm not found on destination host. Unable to complete migration for " + vm);
 +                    try {
 +                        _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null);
 +                    } catch (final AgentUnavailableException e) {
 +                        s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId);
 +                    }
 +                    cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true);
 +                    throw new CloudRuntimeException("VM not found on desintation host. Unable to complete migration for " + vm);
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                s_logger.warn("Error while checking the vm " + vm + " is on host " + destHost, e);
 +            }
 +
 +            migrated = true;
 +        } finally {
 +            if (!migrated) {
 +                s_logger.info("Migration was unsuccessful.  Cleaning up: " + vm);
 +                _alertMgr.sendAlert(alertType, srcHost.getDataCenterId(), srcHost.getPodId(),
 +                        "Unable to migrate vm " + vm.getInstanceName() + " from host " + srcHost.getName() + " in zone " + dc.getName() + " and pod " + dc.getName(),
 +                        "Migrate Command failed.  Please check logs.");
 +                try {
 +                    _agentMgr.send(destHostId, new Commands(cleanup(vm.getInstanceName())), null);
 +                    vm.setPodIdToDeployIn(srcHost.getPodId());
 +                    stateTransitTo(vm, Event.OperationFailed, srcHostId);
 +                } catch (final AgentUnavailableException e) {
 +                    s_logger.warn("Looks like the destination Host is unavailable for cleanup.", e);
 +                } catch (final NoTransitionException e) {
 +                    s_logger.error("Error while transitioning vm from migrating to running state.", e);
 +                }
 +            }
 +
 +            work.setStep(Step.Done);
 +            _workDao.update(work.getId(), work);
 +        }
 +    }
 +
 +    @Override
 +    public VirtualMachineTO toVmTO(final VirtualMachineProfile profile) {
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(profile.getVirtualMachine().getHypervisorType());
 +        final VirtualMachineTO to = hvGuru.implement(profile);
 +        return to;
 +    }
 +
 +    protected void cancelWorkItems(final long nodeId) {
 +        final GlobalLock scanLock = GlobalLock.getInternLock("vmmgr.cancel.workitem");
 +
 +        try {
 +            if (scanLock.lock(3)) {
 +                try {
 +                    final List<ItWorkVO> works = _workDao.listWorkInProgressFor(nodeId);
 +                    for (final ItWorkVO work : works) {
 +                        s_logger.info("Handling unfinished work item: " + work);
 +                        try {
 +                            final VMInstanceVO vm = _vmDao.findById(work.getInstanceId());
 +                            if (vm != null) {
 +                                if (work.getType() == State.Starting) {
 +                                    _haMgr.scheduleRestart(vm, true);
 +                                    work.setManagementServerId(_nodeId);
 +                                    work.setStep(Step.Done);
 +                                    _workDao.update(work.getId(), work);
 +                                } else if (work.getType() == State.Stopping) {
 +                                    _haMgr.scheduleStop(vm, vm.getHostId(), WorkType.CheckStop);
 +                                    work.setManagementServerId(_nodeId);
 +                                    work.setStep(Step.Done);
 +                                    _workDao.update(work.getId(), work);
 +                                } else if (work.getType() == State.Migrating) {
 +                                    _haMgr.scheduleMigration(vm);
 +                                    work.setStep(Step.Done);
 +                                    _workDao.update(work.getId(), work);
 +                                }
 +                            }
 +                        } catch (final Exception e) {
 +                            s_logger.error("Error while handling " + work, e);
 +                        }
 +                    }
 +                } finally {
 +                    scanLock.unlock();
 +                }
 +            }
 +        } finally {
 +            scanLock.releaseRef();
 +        }
 +    }
 +
 +    @Override
 +    public void migrateAway(final String vmUuid, final long srcHostId) throws InsufficientServerCapacityException {
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                try {
 +                    orchestrateMigrateAway(vmUuid, srcHostId, null);
 +                } catch (final InsufficientServerCapacityException e) {
 +                    s_logger.warn("Failed to deploy vm " + vmUuid + " with original planner, sending HAPlanner");
 +                    orchestrateMigrateAway(vmUuid, srcHostId, _haMgr.getHAPlanner());
 +                }
 +            } finally {
 +                _workJobDao.expunge(placeHolder.getId());
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = migrateVmAwayThroughJobQueue(vmUuid, srcHostId);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobException = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobException != null) {
 +                if (jobException instanceof InsufficientServerCapacityException) {
 +                    throw (InsufficientServerCapacityException)jobException;
 +                } else if (jobException instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobException;
 +                } else if (jobException instanceof RuntimeException) {
 +                    throw (RuntimeException)jobException;
 +                } else if (jobException instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobException);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, final DeploymentPlanner planner) throws InsufficientServerCapacityException {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        if (vm == null) {
 +            s_logger.debug("Unable to find a VM for " + vmUuid);
 +            throw new CloudRuntimeException("Unable to find " + vmUuid);
 +        }
 +
 +        ServiceOfferingVO offeringVO = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offeringVO, null, null);
 +
 +        final Long hostId = vm.getHostId();
 +        if (hostId == null) {
 +            s_logger.debug("Unable to migrate because the VM doesn't have a host id: " + vm);
 +            throw new CloudRuntimeException("Unable to migrate " + vmUuid);
 +        }
 +
 +        final Host host = _hostDao.findById(hostId);
 +        Long poolId = null;
 +        final List<VolumeVO> vols = _volsDao.findReadyRootVolumesByInstance(vm.getId());
 +        for (final VolumeVO rootVolumeOfVm : vols) {
 +            final StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId());
 +            if (rootDiskPool != null) {
 +                poolId = rootDiskPool.getId();
 +            }
 +        }
 +
 +        final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, poolId, null);
 +        final ExcludeList excludes = new ExcludeList();
 +        excludes.addHost(hostId);
 +
 +        DeployDestination dest = null;
 +        while (true) {
 +
 +            try {
 +                dest = _dpMgr.planDeployment(profile, plan, excludes, planner);
 +            } catch (final AffinityConflictException e2) {
 +                s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2);
 +                throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict");
 +            }
 +
 +            if (dest != null) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Found destination " + dest + " for migrating to.");
 +                }
 +            } else {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Unable to find destination for migrating the vm " + profile);
 +                }
 +                throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", host.getClusterId());
 +            }
 +
 +            excludes.addHost(dest.getHost().getId());
 +            try {
 +                migrate(vm, srcHostId, dest);
 +                return;
 +            } catch (final ResourceUnavailableException e) {
 +                s_logger.debug("Unable to migrate to unavailable " + dest);
 +            } catch (final ConcurrentOperationException e) {
 +                s_logger.debug("Unable to migrate VM due to: " + e.getMessage());
 +            }
 +
 +            try {
 +                advanceStop(vmUuid, true);
 +                throw new CloudRuntimeException("Unable to migrate " + vm);
 +            } catch (final ResourceUnavailableException e) {
 +                s_logger.debug("Unable to stop VM due to " + e.getMessage());
 +                throw new CloudRuntimeException("Unable to migrate " + vm);
 +            } catch (final ConcurrentOperationException e) {
 +                s_logger.debug("Unable to stop VM due to " + e.getMessage());
 +                throw new CloudRuntimeException("Unable to migrate " + vm);
 +            } catch (final OperationTimedoutException e) {
 +                s_logger.debug("Unable to stop VM due to " + e.getMessage());
 +                throw new CloudRuntimeException("Unable to migrate " + vm);
 +            }
 +        }
 +    }
 +
 +    protected class CleanupTask extends ManagedContextRunnable {
 +        @Override
 +        protected void runInContext() {
 +            s_logger.trace("VM Operation Thread Running");
 +            try {
 +                _workDao.cleanup(VmOpCleanupWait.value());
 +                final Date cutDate = new Date(DateUtil.currentGMTTime().getTime() - VmOpCleanupInterval.value() * 1000);
 +                _workJobDao.expungeCompletedWorkJobs(cutDate);
 +            } catch (final Exception e) {
 +                s_logger.error("VM Operations failed due to ", e);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering) {
 +        boolean isMachineUpgradable = true;
 +        for (final HostAllocator allocator : hostAllocators) {
 +            isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering);
 +            if (isMachineUpgradable) {
 +                continue;
 +            } else {
 +                break;
 +            }
 +        }
 +
 +        return isMachineUpgradable;
 +    }
 +
 +    @Override
 +    public void reboot(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params) throws InsufficientCapacityException, ResourceUnavailableException {
 +        try {
 +            advanceReboot(vmUuid, params);
 +        } catch (final ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Unable to reboot a VM due to concurrent operation", e);
 +        }
 +    }
 +
 +    @Override
 +    public void advanceReboot(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params)
 +            throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if ( jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateReboot(vmUuid, params);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = rebootVmThroughJobQueue(vmUuid, params);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobResult;
 +                } else if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof InsufficientCapacityException) {
 +                    throw (InsufficientCapacityException)jobResult;
 +                } else if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void orchestrateReboot(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params) throws InsufficientCapacityException, ConcurrentOperationException,
 +    ResourceUnavailableException {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        // if there are active vm snapshots task, state change is not allowed
 +        if(_vmSnapshotMgr.hasActiveVMSnapshotTasks(vm.getId())){
 +            s_logger.error("Unable to reboot VM " + vm + " due to: " + vm.getInstanceName() + " has active VM snapshots tasks");
 +            throw new CloudRuntimeException("Unable to reboot VM " + vm + " due to: " + vm.getInstanceName() + " has active VM snapshots tasks");
 +        }
 +        final DataCenter dc = _entityMgr.findById(DataCenter.class, vm.getDataCenterId());
 +        final Host host = _hostDao.findById(vm.getHostId());
 +        if (host == null) {
 +            // Should findById throw an Exception is the host is not found?
 +            throw new CloudRuntimeException("Unable to retrieve host with id " + vm.getHostId());
 +        }
 +        final Cluster cluster = _entityMgr.findById(Cluster.class, host.getClusterId());
 +        final Pod pod = _entityMgr.findById(Pod.class, host.getPodId());
 +        final DeployDestination dest = new DeployDestination(dc, pod, cluster, host);
 +
 +        try {
 +
 +            final Commands cmds = new Commands(Command.OnError.Stop);
 +            cmds.addCommand(new RebootCommand(vm.getInstanceName(), getExecuteInSequence(vm.getHypervisorType())));
 +            _agentMgr.send(host.getId(), cmds);
 +
 +            final Answer rebootAnswer = cmds.getAnswer(RebootAnswer.class);
 +            if (rebootAnswer != null && rebootAnswer.getResult()) {
 +                return;
 +            }
 +            s_logger.info("Unable to reboot VM " + vm + " on " + dest.getHost() + " due to " + (rebootAnswer == null ? " no reboot answer" : rebootAnswer.getDetails()));
 +        } catch (final OperationTimedoutException e) {
 +            s_logger.warn("Unable to send the reboot command to host " + dest.getHost() + " for the vm " + vm + " due to operation timeout", e);
 +            throw new CloudRuntimeException("Failed to reboot the vm on host " + dest.getHost());
 +        }
 +    }
 +
 +    public Command cleanup(final VirtualMachine vm) {
 +        StopCommand cmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false);
 +        cmd.setControlIp(getControlNicIpForVM(vm));
 +        return cmd;
 +    }
 +
 +    private String getControlNicIpForVM(VirtualMachine vm) {
 +        if (vm.getType() == VirtualMachine.Type.ConsoleProxy || vm.getType() == VirtualMachine.Type.SecondaryStorageVm) {
 +            NicVO nic = _nicsDao.getControlNicForVM(vm.getId());
 +            return nic.getIPv4Address();
 +        } else if (vm.getType() == VirtualMachine.Type.DomainRouter) {
 +            return vm.getPrivateIpAddress();
 +        } else {
 +            return null;
 +        }
 +    }
 +    public Command cleanup(final String vmName) {
 +        VirtualMachine vm = _vmDao.findVMByInstanceName(vmName);
 +
 +        StopCommand cmd = new StopCommand(vmName, getExecuteInSequence(null), false);
 +        cmd.setControlIp(getControlNicIpForVM(vm));
 +        return cmd;
 +    }
 +
 +
 +    // this is XenServer specific
 +    public void syncVMMetaData(final Map<String, String> vmMetadatum) {
 +        if (vmMetadatum == null || vmMetadatum.isEmpty()) {
 +            return;
 +        }
 +        List<Pair<Pair<String, VirtualMachine.Type>, Pair<Long, String>>> vmDetails = _userVmDao.getVmsDetailByNames(vmMetadatum.keySet(), "platform");
 +        for (final Map.Entry<String, String> entry : vmMetadatum.entrySet()) {
 +            final String name = entry.getKey();
 +            final String platform = entry.getValue();
 +            if (platform == null || platform.isEmpty()) {
 +                continue;
 +            }
 +
 +            boolean found = false;
 +            for(Pair<Pair<String, VirtualMachine.Type>, Pair<Long, String>> vmDetail : vmDetails ) {
 +                Pair<String, VirtualMachine.Type> vmNameTypePair = vmDetail.first();
 +                if(vmNameTypePair.first().equals(name)) {
 +                    found = true;
 +                    if(vmNameTypePair.second() == VirtualMachine.Type.User) {
 +                        Pair<Long, String> detailPair = vmDetail.second();
 +                        String platformDetail = detailPair.second();
 +
 +                        if (platformDetail != null && platformDetail.equals(platform)) {
 +                            break;
 +                        }
 +                        updateVmMetaData(detailPair.first(), platform);
 +                    }
 +                    break;
 +                }
 +            }
 +
 +            if(!found) {
 +                VMInstanceVO vm = _vmDao.findVMByInstanceName(name);
 +                if(vm != null && vm.getType() == VirtualMachine.Type.User) {
 +                    updateVmMetaData(vm.getId(), platform);
 +                }
 +            }
 +        }
 +    }
 +
 +    // this is XenServer specific
 +    private void updateVmMetaData(Long vmId, String platform) {
 +        UserVmVO userVm = _userVmDao.findById(vmId);
 +        _userVmDao.loadDetails(userVm);
 +        if ( userVm.details.containsKey("timeoffset")) {
 +            userVm.details.remove("timeoffset");
 +        }
 +        userVm.setDetail("platform",  platform);
 +        String pvdriver = "xenserver56";
 +        if ( platform.contains("device_id")) {
 +            pvdriver = "xenserver61";
 +        }
 +        if (!userVm.details.containsKey("hypervisortoolsversion") || !userVm.details.get("hypervisortoolsversion").equals(pvdriver)) {
 +            userVm.setDetail("hypervisortoolsversion", pvdriver);
 +        }
 +        _userVmDao.saveDetails(userVm);
 +    }
 +
 +    @Override
 +    public boolean isRecurring() {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean processAnswers(final long agentId, final long seq, final Answer[] answers) {
 +        for (final Answer answer : answers) {
 +            if ( answer instanceof ClusterVMMetaDataSyncAnswer) {
 +                final ClusterVMMetaDataSyncAnswer cvms = (ClusterVMMetaDataSyncAnswer)answer;
 +                if (!cvms.isExecuted()) {
 +                    syncVMMetaData(cvms.getVMMetaDatum());
 +                    cvms.setExecuted();
 +                }
 +            }
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean processTimeout(final long agentId, final long seq) {
 +        return true;
 +    }
 +
 +    @Override
 +    public int getTimeout() {
 +        return -1;
 +    }
 +
 +    @Override
 +    public boolean processCommands(final long agentId, final long seq, final Command[] cmds) {
 +        boolean processed = false;
 +        for (final Command cmd : cmds) {
 +            if (cmd instanceof PingRoutingCommand) {
 +                final PingRoutingCommand ping = (PingRoutingCommand)cmd;
 +                if (ping.getHostVmStateReport() != null) {
 +                    _syncMgr.processHostVmStatePingReport(agentId, ping.getHostVmStateReport());
 +                }
 +
 +                // take the chance to scan VMs that are stuck in transitional states
 +                // and are missing from the report
 +                scanStalledVMInTransitionStateOnUpHost(agentId);
 +                processed = true;
 +            }
 +        }
 +        return processed;
 +    }
 +
 +    @Override
 +    public AgentControlAnswer processControlCommand(final long agentId, final AgentControlCommand cmd) {
 +        return null;
 +    }
 +
 +    @Override
 +    public boolean processDisconnect(final long agentId, final Status state) {
 +        return true;
 +    }
 +
 +    @Override
 +    public void processHostAboutToBeRemoved(long hostId) {
 +    }
 +
 +    @Override
 +    public void processHostRemoved(long hostId, long clusterId) {
 +    }
 +
 +    @Override
 +    public void processHostAdded(long hostId) {
 +    }
 +
 +    @Override
 +    public void processConnect(final Host agent, final StartupCommand cmd, final boolean forRebalance) throws ConnectionException {
 +        if (!(cmd instanceof StartupRoutingCommand)) {
 +            return;
 +        }
 +
 +        if(s_logger.isDebugEnabled()) {
 +            s_logger.debug("Received startup command from hypervisor host. host id: " + agent.getId());
 +        }
 +
 +        _syncMgr.resetHostSyncState(agent.getId());
 +
 +        if (forRebalance) {
 +            s_logger.debug("Not processing listener " + this + " as connect happens on rebalance process");
 +            return;
 +        }
 +        final Long clusterId = agent.getClusterId();
 +        final long agentId = agent.getId();
 +
 +        if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen
 +            // initiate the cron job
 +            final ClusterVMMetaDataSyncCommand syncVMMetaDataCmd = new ClusterVMMetaDataSyncCommand(ClusterVMMetaDataSyncInterval.value(), clusterId);
 +            try {
 +                final long seq_no = _agentMgr.send(agentId, new Commands(syncVMMetaDataCmd), this);
 +                s_logger.debug("Cluster VM metadata sync started with jobid " + seq_no);
 +            } catch (final AgentUnavailableException e) {
 +                s_logger.fatal("The Cluster VM metadata sync process failed for cluster id " + clusterId + " with ", e);
 +            }
 +        }
 +    }
 +
 +    protected class TransitionTask extends ManagedContextRunnable {
 +        @Override
 +        protected void runInContext() {
 +            final GlobalLock lock = GlobalLock.getInternLock("TransitionChecking");
 +            if (lock == null) {
 +                s_logger.debug("Couldn't get the global lock");
 +                return;
 +            }
 +
 +            if (!lock.lock(30)) {
 +                s_logger.debug("Couldn't lock the db");
 +                return;
 +            }
 +            try {
 +                scanStalledVMInTransitionStateOnDisconnectedHosts();
 +
 +                final List<VMInstanceVO> instances = _vmDao.findVMInTransition(new Date(DateUtil.currentGMTTime().getTime() - AgentManager.Wait.value() * 1000), State.Starting, State.Stopping);
 +                for (final VMInstanceVO instance : instances) {
 +                    final State state = instance.getState();
 +                    if (state == State.Stopping) {
 +                        _haMgr.scheduleStop(instance, instance.getHostId(), WorkType.CheckStop);
 +                    } else if (state == State.Starting) {
 +                        _haMgr.scheduleRestart(instance, true);
 +                    }
 +                }
 +            } catch (final Exception e) {
 +                s_logger.warn("Caught the following exception on transition checking", e);
 +            } finally {
 +                lock.unlock();
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public VMInstanceVO findById(final long vmId) {
 +        return _vmDao.findById(vmId);
 +    }
 +
 +    @Override
 +    public void checkIfCanUpgrade(final VirtualMachine vmInstance, final ServiceOffering newServiceOffering) {
 +        if (newServiceOffering == null) {
 +            throw new InvalidParameterValueException("Invalid parameter, newServiceOffering can't be null");
 +        }
 +
 +        // Check that the VM is stopped / running
 +        if (!(vmInstance.getState().equals(State.Stopped) || vmInstance.getState().equals(State.Running))) {
 +            s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState());
 +            throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + " in state " + vmInstance.getState() +
 +                    "; make sure the virtual machine is stopped/running");
 +        }
 +
 +        // Check if the service offering being upgraded to is what the VM is already running with
 +        if (!newServiceOffering.isDynamic() && vmInstance.getServiceOfferingId() == newServiceOffering.getId()) {
 +            if (s_logger.isInfoEnabled()) {
 +                s_logger.info("Not upgrading vm " + vmInstance.toString() + " since it already has the requested " + "service offering (" + newServiceOffering.getName() +
 +                        ")");
 +            }
 +
 +            throw new InvalidParameterValueException("Not upgrading vm " + vmInstance.toString() + " since it already " + "has the requested service offering (" +
 +                    newServiceOffering.getName() + ")");
 +        }
 +
 +        final ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId());
 +
 +        // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering
 +        // NOTE: With the new network refactoring in 2.2, we shouldn't need the check for same guest IP type anymore.
 +        /*
 +         * if (!currentServiceOffering.getGuestIpType().equals(newServiceOffering.getGuestIpType())) { String errorMsg =
 +         * "The service offering being upgraded to has a guest IP type: " + newServiceOffering.getGuestIpType(); errorMsg +=
 +         * ". Please select a service offering with the same guest IP type as the VM's current service offering (" +
 +         * currentServiceOffering.getGuestIpType() + ")."; throw new InvalidParameterValueException(errorMsg); }
 +         */
 +
 +        // Check that the service offering being upgraded to has the same storage pool preference as the VM's current service
 +        // offering
 +        if (currentServiceOffering.getUseLocalStorage() != newServiceOffering.getUseLocalStorage()) {
 +            throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() +
 +                    ", cannot switch between local storage and shared storage service offerings.  Current offering " + "useLocalStorage=" +
 +                    currentServiceOffering.getUseLocalStorage() + ", target offering useLocalStorage=" + newServiceOffering.getUseLocalStorage());
 +        }
 +
 +        // if vm is a system vm, check if it is a system service offering, if yes return with error as it cannot be used for user vms
 +        if (currentServiceOffering.getSystemUse() != newServiceOffering.getSystemUse()) {
 +            throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering");
 +        }
 +
 +        // Check that there are enough resources to upgrade the service offering
 +        if (!isVirtualMachineUpgradable(vmInstance, newServiceOffering)) {
 +            throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available " + "for an offering of " +
 +                    newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory");
 +        }
 +
 +        // Check that the service offering being upgraded to has all the tags of the current service offering.
 +        final List<String> currentTags = StringUtils.csvTagsToList(currentServiceOffering.getTags());
 +        final List<String> newTags = StringUtils.csvTagsToList(newServiceOffering.getTags());
 +        if (!newTags.containsAll(currentTags)) {
 +            throw new InvalidParameterValueException("Unable to upgrade virtual machine; the current service offering " + " should have tags as subset of " +
 +                    "the new service offering tags. Current service offering tags: " + currentTags + "; " + "new service " + "offering tags: " + newTags);
 +        }
 +    }
 +
 +    @Override
 +    public boolean upgradeVmDb(final long vmId, final long serviceOfferingId) {
 +        final VMInstanceVO vmForUpdate = _vmDao.createForUpdate();
 +        vmForUpdate.setServiceOfferingId(serviceOfferingId);
 +        final ServiceOffering newSvcOff = _entityMgr.findById(ServiceOffering.class, serviceOfferingId);
 +        vmForUpdate.setHaEnabled(newSvcOff.getOfferHA());
 +        vmForUpdate.setLimitCpuUse(newSvcOff.getLimitCpuUse());
 +        vmForUpdate.setServiceOfferingId(newSvcOff.getId());
 +        return _vmDao.update(vmId, vmForUpdate);
 +    }
 +
 +    @Override
 +    public NicProfile addVmToNetwork(final VirtualMachine vm, final Network network, final NicProfile requested)
 +            throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                return orchestrateAddVmToNetwork(vm, network, requested);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = addVmToNetworkThroughJobQueue(vm, network, requested);
 +
 +            try {
 +                outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution exception", e);
 +            }
 +
 +            final Object jobException = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobException != null) {
 +                if (jobException instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobException;
 +                } else if (jobException instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobException;
 +                } else if (jobException instanceof InsufficientCapacityException) {
 +                    throw (InsufficientCapacityException)jobException;
 +                } else if (jobException instanceof RuntimeException) {
 +                    throw (RuntimeException)jobException;
 +                } else if (jobException instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobException);
 +                } else if (jobException instanceof NicProfile) {
 +                    return (NicProfile)jobException;
 +                }
 +            }
 +
 +            throw new RuntimeException("Unexpected job execution result");
 +        }
 +    }
 +
 +    private NicProfile orchestrateAddVmToNetwork(final VirtualMachine vm, final Network network, final NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException,
 +    InsufficientCapacityException {
 +        final CallContext cctx = CallContext.current();
 +
 +        s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested);
 +        final VMInstanceVO vmVO = _vmDao.findById(vm.getId());
 +        final ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount());
 +
 +        final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null);
 +
 +        final DataCenter dc = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
 +        final Host host = _hostDao.findById(vm.getHostId());
 +        final DeployDestination dest = new DeployDestination(dc, null, null, host);
 +
 +        //check vm state
 +        if (vm.getState() == State.Running) {
 +            //1) allocate and prepare nic
 +            final NicProfile nic = _networkMgr.createNicForVm(network, requested, context, vmProfile, true);
 +
 +            //2) Convert vmProfile to vmTO
 +            final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType());
 +            final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
 +
 +            //3) Convert nicProfile to NicTO
 +            final NicTO nicTO = toNicTO(nic, vmProfile.getVirtualMachine().getHypervisorType());
 +
 +            //4) plug the nic to the vm
 +            s_logger.debug("Plugging nic for vm " + vm + " in network " + network);
 +
 +            boolean result = false;
 +            try {
 +                result = plugNic(network, nicTO, vmTO, context, dest);
 +                if (result) {
 +                    s_logger.debug("Nic is plugged successfully for vm " + vm + " in network " + network + ". Vm  is a part of network now");
 +                    final long isDefault = nic.isDefaultNic() ? 1 : 0;
 +                    // insert nic's Id into DB as resource_name
 +                    if(VirtualMachine.Type.User.equals(vmVO.getType())) {
 +                        //Log usage event for user Vms only
 +                        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmVO.getAccountId(), vmVO.getDataCenterId(), vmVO.getId(),
 +                                Long.toString(nic.getId()), network.getNetworkOfferingId(), null, isDefault, VirtualMachine.class.getName(), vmVO.getUuid(), vm.isDisplay());
 +                    }
 +                    return nic;
 +                } else {
 +                    s_logger.warn("Failed to plug nic to the vm " + vm + " in network " + network);
 +                    return null;
 +                }
 +            } finally {
 +                if (!result) {
 +                    s_logger.debug("Removing nic " + nic + " from vm " + vmProfile.getVirtualMachine() + " as nic plug failed on the backend");
 +                    _networkMgr.removeNic(vmProfile, _nicsDao.findById(nic.getId()));
 +                }
 +            }
 +        } else if (vm.getState() == State.Stopped) {
 +            //1) allocate nic
 +            return _networkMgr.createNicForVm(network, requested, context, vmProfile, false);
 +        } else {
 +            s_logger.warn("Unable to add vm " + vm + " to network  " + network);
 +            throw new ResourceUnavailableException("Unable to add vm " + vm + " to network, is not in the right state", DataCenter.class, vm.getDataCenterId());
 +        }
 +    }
 +
 +    @Override
 +    public NicTO toNicTO(final NicProfile nic, final HypervisorType hypervisorType) {
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(hypervisorType);
 +
 +        final NicTO nicTO = hvGuru.toNicTO(nic);
 +        return nicTO;
 +    }
 +
 +    @Override
 +    public boolean removeNicFromVm(final VirtualMachine vm, final Nic nic)
 +            throws ConcurrentOperationException, ResourceUnavailableException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                return orchestrateRemoveNicFromVm(vm, nic);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +
 +        } else {
 +            final Outcome<VirtualMachine> outcome = removeNicFromVmThroughJobQueue(vm, nic);
 +
 +            try {
 +                outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobResult;
 +                } else if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                } else if (jobResult instanceof Boolean) {
 +                    return (Boolean)jobResult;
 +                }
 +            }
 +
 +            throw new RuntimeException("Job failed with un-handled exception");
 +        }
 +    }
 +
 +    private boolean orchestrateRemoveNicFromVm(final VirtualMachine vm, final Nic nic) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final CallContext cctx = CallContext.current();
 +        final VMInstanceVO vmVO = _vmDao.findById(vm.getId());
 +        final NetworkVO network = _networkDao.findById(nic.getNetworkId());
 +        final ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount());
 +
 +        final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null);
 +
 +        final DataCenter dc = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
 +        final Host host = _hostDao.findById(vm.getHostId());
 +        final DeployDestination dest = new DeployDestination(dc, null, null, host);
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType());
 +        final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
 +
 +        final NicProfile nicProfile =
 +                new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), _networkModel.getNetworkRate(network.getId(), vm.getId()),
 +                        _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network));
 +
 +        //1) Unplug the nic
 +        if (vm.getState() == State.Running) {
 +            final NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType());
 +            s_logger.debug("Un-plugging nic " + nic + " for vm " + vm + " from network " + network);
 +            final boolean result = unplugNic(network, nicTO, vmTO, context, dest);
 +            if (result) {
 +                s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network);
 +                final long isDefault = nic.isDefaultNic() ? 1 : 0;
 +                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(),
 +                        Long.toString(nic.getId()), network.getNetworkOfferingId(), null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay());
 +            } else {
 +                s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network);
 +                return false;
 +            }
 +        } else if (vm.getState() != State.Stopped) {
 +            s_logger.warn("Unable to remove vm " + vm + " from network  " + network);
 +            throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", DataCenter.class, vm.getDataCenterId());
 +        }
 +
 +        //2) Release the nic
 +        _networkMgr.releaseNic(vmProfile, nic);
 +        s_logger.debug("Successfully released nic " + nic + "for vm " + vm);
 +
 +        //3) Remove the nic
 +        _networkMgr.removeNic(vmProfile, nic);
 +        _nicsDao.expunge(nic.getId());
 +        return true;
 +    }
 +
 +    @Override
 +    @DB
 +    public boolean removeVmFromNetwork(final VirtualMachine vm, final Network network, final URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException {
 +        // TODO will serialize on the VM object later to resolve operation conflicts
 +        return orchestrateRemoveVmFromNetwork(vm, network, broadcastUri);
 +    }
 +
 +    @DB
 +    private boolean orchestrateRemoveVmFromNetwork(final VirtualMachine vm, final Network network, final URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final CallContext cctx = CallContext.current();
 +        final VMInstanceVO vmVO = _vmDao.findById(vm.getId());
 +        final ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount());
 +
 +        final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null);
 +
 +        final DataCenter dc = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
 +        final Host host = _hostDao.findById(vm.getHostId());
 +        final DeployDestination dest = new DeployDestination(dc, null, null, host);
 +        final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType());
 +        final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
 +
 +        Nic nic = null;
 +        if (broadcastUri != null) {
 +            nic = _nicsDao.findByNetworkIdInstanceIdAndBroadcastUri(network.getId(), vm.getId(), broadcastUri.toString());
 +        } else {
 +            nic = _networkModel.getNicInNetwork(vm.getId(), network.getId());
 +        }
 +
 +        if (nic == null) {
 +            s_logger.warn("Could not get a nic with " + network);
 +            return false;
 +        }
 +
 +        // don't delete default NIC on a user VM
 +        if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User) {
 +            s_logger.warn("Failed to remove nic from " + vm + " in " + network + ", nic is default.");
 +            throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default.");
 +        }
 +
 +        //Lock on nic is needed here
 +        final Nic lock = _nicsDao.acquireInLockTable(nic.getId());
 +        if (lock == null) {
 +            //check if nic is still there. Return if it was released already
 +            if (_nicsDao.findById(nic.getId()) == null) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Not need to remove the vm " + vm + " from network " + network + " as the vm doesn't have nic in this network");
 +                }
 +                return true;
 +            }
 +            throw new ConcurrentOperationException("Unable to lock nic " + nic.getId());
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Lock is acquired for nic id " + lock.getId() + " as a part of remove vm " + vm + " from network " + network);
 +        }
 +
 +        try {
 +            final NicProfile nicProfile =
 +                    new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), _networkModel.getNetworkRate(network.getId(), vm.getId()),
 +                            _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network));
 +
 +            //1) Unplug the nic
 +            if (vm.getState() == State.Running) {
 +                final NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType());
 +                s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network);
 +                final boolean result = unplugNic(network, nicTO, vmTO, context, dest);
 +                if (result) {
 +                    s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network);
 +                } else {
 +                    s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network);
 +                    return false;
 +                }
 +            } else if (vm.getState() != State.Stopped) {
 +                s_logger.warn("Unable to remove vm " + vm + " from network  " + network);
 +                throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", DataCenter.class, vm.getDataCenterId());
 +            }
 +
 +            //2) Release the nic
 +            _networkMgr.releaseNic(vmProfile, nic);
 +            s_logger.debug("Successfully released nic " + nic + "for vm " + vm);
 +
 +            //3) Remove the nic
 +            _networkMgr.removeNic(vmProfile, nic);
 +            return true;
 +        } finally {
 +            if (lock != null) {
 +                _nicsDao.releaseFromLockTable(lock.getId());
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Lock is released for nic id " + lock.getId() + " as a part of remove vm " + vm + " from network " + network);
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void findHostAndMigrate(final String vmUuid, final Long newSvcOfferingId, final ExcludeList excludes) throws InsufficientCapacityException, ConcurrentOperationException,
 +    ResourceUnavailableException {
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        if (vm == null) {
 +            throw new CloudRuntimeException("Unable to find " + vmUuid);
 +        }
 +
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +
 +        final Long srcHostId = vm.getHostId();
 +        final Long oldSvcOfferingId = vm.getServiceOfferingId();
 +        if (srcHostId == null) {
 +            throw new CloudRuntimeException("Unable to scale the vm because it doesn't have a host id");
 +        }
 +        final Host host = _hostDao.findById(srcHostId);
 +        final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null);
 +        excludes.addHost(vm.getHostId());
 +        vm.setServiceOfferingId(newSvcOfferingId); // Need to find the destination host based on new svc offering
 +
 +        DeployDestination dest = null;
 +
 +        try {
 +            dest = _dpMgr.planDeployment(profile, plan, excludes, null);
 +        } catch (final AffinityConflictException e2) {
 +            s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2);
 +            throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict");
 +        }
 +
 +        if (dest != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug(" Found " + dest + " for scaling the vm to.");
 +            }
 +        }
 +
 +        if (dest == null) {
 +            throw new InsufficientServerCapacityException("Unable to find a server to scale the vm to.", host.getClusterId());
 +        }
 +
 +        excludes.addHost(dest.getHost().getId());
 +        try {
 +            migrateForScale(vm.getUuid(), srcHostId, dest, oldSvcOfferingId);
 +        } catch (final ResourceUnavailableException e) {
 +            s_logger.debug("Unable to migrate to unavailable " + dest);
 +            throw e;
 +        } catch (final ConcurrentOperationException e) {
 +            s_logger.debug("Unable to migrate VM due to: " + e.getMessage());
 +            throw e;
 +        }
 +    }
 +
 +    @Override
 +    public void migrateForScale(final String vmUuid, final long srcHostId, final DeployDestination dest, final Long oldSvcOfferingId)
 +            throws ResourceUnavailableException, ConcurrentOperationException {
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                orchestrateMigrateForScale(vmUuid, srcHostId, dest, oldSvcOfferingId);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = migrateVmForScaleThroughJobQueue(vmUuid, srcHostId, dest, oldSvcOfferingId);
 +
 +            try {
 +                final VirtualMachine vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobResult;
 +                } else if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof RuntimeException) {
 +                    throw (RuntimeException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void orchestrateMigrateForScale(final String vmUuid, final long srcHostId, final DeployDestination dest, final Long oldSvcOfferingId)
 +            throws ResourceUnavailableException, ConcurrentOperationException {
 +
 +        VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +        s_logger.info("Migrating " + vm + " to " + dest);
 +
 +        vm.getServiceOfferingId();
 +        final long dstHostId = dest.getHost().getId();
 +        final Host fromHost = _hostDao.findById(srcHostId);
 +        if (fromHost == null) {
 +            s_logger.info("Unable to find the host to migrate from: " + srcHostId);
 +            throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId);
 +        }
 +
 +        if (fromHost.getClusterId().longValue() != dest.getCluster().getId()) {
 +            s_logger.info("Source and destination host are not in same cluster, unable to migrate to host: " + dstHostId);
 +            throw new CloudRuntimeException("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId());
 +        }
 +
 +        final VirtualMachineGuru vmGuru = getVmGuru(vm);
 +
 +        final long vmId = vm.getId();
 +        vm = _vmDao.findByUuid(vmUuid);
 +        if (vm == null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Unable to find the vm " + vm);
 +            }
 +            throw new CloudRuntimeException("Unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        if (vm.getState() != State.Running) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
 +            }
 +            throw new CloudRuntimeException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString());
 +        }
 +
 +        AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE;
 +        if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
 +            alertType = AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE;
 +        } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
 +            alertType = AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY_MIGRATE;
 +        }
 +
 +        final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +        _networkMgr.prepareNicForMigration(profile, dest);
 +
 +        volumeMgr.prepareForMigration(profile, dest);
 +
 +        final VirtualMachineTO to = toVmTO(profile);
 +        final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
 +
 +        ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
 +        work.setStep(Step.Prepare);
 +        work.setResourceType(ItWorkVO.ResourceType.Host);
 +        work.setResourceId(dstHostId);
 +        work = _workDao.persist(work);
 +
 +        Answer pfma = null;
 +        try {
 +            pfma = _agentMgr.send(dstHostId, pfmc);
 +            if (pfma == null || !pfma.getResult()) {
 +                final String details = pfma != null ? pfma.getDetails() : "null answer returned";
 +                final String msg = "Unable to prepare for migration due to " + details;
 +                pfma = null;
 +                throw new AgentUnavailableException(msg, dstHostId);
 +            }
 +        } catch (final OperationTimedoutException e1) {
 +            throw new AgentUnavailableException("Operation timed out", dstHostId);
 +        } finally {
 +            if (pfma == null) {
 +                work.setStep(Step.Done);
 +                _workDao.update(work.getId(), work);
 +            }
 +        }
 +
 +        vm.setLastHostId(srcHostId);
 +        try {
 +            if (vm == null || vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) {
 +                s_logger.info("Migration cancelled because state has changed: " + vm);
 +                throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
 +            }
 +        } catch (final NoTransitionException e1) {
 +            s_logger.info("Migration cancelled because " + e1.getMessage());
 +            throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage());
 +        }
 +
 +        boolean migrated = false;
 +        try {
 +            final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
 +            final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows, to, getExecuteInSequence(vm.getHypervisorType()));
 +
 +            String autoConvergence = _configDao.getValue(Config.KvmAutoConvergence.toString());
 +            boolean kvmAutoConvergence = Boolean.parseBoolean(autoConvergence);
 +
 +            mc.setAutoConvergence(kvmAutoConvergence);
 +
 +            mc.setHostGuid(dest.getHost().getGuid());
 +
 +            try {
 +                final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
 +                if (ma == null || !ma.getResult()) {
 +                    final String details = ma != null ? ma.getDetails() : "null answer returned";
 +                    final String msg = "Unable to migrate due to " + details;
 +                    s_logger.error(msg);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                if (e.isActive()) {
 +                    s_logger.warn("Active migration command so scheduling a restart for " + vm);
 +                    _haMgr.scheduleRestart(vm, true);
 +                }
 +                throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId);
 +            }
 +
 +            try {
 +                final long newServiceOfferingId = vm.getServiceOfferingId();
 +                vm.setServiceOfferingId(oldSvcOfferingId); // release capacity for the old service offering only
 +                if (!changeState(vm, VirtualMachine.Event.OperationSucceeded, dstHostId, work, Step.Started)) {
 +                    throw new ConcurrentOperationException("Unable to change the state for " + vm);
 +                }
 +                vm.setServiceOfferingId(newServiceOfferingId);
 +            } catch (final NoTransitionException e1) {
 +                throw new ConcurrentOperationException("Unable to change state due to " + e1.getMessage());
 +            }
 +
 +            try {
 +                if (!checkVmOnHost(vm, dstHostId)) {
 +                    s_logger.error("Unable to complete migration for " + vm);
 +                    try {
 +                        _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null);
 +                    } catch (final AgentUnavailableException e) {
 +                        s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId);
 +                    }
 +                    cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true);
 +                    throw new CloudRuntimeException("Unable to complete migration for " + vm);
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                s_logger.debug("Error while checking the vm " + vm + " on host " + dstHostId, e);
 +            }
 +
 +            migrated = true;
 +        } finally {
 +            if (!migrated) {
 +                s_logger.info("Migration was unsuccessful.  Cleaning up: " + vm);
 +
 +                _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(),
 +                        "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone " + dest.getDataCenter().getName() + " and pod " +
 +                                dest.getPod().getName(), "Migrate Command failed.  Please check logs.");
 +                try {
 +                    _agentMgr.send(dstHostId, new Commands(cleanup(vm.getInstanceName())), null);
 +                } catch (final AgentUnavailableException ae) {
 +                    s_logger.info("Looks like the destination Host is unavailable for cleanup");
 +                }
 +
 +                try {
 +                    stateTransitTo(vm, Event.OperationFailed, srcHostId);
 +                } catch (final NoTransitionException e) {
 +                    s_logger.warn(e.getMessage());
 +                }
 +            }
 +
 +            work.setStep(Step.Done);
 +            _workDao.update(work.getId(), work);
 +        }
 +    }
 +
 +    @Override
 +    public boolean replugNic(final Network network, final NicTO nic, final VirtualMachineTO vm, final ReservationContext context, final DeployDestination dest) throws ConcurrentOperationException,
 +    ResourceUnavailableException, InsufficientCapacityException {
 +        boolean result = true;
 +
 +        final VMInstanceVO router = _vmDao.findById(vm.getId());
 +        if (router.getState() == State.Running) {
 +            try {
 +                final ReplugNicCommand replugNicCmd = new ReplugNicCommand(nic, vm.getName(), vm.getType(), vm.getDetails());
 +                final Commands cmds = new Commands(Command.OnError.Stop);
 +                cmds.addCommand("replugnic", replugNicCmd);
 +                _agentMgr.send(dest.getHost().getId(), cmds);
 +                final ReplugNicAnswer replugNicAnswer = cmds.getAnswer(ReplugNicAnswer.class);
 +                if (replugNicAnswer == null || !replugNicAnswer.getResult()) {
 +                    s_logger.warn("Unable to replug nic for vm " + vm.getName());
 +                    result = false;
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                throw new AgentUnavailableException("Unable to plug nic for router " + vm.getName() + " in network " + network, dest.getHost().getId(), e);
 +            }
 +        } else {
 +            s_logger.warn("Unable to apply ReplugNic, vm " + router + " is not in the right state " + router.getState());
 +
 +            throw new ResourceUnavailableException("Unable to apply ReplugNic on the backend," + " vm " + vm + " is not in the right state", DataCenter.class,
 +                    router.getDataCenterId());
 +        }
 +
 +        return result;
 +    }
 +
 +    public boolean plugNic(final Network network, final NicTO nic, final VirtualMachineTO vm, final ReservationContext context, final DeployDestination dest) throws ConcurrentOperationException,
 +    ResourceUnavailableException, InsufficientCapacityException {
 +        boolean result = true;
 +
 +        final VMInstanceVO router = _vmDao.findById(vm.getId());
 +        if (router.getState() == State.Running) {
 +            try {
 +                final PlugNicCommand plugNicCmd = new PlugNicCommand(nic, vm.getName(), vm.getType(), vm.getDetails());
 +                final Commands cmds = new Commands(Command.OnError.Stop);
 +                cmds.addCommand("plugnic", plugNicCmd);
 +                _agentMgr.send(dest.getHost().getId(), cmds);
 +                final PlugNicAnswer plugNicAnswer = cmds.getAnswer(PlugNicAnswer.class);
 +                if (plugNicAnswer == null || !plugNicAnswer.getResult()) {
 +                    s_logger.warn("Unable to plug nic for vm " + vm.getName());
 +                    result = false;
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                throw new AgentUnavailableException("Unable to plug nic for router " + vm.getName() + " in network " + network, dest.getHost().getId(), e);
 +            }
 +        } else {
 +            s_logger.warn("Unable to apply PlugNic, vm " + router + " is not in the right state " + router.getState());
 +
 +            throw new ResourceUnavailableException("Unable to apply PlugNic on the backend," + " vm " + vm + " is not in the right state", DataCenter.class,
 +                    router.getDataCenterId());
 +        }
 +
 +        return result;
 +    }
 +
 +    public boolean unplugNic(final Network network, final NicTO nic, final VirtualMachineTO vm, final ReservationContext context, final DeployDestination dest) throws ConcurrentOperationException,
 +    ResourceUnavailableException {
 +
 +        boolean result = true;
 +        final VMInstanceVO router = _vmDao.findById(vm.getId());
 +
 +        if (router.getState() == State.Running) {
 +            // collect vm network statistics before unplug a nic
 +            UserVmVO userVm = _userVmDao.findById(vm.getId());
 +            if (userVm != null && userVm.getType() == VirtualMachine.Type.User) {
 +                _userVmService.collectVmNetworkStatistics(userVm);
 +            }
 +            try {
 +                final Commands cmds = new Commands(Command.OnError.Stop);
 +                final UnPlugNicCommand unplugNicCmd = new UnPlugNicCommand(nic, vm.getName());
 +                cmds.addCommand("unplugnic", unplugNicCmd);
 +                _agentMgr.send(dest.getHost().getId(), cmds);
 +
 +                final UnPlugNicAnswer unplugNicAnswer = cmds.getAnswer(UnPlugNicAnswer.class);
 +                if (unplugNicAnswer == null || !unplugNicAnswer.getResult()) {
 +                    s_logger.warn("Unable to unplug nic from router " + router);
 +                    result = false;
 +                }
 +            } catch (final OperationTimedoutException e) {
 +                throw new AgentUnavailableException("Unable to unplug nic from rotuer " + router + " from network " + network, dest.getHost().getId(), e);
 +            }
 +        } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) {
 +            s_logger.debug("Vm " + router.getInstanceName() + " is in " + router.getState() + ", so not sending unplug nic command to the backend");
 +        } else {
 +            s_logger.warn("Unable to apply unplug nic, Vm " + router + " is not in the right state " + router.getState());
 +
 +            throw new ResourceUnavailableException("Unable to apply unplug nic on the backend," + " vm " + router + " is not in the right state", DataCenter.class,
 +                    router.getDataCenterId());
 +        }
 +
 +        return result;
 +    }
 +
 +    @Override
 +    public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering oldServiceOffering,
 +            final boolean reconfiguringOnExistingHost)
 +                    throws ResourceUnavailableException, InsufficientServerCapacityException, ConcurrentOperationException {
 +
 +        final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
 +        if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +            // avoid re-entrance
 +            VmWorkJobVO placeHolder = null;
 +            final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
 +            placeHolder = createPlaceHolderWork(vm.getId());
 +            try {
 +                return orchestrateReConfigureVm(vmUuid, oldServiceOffering, reconfiguringOnExistingHost);
 +            } finally {
 +                if (placeHolder != null) {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            }
 +        } else {
 +            final Outcome<VirtualMachine> outcome = reconfigureVmThroughJobQueue(vmUuid, oldServiceOffering, reconfiguringOnExistingHost);
 +
 +            VirtualMachine vm = null;
 +            try {
 +                vm = outcome.get();
 +            } catch (final InterruptedException e) {
 +                throw new RuntimeException("Operation is interrupted", e);
 +            } catch (final java.util.concurrent.ExecutionException e) {
 +                throw new RuntimeException("Execution excetion", e);
 +            }
 +
 +            final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
 +            if (jobResult != null) {
 +                if (jobResult instanceof ResourceUnavailableException) {
 +                    throw (ResourceUnavailableException)jobResult;
 +                } else if (jobResult instanceof ConcurrentOperationException) {
 +                    throw (ConcurrentOperationException)jobResult;
 +                } else if (jobResult instanceof InsufficientServerCapacityException) {
 +                    throw (InsufficientServerCapacityException)jobResult;
 +                } else if (jobResult instanceof Throwable) {
 +                    s_logger.error("Unhandled exception", (Throwable)jobResult);
 +                    throw new RuntimeException("Unhandled exception", (Throwable)jobResult);
 +                }
 +            }
 +
 +            return (VMInstanceVO)vm;
 +        }
 +    }
 +
 +    private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final ServiceOffering oldServiceOffering, final boolean reconfiguringOnExistingHost) throws ResourceUnavailableException,
 +    ConcurrentOperationException {
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final long newServiceofferingId = vm.getServiceOfferingId();
 +        final ServiceOffering newServiceOffering = _offeringDao.findById(vm.getId(), newServiceofferingId);
 +        final HostVO hostVo = _hostDao.findById(vm.getHostId());
 +
 +        final Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(hostVo.getClusterId());
 +        final Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(hostVo.getClusterId());
 +        final long minMemory = (long)(newServiceOffering.getRamSize() / memoryOvercommitRatio);
 +        final ScaleVmCommand reconfigureCmd =
 +                new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), (int)(newServiceOffering.getSpeed() / cpuOvercommitRatio),
 +                        newServiceOffering.getSpeed(), minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, newServiceOffering.getLimitCpuUse());
 +
 +        final Long dstHostId = vm.getHostId();
 +        if(vm.getHypervisorType().equals(HypervisorType.VMware)) {
 +            final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
 +            Map<String, String> details = null;
 +            details = hvGuru.getClusterSettings(vm.getId());
 +            reconfigureCmd.getVirtualMachine().setDetails(details);
 +        }
 +
 +        final ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Running, vm.getType(), vm.getId());
 +
 +        work.setStep(Step.Prepare);
 +        work.setResourceType(ItWorkVO.ResourceType.Host);
 +        work.setResourceId(vm.getHostId());
 +        _workDao.persist(work);
 +        boolean success = false;
 +        try {
 +            if (reconfiguringOnExistingHost) {
 +                vm.setServiceOfferingId(oldServiceOffering.getId());
 +                _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); //release the old capacity
 +                vm.setServiceOfferingId(newServiceofferingId);
 +                _capacityMgr.allocateVmCapacity(vm, false); // lock the new capacity
 +            }
 +
 +            final Answer reconfigureAnswer = _agentMgr.send(vm.getHostId(), reconfigureCmd);
 +            if (reconfigureAnswer == null || !reconfigureAnswer.getResult()) {
 +                s_logger.error("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
 +                throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
 +            }
 +
 +            success = true;
 +        } catch (final OperationTimedoutException e) {
 +            throw new AgentUnavailableException("Operation timed out on reconfiguring " + vm, dstHostId);
 +        } catch (final AgentUnavailableException e) {
 +            throw e;
 +        } finally {
 +            if (!success) {
 +                _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); // release the new capacity
 +                vm.setServiceOfferingId(oldServiceOffering.getId());
 +                _capacityMgr.allocateVmCapacity(vm, false); // allocate the old capacity
 +            }
 +        }
 +
 +        return vm;
 +
 +    }
 +
 +    @Override
 +    public String getConfigComponentName() {
 +        return VirtualMachineManager.class.getSimpleName();
 +    }
 +
 +    @Override
 +    public ConfigKey<?>[] getConfigKeys() {
 +        return new ConfigKey<?>[] {ClusterDeltaSyncInterval, StartRetry, VmDestroyForcestop, VmOpCancelInterval, VmOpCleanupInterval, VmOpCleanupWait,
 +            VmOpLockStateRetry,
 +            VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval, VmConfigDriveLabel};
 +    }
 +
 +    public List<StoragePoolAllocator> getStoragePoolAllocators() {
 +        return _storagePoolAllocators;
 +    }
 +
 +    @Inject
 +    public void setStoragePoolAllocators(final List<StoragePoolAllocator> storagePoolAllocators) {
 +        _storagePoolAllocators = storagePoolAllocators;
 +    }
 +
 +    //
 +    // PowerState report handling for out-of-band changes and handling of left-over transitional VM states
 +    //
 +
 +    @MessageHandler(topic = Topics.VM_POWER_STATE)
 +    protected void HandlePowerStateReport(final String subject, final String senderAddress, final Object args) {
 +        assert args != null;
 +        final Long vmId = (Long)args;
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vmId);
 +        if (pendingWorkJobs.size() == 0 && !_haMgr.hasPendingHaWork(vmId)) {
 +            // there is no pending operation job
 +            final VMInstanceVO vm = _vmDao.findById(vmId);
 +            if (vm != null) {
 +                switch (vm.getPowerState()) {
 +                case PowerOn:
 +                    handlePowerOnReportWithNoPendingJobsOnVM(vm);
 +                    break;
 +
 +                case PowerOff:
 +                case PowerReportMissing:
 +                    handlePowerOffReportWithNoPendingJobsOnVM(vm);
 +                    break;
 +
 +                    // PowerUnknown shouldn't be reported, it is a derived
 +                    // VM power state from host state (host un-reachable)
 +                case PowerUnknown:
 +                default:
 +                    assert false;
 +                    break;
 +                }
 +            } else {
 +                s_logger.warn("VM " + vmId + " no longer exists when processing VM state report");
 +            }
 +        } else {
 +            s_logger.info("There is pending job or HA tasks working on the VM. vm id: " + vmId + ", postpone power-change report by resetting power-change counters");
 +
 +            // reset VM power state tracking so that we won't lost signal when VM has
 +            // been translated to
 +            _vmDao.resetVmPowerStateTracking(vmId);
 +        }
 +    }
 +
 +    private void handlePowerOnReportWithNoPendingJobsOnVM(final VMInstanceVO vm) {
 +        //
 +        //    1) handle left-over transitional VM states
 +        //    2) handle out of band VM live migration
 +        //    3) handle out of sync stationary states, marking VM from Stopped to Running with
 +        //       alert messages
 +        //
 +        switch (vm.getState()) {
 +        case Starting:
 +            s_logger.info("VM " + vm.getInstanceName() + " is at " + vm.getState() + " and we received a power-on report while there is no pending jobs on it");
 +
 +            try {
 +                stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId());
 +            } catch (final NoTransitionException e) {
 +                s_logger.warn("Unexpected VM state transition exception, race-condition?", e);
 +            }
 +
 +            s_logger.info("VM " + vm.getInstanceName() + " is sync-ed to at Running state according to power-on report from hypervisor");
 +
 +            // we need to alert admin or user about this risky state transition
 +            _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(),
 +                    VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName()
 +                    + ") state is sync-ed (Starting -> Running) from out-of-context transition. VM network environment may need to be reset");
 +            break;
 +
 +        case Running:
 +            try {
 +                if (vm.getHostId() != null && vm.getHostId().longValue() != vm.getPowerHostId().longValue()) {
 +                    s_logger.info("Detected out of band VM migration from host " + vm.getHostId() + " to host " + vm.getPowerHostId());
 +                }
 +                stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId());
 +            } catch (final NoTransitionException e) {
 +                s_logger.warn("Unexpected VM state transition exception, race-condition?", e);
 +            }
 +
 +            break;
 +
 +        case Stopping:
 +        case Stopped:
 +            s_logger.info("VM " + vm.getInstanceName() + " is at " + vm.getState() + " and we received a power-on report while there is no pending jobs on it");
 +
 +            try {
 +                stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId());
 +            } catch (final NoTransitionException e) {
 +                s_logger.warn("Unexpected VM state transition exception, race-condition?", e);
 +            }
 +            _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(),
 +                    VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") state is sync-ed (" + vm.getState()
 +                    + " -> Running) from out-of-context transition. VM network environment may need to be reset");
 +
 +            s_logger.info("VM " + vm.getInstanceName() + " is sync-ed to at Running state according to power-on report from hypervisor");
 +            break;
 +
 +        case Destroyed:
 +        case Expunging:
 +            s_logger.info("Receive power on report when VM is in destroyed or expunging state. vm: "
 +                    + vm.getId() + ", state: " + vm.getState());
 +            break;
 +
 +        case Migrating:
 +            s_logger.info("VM " + vm.getInstanceName() + " is at " + vm.getState() + " and we received a power-on report while there is no pending jobs on it");
 +            try {
 +                stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId());
 +            } catch (final NoTransitionException e) {
 +                s_logger.warn("Unexpected VM state transition exception, race-condition?", e);
 +            }
 +            s_logger.info("VM " + vm.getInstanceName() + " is sync-ed to at Running state according to power-on report from hypervisor");
 +            break;
 +
 +        case Error:
 +        default:
 +            s_logger.info("Receive power on report when VM is in error or unexpected state. vm: "
 +                    + vm.getId() + ", state: " + vm.getState());
 +            break;
 +        }
 +    }
 +
 +    private void handlePowerOffReportWithNoPendingJobsOnVM(final VMInstanceVO vm) {
 +
 +        //    1) handle left-over transitional VM states
 +        //    2) handle out of sync stationary states, schedule force-stop to release resources
 +        //
 +        switch (vm.getState()) {
 +        case Starting:
 +        case Stopping:
 +        case Running:
 +        case Stopped:
 +        case Migrating:
 +            s_logger.info("VM " + vm.getInstanceName() + " is at " + vm.getState() + " and we received a power-off report while there is no pending jobs on it");
 +            if(vm.isHaEnabled() && vm.getState() == State.Running && vm.getHypervisorType() != HypervisorType.VMware && vm.getHypervisorType() != HypervisorType.Hyperv) {
 +                s_logger.info("Detected out-of-band stop of a HA enabled VM " + vm.getInstanceName() + ", will schedule restart");
 +                if(!_haMgr.hasPendingHaWork(vm.getId())) {
 +                    _haMgr.scheduleRestart(vm, true);
 +                } else {
 +                    s_logger.info("VM " + vm.getInstanceName() + " already has an pending HA task working on it");
 +                }
 +                return;
 +            }
 +
 +            final VirtualMachineGuru vmGuru = getVmGuru(vm);
 +            final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
 +            if (!sendStop(vmGuru, profile, true, true)) {
 +                // In case StopCommand fails, don't proceed further
 +                return;
 +            }
 +
 +            try {
 +                stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOffReport, null);
 +            } catch (final NoTransitionException e) {
 +                s_logger.warn("Unexpected VM state transition exception, race-condition?", e);
 +            }
 +
 +            _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(),
 +                    VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") state is sync-ed (" + vm.getState()
 +                    + " -> Stopped) from out-of-context transition.");
 +
 +            s_logger.info("VM " + vm.getInstanceName() + " is sync-ed to at Stopped state according to power-off report from hypervisor");
 +
 +            break;
 +
 +        case Destroyed:
 +        case Expunging:
 +            break;
 +
 +        case Error:
 +        default:
 +            break;
 +        }
 +    }
 +
 +    private void scanStalledVMInTransitionStateOnUpHost(final long hostId) {
 +        //
 +        // Check VM that is stuck in Starting, Stopping, Migrating states, we won't check
 +        // VMs in expunging state (this need to be handled specially)
 +        //
 +        // checking condition
 +        //    1) no pending VmWork job
 +        //    2) on hostId host and host is UP
 +        //
 +        // When host is UP, soon or later we will get a report from the host about the VM,
 +        // however, if VM is missing from the host report (it may happen in out of band changes
 +        // or from designed behave of XS/KVM), the VM may not get a chance to run the state-sync logic
 +        //
 +        // Therefore, we will scan thoses VMs on UP host based on last update timestamp, if the host is UP
 +        // and a VM stalls for status update, we will consider them to be powered off
 +        // (which is relatively safe to do so)
 +
 +        final long stallThresholdInMs = VmJobStateReportInterval.value() + (VmJobStateReportInterval.value() >> 1);
 +        final Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - stallThresholdInMs);
 +        final List<Long> mostlikelyStoppedVMs = listStalledVMInTransitionStateOnUpHost(hostId, cutTime);
 +        for (final Long vmId : mostlikelyStoppedVMs) {
 +            final VMInstanceVO vm = _vmDao.findById(vmId);
 +            assert vm != null;
 +            handlePowerOffReportWithNoPendingJobsOnVM(vm);
 +        }
 +
 +        final List<Long> vmsWithRecentReport = listVMInTransitionStateWithRecentReportOnUpHost(hostId, cutTime);
 +        for (final Long vmId : vmsWithRecentReport) {
 +            final VMInstanceVO vm = _vmDao.findById(vmId);
 +            assert vm != null;
 +            if (vm.getPowerState() == PowerState.PowerOn) {
 +                handlePowerOnReportWithNoPendingJobsOnVM(vm);
 +            } else {
 +                handlePowerOffReportWithNoPendingJobsOnVM(vm);
 +            }
 +        }
 +    }
 +
 +    private void scanStalledVMInTransitionStateOnDisconnectedHosts() {
 +        final Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - VmOpWaitInterval.value() * 1000);
 +        final List<Long> stuckAndUncontrollableVMs = listStalledVMInTransitionStateOnDisconnectedHosts(cutTime);
 +        for (final Long vmId : stuckAndUncontrollableVMs) {
 +            final VMInstanceVO vm = _vmDao.findById(vmId);
 +
 +            // We now only alert administrator about this situation
 +            _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(),
 +                    VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") is stuck in " + vm.getState()
 +                    + " state and its host is unreachable for too long");
 +        }
 +    }
 +
 +    // VMs that in transitional state without recent power state report
 +    private List<Long> listStalledVMInTransitionStateOnUpHost(final long hostId, final Date cutTime) {
 +        final String sql = "SELECT i.* FROM vm_instance as i, host as h WHERE h.status = 'UP' " +
 +                "AND h.id = ? AND i.power_state_update_time < ? AND i.host_id = h.id " +
 +                "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " +
 +                "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)" +
 +                "AND i.removed IS NULL";
 +
 +        final List<Long> l = new ArrayList<Long>();
 +        TransactionLegacy txn = null;
 +        try {
 +            txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
 +
 +            PreparedStatement pstmt = null;
 +            try {
 +                pstmt = txn.prepareAutoCloseStatement(sql);
 +
 +                pstmt.setLong(1, hostId);
 +                pstmt.setString(2, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), cutTime));
 +                pstmt.setInt(3, JobInfo.Status.IN_PROGRESS.ordinal());
 +                final ResultSet rs = pstmt.executeQuery();
 +                while (rs.next()) {
 +                    l.add(rs.getLong(1));
 +                }
 +            } catch (final SQLException e) {
 +            } catch (final Throwable e) {
 +            }
 +
 +        } finally {
 +            if (txn != null) {
 +                txn.close();
 +            }
 +        }
 +        return l;
 +    }
 +
 +    // VMs that in transitional state and recently have power state update
 +    private List<Long> listVMInTransitionStateWithRecentReportOnUpHost(final long hostId, final Date cutTime) {
 +        final String sql = "SELECT i.* FROM vm_instance as i, host as h WHERE h.status = 'UP' " +
 +                "AND h.id = ? AND i.power_state_update_time > ? AND i.host_id = h.id " +
 +                "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " +
 +                "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)" +
 +                "AND i.removed IS NULL";
 +
 +        final List<Long> l = new ArrayList<Long>();
 +        TransactionLegacy txn = null;
 +        try {
 +            txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
 +            PreparedStatement pstmt = null;
 +            try {
 +                pstmt = txn.prepareAutoCloseStatement(sql);
 +
 +                pstmt.setLong(1, hostId);
 +                pstmt.setString(2, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), cutTime));
 +                pstmt.setInt(3, JobInfo.Status.IN_PROGRESS.ordinal());
 +                final ResultSet rs = pstmt.executeQuery();
 +                while (rs.next()) {
 +                    l.add(rs.getLong(1));
 +                }
 +            } catch (final SQLException e) {
 +            } catch (final Throwable e) {
 +            }
 +            return l;
 +        } finally {
 +            if (txn != null) {
 +                txn.close();
 +            }
 +        }
 +    }
 +
 +    private List<Long> listStalledVMInTransitionStateOnDisconnectedHosts(final Date cutTime) {
 +        final String sql = "SELECT i.* FROM vm_instance as i, host as h WHERE h.status != 'UP' " +
 +                "AND i.power_state_update_time < ? AND i.host_id = h.id " +
 +                "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " +
 +                "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)" +
 +                "AND i.removed IS NULL";
 +
 +        final List<Long> l = new ArrayList<Long>();
 +        TransactionLegacy txn = null;
 +        try {
 +            txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
 +            PreparedStatement pstmt = null;
 +            try {
 +                pstmt = txn.prepareAutoCloseStatement(sql);
 +
 +                pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), cutTime));
 +                pstmt.setInt(2, JobInfo.Status.IN_PROGRESS.ordinal());
 +                final ResultSet rs = pstmt.executeQuery();
 +                while (rs.next()) {
 +                    l.add(rs.getLong(1));
 +                }
 +            } catch (final SQLException e) {
 +            } catch (final Throwable e) {
 +            }
 +            return l;
 +        } finally {
 +            if (txn != null) {
 +                txn.close();
 +            }
 +        }
 +    }
 +
 +    //
 +    // VM operation based on new sync model
 +    //
 +
 +    public class VmStateSyncOutcome extends OutcomeImpl<VirtualMachine> {
 +        private long _vmId;
 +
 +        public VmStateSyncOutcome(final AsyncJob job, final PowerState desiredPowerState, final long vmId, final Long srcHostIdForMigration) {
 +            super(VirtualMachine.class, job, VmJobCheckInterval.value(), new Predicate() {
 +                @Override
 +                public boolean checkCondition() {
 +                    final AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId());
 +                    assert jobVo != null;
 +                    if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS) {
 +                        return true;
 +                    }
 +                    return false;
 +                }
 +            }, Topics.VM_POWER_STATE, AsyncJob.Topics.JOB_STATE);
 +            _vmId = vmId;
 +        }
 +
 +        @Override
 +        protected VirtualMachine retrieve() {
 +            return _vmDao.findById(_vmId);
 +        }
 +    }
 +
 +    public class VmJobVirtualMachineOutcome extends OutcomeImpl<VirtualMachine> {
 +        private long _vmId;
 +
 +        public VmJobVirtualMachineOutcome(final AsyncJob job, final long vmId) {
 +            super(VirtualMachine.class, job, VmJobCheckInterval.value(), new Predicate() {
 +                @Override
 +                public boolean checkCondition() {
 +                    final AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId());
 +                    assert jobVo != null;
 +                    if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS) {
 +                        return true;
 +                    }
 +
 +                    return false;
 +                }
 +            }, AsyncJob.Topics.JOB_STATE);
 +            _vmId = vmId;
 +        }
 +
 +        @Override
 +        protected VirtualMachine retrieve() {
 +            return _vmDao.findById(_vmId);
 +        }
 +    }
 +
 +    //
 +    // TODO build a common pattern to reduce code duplication in following methods
 +    // no time for this at current iteration
 +    //
 +    public Outcome<VirtualMachine> startVmThroughJobQueue(final String vmUuid,
 +            final Map<VirtualMachineProfile.Param, Object> params,
 +            final DeploymentPlan planToDeploy, final DeploymentPlanner planner) {
 +
 +        final CallContext context = CallContext.current();
 +        final User callingUser = context.getCallingUser();
 +        final Account callingAccount = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        VmWorkJobVO workJob = null;
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance,
 +                vm.getId(), VmWorkStart.class.getName());
 +
 +        if (pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkStart.class.getName());
 +
 +            workJob.setAccountId(callingAccount.getId());
 +            workJob.setUserId(callingUser.getId());
 +            workJob.setStep(VmWorkJobVO.Step.Starting);
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkStart workInfo = new VmWorkStart(callingUser.getId(), callingAccount.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER);
 +            workInfo.setPlan(planToDeploy);
 +            workInfo.setParams(params);
 +            if (planner != null) {
 +                workInfo.setDeploymentPlanner(planner.getName());
 +            }
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmStateSyncOutcome(workJob,
 +                VirtualMachine.PowerState.PowerOn, vm.getId(), null);
 +    }
 +
 +    public Outcome<VirtualMachine> stopVmThroughJobQueue(final String vmUuid, final boolean cleanup) {
 +        final CallContext context = CallContext.current();
 +        final Account account = context.getCallingAccount();
 +        final User user = context.getCallingUser();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                vm.getType(), vm.getId(),
 +                VmWorkStop.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkStop.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setStep(VmWorkJobVO.Step.Prepare);
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkStop workInfo = new VmWorkStop(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, cleanup);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmStateSyncOutcome(workJob,
 +                VirtualMachine.PowerState.PowerOff, vm.getId(), null);
 +    }
 +
 +    public Outcome<VirtualMachine> rebootVmThroughJobQueue(final String vmUuid,
 +            final Map<VirtualMachineProfile.Param, Object> params) {
 +
 +        final CallContext context = CallContext.current();
 +        final Account account = context.getCallingAccount();
 +        final User user = context.getCallingUser();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkReboot.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkReboot.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setStep(VmWorkJobVO.Step.Prepare);
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkReboot workInfo = new VmWorkReboot(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, params);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob,
 +                vm.getId());
 +    }
 +
 +    public Outcome<VirtualMachine> migrateVmThroughJobQueue(final String vmUuid, final long srcHostId, final DeployDestination dest) {
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkMigrate.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkMigrate.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkMigrate workInfo = new VmWorkMigrate(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId, dest);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmStateSyncOutcome(workJob,
 +                VirtualMachine.PowerState.PowerOn, vm.getId(), vm.getPowerHostId());
 +    }
 +
 +    public Outcome<VirtualMachine> migrateVmAwayThroughJobQueue(final String vmUuid, final long srcHostId) {
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkMigrateAway.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkMigrateAway.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkMigrateAway workInfo = new VmWorkMigrateAway(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +        }
 +
 +        _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmStateSyncOutcome(workJob, VirtualMachine.PowerState.PowerOn, vm.getId(), vm.getPowerHostId());
 +    }
 +
 +    public Outcome<VirtualMachine> migrateVmWithStorageThroughJobQueue(
 +            final String vmUuid, final long srcHostId, final long destHostId,
 +            final Map<Long, Long> volumeToPool) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkMigrateWithStorage.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkMigrateWithStorage.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkMigrateWithStorage workInfo = new VmWorkMigrateWithStorage(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId, destHostId, volumeToPool);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmStateSyncOutcome(workJob,
 +                VirtualMachine.PowerState.PowerOn, vm.getId(), destHostId);
 +    }
 +
 +    public Outcome<VirtualMachine> migrateVmForScaleThroughJobQueue(
 +            final String vmUuid, final long srcHostId, final DeployDestination dest, final Long newSvcOfferingId) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkMigrateForScale.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkMigrateForScale.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkMigrateForScale workInfo = new VmWorkMigrateForScale(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId, dest, newSvcOfferingId);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob, vm.getId());
 +    }
 +
 +    public Outcome<VirtualMachine> migrateVmStorageThroughJobQueue(
 +            final String vmUuid, final StoragePool destPool) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkStorageMigration.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkStorageMigration.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkStorageMigration workInfo = new VmWorkStorageMigration(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, destPool.getId());
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob, vm.getId());
 +    }
 +
 +    public Outcome<VirtualMachine> addVmToNetworkThroughJobQueue(
 +            final VirtualMachine vm, final Network network, final NicProfile requested) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkAddVmToNetwork.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkAddVmToNetwork.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkAddVmToNetwork workInfo = new VmWorkAddVmToNetwork(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, network.getId(), requested);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob, vm.getId());
 +    }
 +
 +    public Outcome<VirtualMachine> removeNicFromVmThroughJobQueue(
 +            final VirtualMachine vm, final Nic nic) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkRemoveNicFromVm.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkRemoveNicFromVm.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkRemoveNicFromVm workInfo = new VmWorkRemoveNicFromVm(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, nic.getId());
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob, vm.getId());
 +    }
 +
 +    public Outcome<VirtualMachine> removeVmFromNetworkThroughJobQueue(
 +            final VirtualMachine vm, final Network network, final URI broadcastUri) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkRemoveVmFromNetwork.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkRemoveVmFromNetwork.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkRemoveVmFromNetwork workInfo = new VmWorkRemoveVmFromNetwork(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, network, broadcastUri);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob, vm.getId());
 +    }
 +
 +    public Outcome<VirtualMachine> reconfigureVmThroughJobQueue(
 +            final String vmUuid, final ServiceOffering newServiceOffering, final boolean reconfiguringOnExistingHost) {
 +
 +        final CallContext context = CallContext.current();
 +        final User user = context.getCallingUser();
 +        final Account account = context.getCallingAccount();
 +
 +        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 +
 +        final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
 +                VirtualMachine.Type.Instance, vm.getId(),
 +                VmWorkReconfigure.class.getName());
 +
 +        VmWorkJobVO workJob = null;
 +        if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
 +            assert pendingWorkJobs.size() == 1;
 +            workJob = pendingWorkJobs.get(0);
 +        } else {
 +
 +            workJob = new VmWorkJobVO(context.getContextId());
 +
 +            workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
 +            workJob.setCmd(VmWorkReconfigure.class.getName());
 +
 +            workJob.setAccountId(account.getId());
 +            workJob.setUserId(user.getId());
 +            workJob.setVmType(VirtualMachine.Type.Instance);
 +            workJob.setVmInstanceId(vm.getId());
 +            workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
 +
 +            // save work context info (there are some duplications)
 +            final VmWorkReconfigure workInfo = new VmWorkReconfigure(user.getId(), account.getId(), vm.getId(),
 +                    VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, newServiceOffering.getId(), reconfiguringOnExistingHost);
 +            workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
 +
 +            _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
 +        }
 +        AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
 +
 +        return new VmJobVirtualMachineOutcome(workJob, vm.getId());
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateStart(final VmWorkStart work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +
 +        try{
 +            orchestrateStart(vm.getUuid(), work.getParams(), work.getPlan(), _dpMgr.getDeploymentPlannerByName(work.getDeploymentPlanner()));
 +        }
 +        catch (CloudRuntimeException e){
 +            e.printStackTrace();
 +            s_logger.info("Caught CloudRuntimeException, returning job failed " + e);
 +            CloudRuntimeException ex = new CloudRuntimeException("Unable to start VM instance");
 +            return new Pair<JobInfo.Status, String>(JobInfo.Status.FAILED, JobSerializerHelper.toObjectSerializedString(ex));
 +        }
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateStop(final VmWorkStop work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +            throw new CloudRuntimeException("Unable to find VM id=" + work.getVmId());
 +        }
 +
 +        orchestrateStop(vm.getUuid(), work.isCleanup());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateMigrate(final VmWorkMigrate work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +
 +        orchestrateMigrate(vm.getUuid(), work.getSrcHostId(), work.getDeployDestination());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateMigrateAway(final VmWorkMigrateAway work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +
 +        try {
 +            orchestrateMigrateAway(vm.getUuid(), work.getSrcHostId(), null);
 +        } catch (final InsufficientServerCapacityException e) {
 +            s_logger.warn("Failed to deploy vm " + vm.getId() + " with original planner, sending HAPlanner");
 +            orchestrateMigrateAway(vm.getUuid(), work.getSrcHostId(), _haMgr.getHAPlanner());
 +        }
 +
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateMigrateWithStorage(final VmWorkMigrateWithStorage work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +        orchestrateMigrateWithStorage(vm.getUuid(),
 +                work.getSrcHostId(),
 +                work.getDestHostId(),
 +                work.getVolumeToPool());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateMigrateForScale(final VmWorkMigrateForScale work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +        orchestrateMigrateForScale(vm.getUuid(),
 +                work.getSrcHostId(),
 +                work.getDeployDestination(),
 +                work.getNewServiceOfferringId());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateReboot(final VmWorkReboot work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +        orchestrateReboot(vm.getUuid(), work.getParams());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateAddVmToNetwork(final VmWorkAddVmToNetwork work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +
 +        final Network network = _networkDao.findById(work.getNetworkId());
 +        final NicProfile nic = orchestrateAddVmToNetwork(vm, network,
 +                work.getRequestedNicProfile());
 +
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(nic));
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateRemoveNicFromVm(final VmWorkRemoveNicFromVm work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +        final NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId());
 +        final boolean result = orchestrateRemoveNicFromVm(vm, nic);
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED,
 +                _jobMgr.marshallResultObject(result));
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateRemoveVmFromNetwork(final VmWorkRemoveVmFromNetwork work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +        final boolean result = orchestrateRemoveVmFromNetwork(vm,
 +                work.getNetwork(), work.getBroadcastUri());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED,
 +                _jobMgr.marshallResultObject(result));
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateReconfigure(final VmWorkReconfigure work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +
 +        final ServiceOffering newServiceOffering = _offeringDao.findById(vm.getId(), work.getNewServiceOfferingId());
 +
 +        reConfigureVm(vm.getUuid(), newServiceOffering,
 +                work.isSameHost());
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @ReflectionUse
 +    private Pair<JobInfo.Status, String> orchestrateStorageMigration(final VmWorkStorageMigration work) throws Exception {
 +        final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
 +        if (vm == null) {
 +            s_logger.info("Unable to find vm " + work.getVmId());
 +        }
 +        assert vm != null;
 +        final StoragePool pool = (PrimaryDataStoreInfo)dataStoreMgr.getPrimaryDataStore(work.getDestStoragePoolId());
 +        orchestrateStorageMigration(vm.getUuid(), pool);
 +
 +        return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
 +    }
 +
 +    @Override
 +    public Pair<JobInfo.Status, String> handleVmWorkJob(final VmWork work) throws Exception {
 +        return _jobHandlerProxy.handleVmWorkJob(work);
 +    }
 +
 +    private VmWorkJobVO createPlaceHolderWork(final long instanceId) {
 +        final VmWorkJobVO workJob = new VmWorkJobVO("");
 +
 +        workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_PLACEHOLDER);
 +        workJob.setCmd("");
 +        workJob.setCmdInfo("");
 +
 +        workJob.setAccountId(0);
 +        workJob.setUserId(0);
 +        workJob.setStep(VmWorkJobVO.Step.Starting);
 +        workJob.setVmType(VirtualMachine.Type.Instance);
 +        workJob.setVmInstanceId(instanceId);
 +        workJob.setInitMsid(ManagementServerNode.getManagementServerId());
 +
 +        _workJobDao.persist(workJob);
 +
 +        return workJob;
 +    }
 +}
diff --cc server/src/main/java/com/cloud/network/NetworkModelImpl.java
index 380aabf,0000000..f67c369
mode 100644,000000..100644
--- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
@@@ -1,2412 -1,0 +1,2442 @@@
 +// 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.math.BigInteger;
 +import java.security.InvalidParameterException;
 +import java.security.MessageDigest;
 +import java.security.NoSuchAlgorithmException;
 +import java.util.ArrayList;
++import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import java.util.TreeSet;
- import java.util.Collections;
++
 +import javax.inject.Inject;
 +import javax.naming.ConfigurationException;
 +
- import org.apache.commons.codec.binary.Base64;
 +import org.apache.log4j.Logger;
 +
++import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 +import org.apache.cloudstack.framework.config.ConfigKey;
 +import org.apache.cloudstack.framework.config.Configurable;
- 
- import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
++
 +import com.cloud.api.ApiDBUtils;
 +import com.cloud.configuration.Config;
 +import com.cloud.configuration.ConfigurationManager;
 +import com.cloud.dc.DataCenter;
++import com.cloud.dc.DataCenterVO;
 +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;
 +import com.cloud.dc.dao.PodVlanMapDao;
 +import com.cloud.dc.dao.VlanDao;
 +import com.cloud.domain.DomainVO;
 +import com.cloud.domain.dao.DomainDao;
 +import com.cloud.exception.InsufficientAddressCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.PermissionDeniedException;
 +import com.cloud.exception.UnsupportedServiceException;
 +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.IpAddresses;
 +import com.cloud.network.Network.Provider;
 +import com.cloud.network.Network.Service;
 +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.IPAddressVO;
 +import com.cloud.network.dao.NetworkDao;
 +import com.cloud.network.dao.NetworkDomainDao;
 +import com.cloud.network.dao.NetworkDomainVO;
 +import com.cloud.network.dao.NetworkServiceMapDao;
 +import com.cloud.network.dao.NetworkServiceMapVO;
 +import com.cloud.network.dao.NetworkVO;
 +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.PhysicalNetworkVO;
 +import com.cloud.network.dao.UserIpv6AddressDao;
 +import com.cloud.network.element.IpDeployer;
 +import com.cloud.network.element.IpDeployingRequester;
 +import com.cloud.network.element.NetworkElement;
 +import com.cloud.network.element.UserDataServiceProvider;
 +import com.cloud.network.rules.FirewallRule.Purpose;
 +import com.cloud.network.rules.FirewallRuleVO;
 +import com.cloud.network.rules.dao.PortForwardingRulesDao;
 +import com.cloud.network.vpc.dao.PrivateIpDao;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offering.NetworkOffering.Detail;
 +import com.cloud.offerings.NetworkOfferingServiceMapVO;
 +import com.cloud.offerings.NetworkOfferingVO;
 +import com.cloud.offerings.dao.NetworkOfferingDao;
 +import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
 +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 +import com.cloud.projects.dao.ProjectAccountDao;
 +import com.cloud.user.Account;
 +import com.cloud.user.AccountManager;
 +import com.cloud.user.AccountVO;
 +import com.cloud.user.DomainManager;
 +import com.cloud.user.dao.AccountDao;
 +import com.cloud.utils.StringUtils;
 +import com.cloud.utils.component.AdapterBase;
 +import com.cloud.utils.component.ManagerBase;
 +import com.cloud.utils.db.DB;
 +import com.cloud.utils.db.EntityManager;
 +import com.cloud.utils.db.JoinBuilder;
 +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.exception.CloudRuntimeException;
 +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.VMInstanceVO;
 +import com.cloud.vm.VirtualMachine;
 +import com.cloud.vm.VirtualMachine.Type;
 +import com.cloud.vm.dao.NicDao;
 +import com.cloud.vm.dao.NicSecondaryIpDao;
 +import com.cloud.vm.dao.VMInstanceDao;
 +
 +public class NetworkModelImpl extends ManagerBase implements NetworkModel, Configurable {
 +    static final Logger s_logger = Logger.getLogger(NetworkModelImpl.class);
 +    @Inject
 +    EntityManager _entityMgr;
 +    @Inject
 +    DataCenterDao _dcDao = null;
 +    @Inject
 +    VlanDao _vlanDao = null;
 +    @Inject
 +    IPAddressDao _ipAddressDao = null;
 +    @Inject
 +    AccountDao _accountDao = null;
 +    @Inject
 +    DomainDao _domainDao = null;
 +    @Inject
 +    AccountManager _accountMgr;
 +    @Inject
 +    ConfigurationDao _configDao;
 +    @Inject
 +    ConfigurationManager _configMgr;
 +    @Inject
 +    NetworkOfferingDao _networkOfferingDao = null;
 +    @Inject
 +    NetworkDao _networksDao = null;
 +    @Inject
 +    NicDao _nicDao = null;
 +    @Inject
 +    PodVlanMapDao _podVlanMapDao;
 +
 +    private List<NetworkElement> networkElements;
 +
 +    public List<NetworkElement> getNetworkElements() {
 +        return networkElements;
 +    }
 +
 +    public void setNetworkElements(List<NetworkElement> networkElements) {
 +        this.networkElements = networkElements;
 +    }
 +
 +    @Inject
 +    NetworkDomainDao _networkDomainDao;
 +    @Inject
 +    VMInstanceDao _vmDao;
 +
 +    @Inject
 +    FirewallRulesDao _firewallDao;
 +    @Inject
 +    DomainManager _domainMgr;
 +
 +    @Inject
 +    NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
 +    @Inject
 +    PhysicalNetworkDao _physicalNetworkDao;
 +    @Inject
 +    PhysicalNetworkServiceProviderDao _pNSPDao;
 +    @Inject
 +    PortForwardingRulesDao _portForwardingRulesDao;
 +    @Inject
 +    PhysicalNetworkTrafficTypeDao _pNTrafficTypeDao;
 +    @Inject
 +    NetworkServiceMapDao _ntwkSrvcDao;
 +    @Inject
 +    PrivateIpDao _privateIpDao;
 +    @Inject
 +    UserIpv6AddressDao _ipv6Dao;
 +    @Inject
 +    NicSecondaryIpDao _nicSecondaryIpDao;
 +    @Inject
 +    ApplicationLoadBalancerRuleDao _appLbRuleDao;
 +    @Inject
 +    private ProjectAccountDao _projectAccountDao;
 +    @Inject
 +    NetworkOfferingDetailsDao _ntwkOffDetailsDao;
 +
 +    private final HashMap<String, NetworkOfferingVO> _systemNetworks = new HashMap<String, NetworkOfferingVO>(5);
 +    static Long s_privateOfferingId = null;
 +
 +    SearchBuilder<IPAddressVO> IpAddressSearch;
 +    SearchBuilder<NicVO> NicForTrafficTypeSearch;
 +
 +    private boolean _allowSubdomainNetworkAccess;
 +
 +    private Map<String, String> _configs;
 +
 +    protected boolean _executeInSequenceNtwkElmtCmd;
 +
 +    HashMap<Long, Long> _lastNetworkIdsToFree = new HashMap<Long, Long>();
 +
 +    static HashMap<Service, List<Provider>> s_serviceToImplementedProvidersMap = new HashMap<Service, List<Provider>>();
 +    static HashMap<String, String> s_providerToNetworkElementMap = new HashMap<String, String>();
 +
 +    /**
 +     *
 +     */
 +    public NetworkModelImpl() {
 +        super();
 +    }
 +
 +    @Override
 +    public NetworkElement getElementImplementingProvider(String providerName) {
 +        String elementName = s_providerToNetworkElementMap.get(providerName);
 +        NetworkElement element = AdapterBase.getAdapterByName(networkElements, elementName);
 +        return element;
 +    }
 +
 +    @Override
 +    public List<Service> getElementServices(Provider provider) {
 +        NetworkElement element = getElementImplementingProvider(provider.getName());
 +        if (element == null) {
 +            throw new InvalidParameterValueException("Unable to find the Network Element implementing the Service Provider '" + provider.getName() + "'");
 +        }
 +        return new ArrayList<Service>(element.getCapabilities().keySet());
 +    }
 +
 +    @Override
 +    public boolean canElementEnableIndividualServices(Provider provider) {
 +        NetworkElement element = getElementImplementingProvider(provider.getName());
 +        if (element == null) {
 +            throw new InvalidParameterValueException("Unable to find the Network Element implementing the Service Provider '" + provider.getName() + "'");
 +        }
 +        return element.canEnableIndividualServices();
 +    }
 +
 +    Set<Purpose> getPublicIpPurposeInRules(PublicIpAddress ip, boolean includeRevoked, boolean includingFirewall) {
 +        Set<Purpose> result = new HashSet<Purpose>();
 +        List<FirewallRuleVO> rules = null;
 +        if (includeRevoked) {
 +            rules = _firewallDao.listByIp(ip.getId());
 +        } else {
 +            rules = _firewallDao.listByIpAndNotRevoked(ip.getId());
 +        }
 +
 +        if (rules == null || rules.isEmpty()) {
 +            return null;
 +        }
 +
 +        for (FirewallRuleVO rule : rules) {
 +            if (rule.getPurpose() != Purpose.Firewall || includingFirewall) {
 +                result.add(rule.getPurpose());
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    @Override
 +    public Map<PublicIpAddress, Set<Service>> getIpToServices(List<? extends PublicIpAddress> publicIps, boolean postApplyRules, boolean includingFirewall) {
 +        Map<PublicIpAddress, Set<Service>> ipToServices = new HashMap<PublicIpAddress, Set<Service>>();
 +
 +        if (publicIps != null && !publicIps.isEmpty()) {
 +            Set<Long> networkSNAT = new HashSet<Long>();
 +            for (PublicIpAddress ip : publicIps) {
 +                Set<Service> services = ipToServices.get(ip);
 +                if (services == null) {
 +                    services = new HashSet<Service>();
 +                }
 +                if (ip.isSourceNat()) {
 +                    if (!networkSNAT.contains(ip.getAssociatedWithNetworkId())) {
 +                        services.add(Service.SourceNat);
 +                        networkSNAT.add(ip.getAssociatedWithNetworkId());
 +                    } else {
 +                        CloudRuntimeException ex = new CloudRuntimeException("Multiple generic source NAT IPs provided for network");
 +                        // see the IPAddressVO.java class.
 +                        IPAddressVO ipAddr = ApiDBUtils.findIpAddressById(ip.getAssociatedWithNetworkId());
 +                        String ipAddrUuid = ip.getAssociatedWithNetworkId().toString();
 +                        if (ipAddr != null) {
 +                            ipAddrUuid = ipAddr.getUuid();
 +                        }
 +                        ex.addProxyObject(ipAddrUuid, "networkId");
 +                        throw ex;
 +                    }
 +                }
 +                ipToServices.put(ip, services);
 +
 +                // if IP in allocating state then it will not have any rules attached so skip IPAssoc to network service
 +                // provider
 +                if (ip.getState() == State.Allocating) {
 +                    continue;
 +                }
 +
 +                // check if any active rules are applied on the public IP
 +                Set<Purpose> purposes = getPublicIpPurposeInRules(ip, false, includingFirewall);
 +                // Firewall rules didn't cover static NAT
 +                if (ip.isOneToOneNat() && ip.getAssociatedWithVmId() != null) {
 +                    if (purposes == null) {
 +                        purposes = new HashSet<Purpose>();
 +                    }
 +                    purposes.add(Purpose.StaticNat);
 +                }
 +                if (purposes == null || purposes.isEmpty()) {
 +                    // since no active rules are there check if any rules are applied on the public IP but are in
 +                    // revoking state
 +
 +                    purposes = getPublicIpPurposeInRules(ip, true, includingFirewall);
 +                    if (ip.isOneToOneNat()) {
 +                        if (purposes == null) {
 +                            purposes = new HashSet<Purpose>();
 +                        }
 +                        purposes.add(Purpose.StaticNat);
 +                    }
 +                    if (purposes == null || purposes.isEmpty()) {
 +                        // IP is not being used for any purpose so skip IPAssoc to network service provider
 +                        continue;
 +                    } else {
 +                        if (postApplyRules) {
 +                            // no active rules/revoked rules are associated with this public IP, so remove the
 +                            // association with the provider
 +                            if (ip.isSourceNat()) {
 +                                s_logger.debug("Not releasing ip " + ip.getAddress().addr() + " as it is in use for SourceNat");
 +                            } else {
 +                                ip.setState(State.Releasing);
 +                            }
 +                        } else {
 +                            if (ip.getState() == State.Releasing) {
 +                                // rules are not revoked yet, so don't let the network service provider revoke the IP
 +                                // association
 +                                // mark IP is allocated so that IP association will not be removed from the provider
 +                                ip.setState(State.Allocated);
 +                            }
 +                        }
 +                    }
 +                }
 +                if (purposes.contains(Purpose.StaticNat)) {
 +                    services.add(Service.StaticNat);
 +                }
 +                if (purposes.contains(Purpose.LoadBalancing)) {
 +                    services.add(Service.Lb);
 +                }
 +                if (purposes.contains(Purpose.PortForwarding)) {
 +                    services.add(Service.PortForwarding);
 +                }
 +                if (purposes.contains(Purpose.Vpn)) {
 +                    services.add(Service.Vpn);
 +                }
 +                if (purposes.contains(Purpose.Firewall)) {
 +                    services.add(Service.Firewall);
 +                }
 +                if (services.isEmpty()) {
 +                    continue;
 +                }
 +                ipToServices.put(ip, services);
 +            }
 +        }
 +        return ipToServices;
 +    }
 +
 +    public boolean canIpUsedForNonConserveService(PublicIp ip, Service service) {
 +        // If it's non-conserve mode, then the new ip should not be used by any other services
 +        List<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
 +        ipList.add(ip);
 +        Map<PublicIpAddress, Set<Service>> ipToServices = getIpToServices(ipList, false, false);
 +        Set<Service> services = ipToServices.get(ip);
 +        // Not used currently, safe
 +        if (services == null || services.isEmpty()) {
 +            return true;
 +        }
 +        // Since it's non-conserve mode, only one service should used for IP
 +        if (services.size() != 1) {
 +            throw new InvalidParameterException("There are multiple services used ip " + ip.getAddress() + ".");
 +        }
 +        if (service != null && !((Service)services.toArray()[0] == service || service.equals(Service.Firewall))) {
 +            throw new InvalidParameterException("The IP " + ip.getAddress() + " is already used as " + ((Service)services.toArray()[0]).getName() + " rather than " +
 +                service.getName());
 +        }
 +        return true;
 +    }
 +
 +    Map<Service, Set<Provider>> getServiceProvidersMap(long networkId) {
 +        Map<Service, Set<Provider>> map = new HashMap<Service, Set<Provider>>();
 +        List<NetworkServiceMapVO> nsms = _ntwkSrvcDao.getServicesInNetwork(networkId);
 +        for (NetworkServiceMapVO nsm : nsms) {
 +            Set<Provider> providers = map.get(Service.getService(nsm.getService()));
 +            if (providers == null) {
 +                providers = new HashSet<Provider>();
 +            }
 +            providers.add(Provider.getProvider(nsm.getProvider()));
 +            map.put(Service.getService(nsm.getService()), providers);
 +        }
 +        return map;
 +    }
 +
 +    public boolean canIpUsedForService(PublicIp publicIp, Service service, Long networkId) {
 +        List<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
 +        ipList.add(publicIp);
 +        Map<PublicIpAddress, Set<Service>> ipToServices = getIpToServices(ipList, false, true);
 +        Set<Service> services = ipToServices.get(publicIp);
 +        if (services == null || services.isEmpty()) {
 +            return true;
 +        }
 +
 +        if (networkId == null) {
 +            networkId = publicIp.getAssociatedWithNetworkId();
 +        }
 +
 +        // We only support one provider for one service now
 +        Map<Service, Set<Provider>> serviceToProviders = getServiceProvidersMap(networkId);
 +        // Since IP already has service to bind with, the oldProvider can't be null
 +        Set<Provider> newProviders = serviceToProviders.get(service);
 +        if (newProviders == null || newProviders.isEmpty()) {
 +            throw new InvalidParameterException("There is no new provider for IP " + publicIp.getAddress() + " of service " + service.getName() + "!");
 +        }
 +        Provider newProvider = (Provider)newProviders.toArray()[0];
 +        Set<Provider> oldProviders = serviceToProviders.get(services.toArray()[0]);
 +        Provider oldProvider = (Provider)oldProviders.toArray()[0];
 +        Network network = _networksDao.findById(networkId);
 +        NetworkElement oldElement = getElementImplementingProvider(oldProvider.getName());
 +        NetworkElement newElement = getElementImplementingProvider(newProvider.getName());
 +        if (oldElement instanceof IpDeployingRequester && newElement instanceof IpDeployingRequester) {
 +            IpDeployer oldIpDeployer = ((IpDeployingRequester)oldElement).getIpDeployer(network);
 +            IpDeployer newIpDeployer = ((IpDeployingRequester)newElement).getIpDeployer(network);
 +            // FIXME: I ignored this check
 +        } else {
 +            throw new InvalidParameterException("Ip cannot be applied for new provider!");
 +        }
 +        return true;
 +    }
 +
 +    Map<Provider, Set<Service>> getProviderServicesMap(long networkId) {
 +        Map<Provider, Set<Service>> map = new HashMap<Provider, Set<Service>>();
 +        List<NetworkServiceMapVO> nsms = _ntwkSrvcDao.getServicesInNetwork(networkId);
 +        for (NetworkServiceMapVO nsm : nsms) {
 +            Set<Service> services = map.get(Provider.getProvider(nsm.getProvider()));
 +            if (services == null) {
 +                services = new HashSet<Service>();
 +            }
 +            services.add(Service.getService(nsm.getService()));
 +            map.put(Provider.getProvider(nsm.getProvider()), services);
 +        }
 +        return map;
 +    }
 +
 +    @Override
 +    public Map<Provider, ArrayList<PublicIpAddress>> getProviderToIpList(Network network, Map<PublicIpAddress, Set<Service>> ipToServices) {
 +        NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +        if (!offering.isConserveMode()) {
 +            for (PublicIpAddress ip : ipToServices.keySet()) {
 +                Set<Service> services = new HashSet<Service>();
 +                services.addAll(ipToServices.get(ip));
 +                if (services != null && services.contains(Service.Firewall)) {
 +                    services.remove(Service.Firewall);
 +                }
 +                if (services != null && services.size() > 1) {
 +                    throw new CloudRuntimeException("Ip " + ip.getAddress() + " is used by multiple services!");
 +                }
 +            }
 +        }
 +        Map<Service, Set<PublicIpAddress>> serviceToIps = new HashMap<Service, Set<PublicIpAddress>>();
 +        for (PublicIpAddress ip : ipToServices.keySet()) {
 +            for (Service service : ipToServices.get(ip)) {
 +                Set<PublicIpAddress> ips = serviceToIps.get(service);
 +                if (ips == null) {
 +                    ips = new HashSet<PublicIpAddress>();
 +                }
 +                ips.add(ip);
 +                serviceToIps.put(service, ips);
 +            }
 +        }
 +        // TODO Check different provider for same IP
 +        Map<Provider, Set<Service>> providerToServices = getProviderServicesMap(network.getId());
 +        Map<Provider, ArrayList<PublicIpAddress>> providerToIpList = new HashMap<Provider, ArrayList<PublicIpAddress>>();
 +        for (Provider provider : providerToServices.keySet()) {
 +            if (!(getElementImplementingProvider(provider.getName()) instanceof IpDeployingRequester)) {
 +                continue;
 +            }
 +            Set<Service> services = providerToServices.get(provider);
 +            ArrayList<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
 +            Set<PublicIpAddress> ipSet = new HashSet<PublicIpAddress>();
 +            for (Service service : services) {
 +                Set<PublicIpAddress> serviceIps = serviceToIps.get(service);
 +                if (serviceIps == null || serviceIps.isEmpty()) {
 +                    continue;
 +                }
 +                ipSet.addAll(serviceIps);
 +            }
 +            Set<PublicIpAddress> sourceNatIps = serviceToIps.get(Service.SourceNat);
 +            if (sourceNatIps != null && !sourceNatIps.isEmpty()) {
 +                ipList.addAll(0, sourceNatIps);
 +                ipSet.removeAll(sourceNatIps);
 +            }
 +            ipList.addAll(ipSet);
 +            providerToIpList.put(provider, ipList);
 +        }
 +        return providerToIpList;
 +    }
 +
 +    @Override
 +    public List<IPAddressVO> listPublicIpsAssignedToGuestNtwk(long accountId, long associatedNetworkId, Boolean sourceNat) {
 +        SearchCriteria<IPAddressVO> sc = IpAddressSearch.create();
 +        sc.setParameters("accountId", accountId);
 +        sc.setParameters("associatedWithNetworkId", associatedNetworkId);
 +        if (sourceNat != null) {
 +            sc.addAnd("sourceNat", SearchCriteria.Op.EQ, sourceNat);
 +        }
 +        sc.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork);
 +
 +        return _ipAddressDao.search(sc, null);
 +    }
 +
 +    @Override
 +    public List<IPAddressVO> listPublicIpsAssignedToGuestNtwk(long associatedNetworkId, Boolean sourceNat) {
 +        SearchCriteria<IPAddressVO> sc = IpAddressSearch.create();
 +        sc.setParameters("associatedWithNetworkId", associatedNetworkId);
 +
 +        if (sourceNat != null) {
 +            sc.addAnd("sourceNat", SearchCriteria.Op.EQ, sourceNat);
 +        }
 +        sc.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork);
 +
 +        return _ipAddressDao.search(sc, null);
 +    }
 +
 +    @Override
 +    public List<IPAddressVO> listPublicIpsAssignedToAccount(long accountId, long dcId, Boolean sourceNat) {
 +        SearchCriteria<IPAddressVO> sc = IpAddressSearch.create();
 +        sc.setParameters("accountId", accountId);
 +        sc.setParameters("dataCenterId", dcId);
 +
 +        if (sourceNat != null) {
 +            sc.addAnd("sourceNat", SearchCriteria.Op.EQ, sourceNat);
 +        }
 +        sc.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork);
 +
 +        return _ipAddressDao.search(sc, null);
 +    }
 +
 +    @Override
 +    public List<? extends Nic> getNics(long vmId) {
 +        return _nicDao.listByVmId(vmId);
 +    }
 +
 +    @Override
 +    public String getNextAvailableMacAddressInNetwork(long networkId) throws InsufficientAddressCapacityException {
 +        NetworkVO network = _networksDao.findById(networkId);
 +        String mac = _networksDao.getNextAvailableMacAddress(networkId, MACIdentifier.value());
 +        if (mac == null) {
 +            throw new InsufficientAddressCapacityException("Unable to create another mac address", Network.class, networkId);
 +        }
 +        return mac;
 +    }
 +
 +    @Override
 +    @DB
 +    public Network getNetwork(long id) {
 +        return _networksDao.findById(id);
 +    }
 +
 +    @Override
 +    public boolean canUseForDeploy(Network network) {
 +        if (network.getTrafficType() != TrafficType.Guest) {
 +            return false;
 +        }
 +        if (listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
 +            return true; // do not check free IPs if there is no service in the network
 +        }
 +        boolean hasFreeIps = true;
 +        if (network.getGuestType() == GuestType.Shared) {
 +            if (network.getGateway() != null) {
 +                hasFreeIps = _ipAddressDao.countFreeIPsInNetwork(network.getId()) > 0;
 +            }
 +            if (!hasFreeIps) {
 +                return false;
 +            }
 +            if (network.getIp6Gateway() != null) {
 +                hasFreeIps = isIP6AddressAvailableInNetwork(network.getId());
 +            }
 +        } else {
 +            if (network.getCidr() == null) {
 +                s_logger.debug("Network - " + network.getId() +  " has NULL CIDR.");
 +                return false;
 +            }
 +            hasFreeIps = (getAvailableIps(network, null)).size() > 0;
 +        }
 +
 +        return hasFreeIps;
 +    }
 +
 +    @Override
 +    public boolean isIP6AddressAvailableInNetwork(long networkId) {
 +        Network network = _networksDao.findById(networkId);
 +        if (network == null) {
 +            return false;
 +        }
 +        if (network.getIp6Gateway() == null) {
 +            return false;
 +        }
 +        List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
 +        for (Vlan vlan : vlans) {
 +            if (isIP6AddressAvailableInVlan(vlan.getId())) {
 +                return true;
 +            }
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean isIP6AddressAvailableInVlan(long vlanId) {
 +        VlanVO vlan = _vlanDao.findById(vlanId);
 +        if (vlan.getIp6Range() == null) {
 +            return false;
 +        }
 +        long existedCount = _ipv6Dao.countExistedIpsInVlan(vlanId);
 +        BigInteger existedInt = BigInteger.valueOf(existedCount);
 +        BigInteger rangeInt = NetUtils.countIp6InRange(vlan.getIp6Range());
 +        return (existedInt.compareTo(rangeInt) < 0);
 +    }
 +
 +    @Override
 +    public Map<Service, Map<Capability, String>> getNetworkCapabilities(long networkId) {
 +
 +        Map<Service, Map<Capability, String>> networkCapabilities = new HashMap<Service, Map<Capability, String>>();
 +
 +        // list all services of this networkOffering
 +        List<NetworkServiceMapVO> servicesMap = _ntwkSrvcDao.getServicesInNetwork(networkId);
 +        for (NetworkServiceMapVO instance : servicesMap) {
 +            Service service = Service.getService(instance.getService());
 +            NetworkElement element = getElementImplementingProvider(instance.getProvider());
 +            if (element != null) {
 +                Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
 +                if (elementCapabilities != null) {
 +                    networkCapabilities.put(service, elementCapabilities.get(service));
 +                }
 +            }
 +        }
 +
 +        return networkCapabilities;
 +    }
 +
 +    @Override
 +    public Map<Capability, String> getNetworkServiceCapabilities(long networkId, Service service) {
 +
 +        if (!areServicesSupportedInNetwork(networkId, service)) {
 +            // TBD: networkId to uuid. No VO object being passed. So we will need to call
 +            // addProxyObject with hardcoded tablename. Or we should probably look up the correct dao proxy object.
 +            throw new UnsupportedServiceException("Service " + service.getName() + " is not supported in the network id=" + networkId);
 +        }
 +
 +        Map<Capability, String> serviceCapabilities = new HashMap<Capability, String>();
 +
 +        // get the Provider for this Service for this offering
 +        String provider = _ntwkSrvcDao.getProviderForServiceInNetwork(networkId, service);
 +
 +        NetworkElement element = getElementImplementingProvider(provider);
 +        if (element != null) {
 +            Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
 +            ;
 +
 +            if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
 +                throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
 +                    " implementing Provider=" + provider);
 +            }
 +            serviceCapabilities = elementCapabilities.get(service);
 +        }
 +
 +        return serviceCapabilities;
 +    }
 +
 +    @Override
 +    public Map<Capability, String> getNetworkOfferingServiceCapabilities(NetworkOffering offering, Service service) {
 +
 +        if (!areServicesSupportedByNetworkOffering(offering.getId(), service)) {
 +            // TBD: We should be sending networkOfferingId and not the offering object itself.
 +            throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the network offering " + offering);
 +        }
 +
 +        Map<Capability, String> serviceCapabilities = new HashMap<Capability, String>();
 +
 +        // get the Provider for this Service for this offering
 +        List<String> providers = _ntwkOfferingSrvcDao.listProvidersForServiceForNetworkOffering(offering.getId(), service);
 +        if (providers.isEmpty()) {
 +            // TBD: We should be sending networkOfferingId and not the offering object itself.
 +            throw new InvalidParameterValueException("Service " + service.getName() + " is not supported by the network offering " + offering);
 +        }
 +
 +        // FIXME - in post 3.0 we are going to support multiple providers for the same service per network offering, so
 +        // we have to calculate capabilities for all of them
 +        String provider = providers.get(0);
 +
 +        // FIXME we return the capabilities of the first provider of the service - what if we have multiple providers
 +        // for same Service?
 +        NetworkElement element = getElementImplementingProvider(provider);
 +        if (element != null) {
 +            Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
 +            ;
 +
 +            if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
 +                // TBD: We should be sending providerId and not the offering object itself.
 +                throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
 +                    " implementing Provider=" + provider);
 +            }
 +            serviceCapabilities = elementCapabilities.get(service);
 +        }
 +
 +        return serviceCapabilities;
 +    }
 +
 +    @Override
 +    public NetworkVO getSystemNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) {
 +        // find system public network offering
 +        Long networkOfferingId = null;
 +        List<NetworkOfferingVO> offerings = _networkOfferingDao.listSystemNetworkOfferings();
 +        for (NetworkOfferingVO offering : offerings) {
 +            if (offering.getTrafficType() == trafficType) {
 +                networkOfferingId = offering.getId();
 +                break;
 +            }
 +        }
 +
 +        if (networkOfferingId == null) {
 +            throw new InvalidParameterValueException("Unable to find system network offering with traffic type " + trafficType);
 +        }
 +
 +        List<NetworkVO> networks = _networksDao.listBy(Account.ACCOUNT_ID_SYSTEM, networkOfferingId, zoneId);
 +        if (networks == null || networks.isEmpty()) {
 +            // TBD: send uuid instead of zoneId. Hardcode tablename in call to addProxyObject().
 +            throw new InvalidParameterValueException("Unable to find network with traffic type " + trafficType + " in zone " + zoneId);
 +        }
 +        return networks.get(0);
 +    }
 +
 +    @Override
 +    public NetworkVO getNetworkWithSGWithFreeIPs(Long zoneId) {
 +        List<NetworkVO> networks = _networksDao.listByZoneSecurityGroup(zoneId);
 +        if (networks == null || networks.isEmpty()) {
 +            return null;
 +        }
 +        NetworkVO ret_network = null;
 +        for (NetworkVO nw : networks) {
 +            List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(nw.getId());
 +            for (VlanVO vlan : vlans) {
 +                if (_ipAddressDao.countFreeIpsInVlan(vlan.getId()) > 0) {
 +                    ret_network = nw;
 +                    break;
 +                }
 +            }
 +            if (ret_network != null) {
 +                break;
 +            }
 +        }
 +        if (ret_network == null) {
 +            s_logger.debug("Can not find network with security group enabled with free IPs");
 +        }
 +        return ret_network;
 +    }
 +
 +    @Override
 +    public NetworkVO getNetworkWithSecurityGroupEnabled(Long zoneId) {
 +        List<NetworkVO> networks = _networksDao.listByZoneSecurityGroup(zoneId);
 +        if (networks == null || networks.isEmpty()) {
 +            return null;
 +        }
 +
 +        if (networks.size() > 1) {
 +            s_logger.debug("There are multiple network with security group enabled? select one of them...");
 +        }
 +        return networks.get(0);
 +    }
 +
 +    @Override
 +    public PublicIpAddress getPublicIpAddress(long ipAddressId) {
 +        IPAddressVO addr = _ipAddressDao.findById(ipAddressId);
 +        if (addr == null) {
 +            return null;
 +        }
 +
 +        return PublicIp.createFromAddrAndVlan(addr, _vlanDao.findById(addr.getVlanId()));
 +    }
 +
 +    @Override
 +    public List<VlanVO> listPodVlans(long podId) {
 +        List<VlanVO> vlans = _vlanDao.listVlansForPodByType(podId, VlanType.DirectAttached);
 +        return vlans;
 +    }
 +
 +    @Override
 +    public List<NetworkVO> listNetworksUsedByVm(long vmId, boolean isSystem) {
 +        List<NetworkVO> networks = new ArrayList<NetworkVO>();
 +
 +        List<NicVO> nics = _nicDao.listByVmId(vmId);
 +        if (nics != null) {
 +            for (Nic nic : nics) {
 +                NetworkVO network = _networksDao.findByIdIncludingRemoved(nic.getNetworkId());
 +
 +                if (isNetworkSystem(network) == isSystem) {
 +                    networks.add(network);
 +                }
 +            }
 +        }
 +
 +        return networks;
 +    }
 +
 +    @Override
 +    public Nic getNicInNetwork(long vmId, long networkId) {
 +        return _nicDao.findByNtwkIdAndInstanceId(networkId, vmId);
 +    }
 +
 +    @Override
 +    public String getIpInNetwork(long vmId, long networkId) {
 +        Nic guestNic = getNicInNetwork(vmId, networkId);
 +        assert (guestNic != null && guestNic.getIPv4Address() != null) : "Vm doesn't belong to network associated with " + "ipAddress or ip4 address is null";
 +        return guestNic.getIPv4Address();
 +    }
 +
 +    @Override
 +    public String getIpInNetworkIncludingRemoved(long vmId, long networkId) {
 +        Nic guestNic = getNicInNetworkIncludingRemoved(vmId, networkId);
 +        assert (guestNic != null && guestNic.getIPv4Address() != null) : "Vm doesn't belong to network associated with " + "ipAddress or ip4 address is null";
 +        return guestNic.getIPv4Address();
 +    }
 +
 +    @Override
 +    public List<NicVO> getNicsForTraffic(long vmId, TrafficType type) {
 +        SearchCriteria<NicVO> sc = NicForTrafficTypeSearch.create();
 +        sc.setParameters("instance", vmId);
 +        sc.setJoinParameters("network", "traffictype", type);
 +
 +        return _nicDao.search(sc, null);
 +    }
 +
 +    @Override
 +    public IpAddress getIp(long ipAddressId) {
 +        return _ipAddressDao.findById(ipAddressId);
 +    }
 +
 +    @Override
 +    public Network getDefaultNetworkForVm(long vmId) {
 +        Nic defaultNic = getDefaultNic(vmId);
 +        if (defaultNic == null) {
 +            return null;
 +        } else {
 +            return _networksDao.findById(defaultNic.getNetworkId());
 +        }
 +    }
 +
 +    @Override
 +    public Nic getDefaultNic(long vmId) {
 +        List<NicVO> nics = _nicDao.listByVmId(vmId);
 +        Nic defaultNic = null;
 +        if (nics != null) {
 +            for (Nic nic : nics) {
 +                if (nic.isDefaultNic()) {
 +                    defaultNic = nic;
 +                    break;
 +                }
 +            }
 +        } else {
 +            s_logger.debug("Unable to find default network for the vm; vm doesn't have any nics");
 +            return null;
 +        }
 +
 +        if (defaultNic == null) {
 +            s_logger.debug("Unable to find default network for the vm; vm doesn't have default nic");
 +        }
 +
 +        return defaultNic;
 +
 +    }
 +
 +    @Override
 +    public UserDataServiceProvider getUserDataUpdateProvider(Network network) {
 +        String userDataProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData);
 +
 +        if (userDataProvider == null) {
 +            s_logger.debug("Network " + network + " doesn't support service " + Service.UserData.getName());
 +            return null;
 +        }
 +
 +        return (UserDataServiceProvider)getElementImplementingProvider(userDataProvider);
 +    }
 +
 +    @Override
 +    public  boolean isSharedNetworkWithoutServices (long networkId) {
 +
 +        Network network = _networksDao.findById(networkId);
 +
 +        if (network != null && network.getGuestType() != GuestType.Shared) {
 +            return false;
 +        }
 +
 +        List<Service> services = listNetworkOfferingServices(network.getNetworkOfferingId());
 +
 +        if (services == null || services.isEmpty()) {
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +
 +    @Override
 +    public boolean areServicesSupportedByNetworkOffering(long networkOfferingId, Service... services) {
 +        return (_ntwkOfferingSrvcDao.areServicesSupportedByNetworkOffering(networkOfferingId, services));
 +    }
 +
 +    @Override
 +    public boolean areServicesSupportedInNetwork(long networkId, Service... services) {
 +        return (_ntwkSrvcDao.areServicesSupportedInNetwork(networkId, services));
 +    }
 +
 +    @Override
 +    public String getIpOfNetworkElementInVirtualNetwork(long accountId, long dataCenterId) {
 +
 +        List<NetworkVO> virtualNetworks = _networksDao.listByZoneAndGuestType(accountId, dataCenterId, GuestType.Isolated, false);
 +
 +        if (virtualNetworks.isEmpty()) {
 +            s_logger.trace("Unable to find default Virtual network account id=" + accountId);
 +            return null;
 +        }
 +
 +        NetworkVO virtualNetwork = virtualNetworks.get(0);
 +
 +        NicVO networkElementNic = _nicDao.findByNetworkIdAndType(virtualNetwork.getId(), Type.DomainRouter);
 +
 +        if (networkElementNic != null) {
 +            return networkElementNic.getIPv4Address();
 +        } else {
 +            s_logger.warn("Unable to set find network element for the network id=" + virtualNetwork.getId());
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    public List<NetworkVO> listNetworksForAccount(long accountId, long zoneId, GuestType type) {
 +        List<NetworkVO> accountNetworks = new ArrayList<NetworkVO>();
 +        List<NetworkVO> zoneNetworks = _networksDao.listByZone(zoneId);
 +
 +        for (NetworkVO network : zoneNetworks) {
 +            if (!isNetworkSystem(network)) {
 +                if (network.getGuestType() == GuestType.Shared || !_networksDao.listBy(accountId, network.getId()).isEmpty()) {
 +                    if (type == null || type == network.getGuestType()) {
 +                        accountNetworks.add(network);
 +                    }
 +                }
 +            }
 +        }
 +        return accountNetworks;
 +    }
 +
 +    @Override
 +    public List<NetworkVO> listAllNetworksInAllZonesByType(GuestType type) {
 +        List<NetworkVO> networks = new ArrayList<NetworkVO>();
 +        for (NetworkVO network : _networksDao.listAll()) {
 +            if (!isNetworkSystem(network)) {
 +                networks.add(network);
 +            }
 +        }
 +        return networks;
 +    }
 +
 +    @Override
 +    public Long getDedicatedNetworkDomain(long networkId) {
 +        NetworkDomainVO networkMaps = _networkDomainDao.getDomainNetworkMapByNetworkId(networkId);
 +        if (networkMaps != null) {
 +            return networkMaps.getDomainId();
 +        } else {
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    public Integer getNetworkRate(long networkId, Long vmId) {
 +        VMInstanceVO vm = null;
 +        if (vmId != null) {
 +            vm = _vmDao.findById(vmId);
 +        }
 +        final Network network = getNetwork(networkId);
 +        final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
 +
 +        // For user VM: For default nic use network rate from the service/compute offering,
 +        //              or on NULL from vm.network.throttling.rate global setting
 +        // For router: Get network rate for guest and public networks from the guest network offering
 +        //              or on NULL from network.throttling.rate
 +        // For others: Use network rate from their network offering,
 +        //              or on NULL from network.throttling.rate setting at zone > global level
 +        // http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/service_offerings.html#network-throttling
 +        if (vm != null) {
 +            if (vm.getType() == Type.User) {
 +                final Nic nic = _nicDao.findByNtwkIdAndInstanceId(networkId, vmId);
 +                if (nic != null && nic.isDefaultNic()) {
 +                    return _configMgr.getServiceOfferingNetworkRate(vm.getServiceOfferingId(), network.getDataCenterId());
 +                }
 +            }
 +            if (vm.getType() == Type.DomainRouter && (network.getTrafficType() == TrafficType.Public || network.getTrafficType() == TrafficType.Guest)) {
 +                for (final Nic nic: _nicDao.listByVmId(vmId)) {
 +                    final NetworkVO nw = _networksDao.findById(nic.getNetworkId());
 +                    if (nw.getTrafficType() == TrafficType.Guest) {
 +                        return _configMgr.getNetworkOfferingNetworkRate(nw.getNetworkOfferingId(), network.getDataCenterId());
 +                    }
 +                }
 +            }
 +            if (vm.getType() == Type.ConsoleProxy || vm.getType() == Type.SecondaryStorageVm) {
 +                return -1;
 +            }
 +        }
 +        if (ntwkOff != null) {
 +            return _configMgr.getNetworkOfferingNetworkRate(ntwkOff.getId(), network.getDataCenterId());
 +        }
 +        final Integer networkRate = NetworkOrchestrationService.NetworkThrottlingRate.valueIn(network.getDataCenterId());
 +        if (networkRate != null && networkRate > 0) {
 +            return networkRate;
 +        }
 +        return -1;
 +    }
 +
 +    @Override
 +    public String getAccountNetworkDomain(long accountId, long zoneId) {
 +        String networkDomain = _accountDao.findById(accountId).getNetworkDomain();
 +
 +        if (networkDomain == null) {
 +            // get domain level network domain
 +            return getDomainNetworkDomain(_accountDao.findById(accountId).getDomainId(), zoneId);
 +        }
 +
 +        return networkDomain;
 +    }
 +
 +    @Override
 +    public String getStartIpAddress(long networkId) {
 +        List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
 +        if (vlans.isEmpty()) {
 +            return null;
 +        }
 +
 +        String startIP = vlans.get(0).getIpRange().split("-")[0];
 +
 +        for (VlanVO vlan : vlans) {
 +            String startIP1 = vlan.getIpRange().split("-")[0];
 +            long startIPLong = NetUtils.ip2Long(startIP);
 +            long startIPLong1 = NetUtils.ip2Long(startIP1);
 +
 +            if (startIPLong1 < startIPLong) {
 +                startIP = startIP1;
 +            }
 +        }
 +
 +        return startIP;
 +    }
 +
 +    @Override
 +    public Long getPodIdForVlan(long vlanDbId) {
 +        PodVlanMapVO podVlanMaps = _podVlanMapDao.listPodVlanMapsByVlan(vlanDbId);
 +        if (podVlanMaps == null) {
 +            return null;
 +        } else {
 +            return podVlanMaps.getPodId();
 +        }
 +    }
 +
 +    @Override
 +    public Map<Service, Set<Provider>> getNetworkOfferingServiceProvidersMap(long networkOfferingId) {
 +        Map<Service, Set<Provider>> serviceProviderMap = new HashMap<Service, Set<Provider>>();
 +        List<NetworkOfferingServiceMapVO> map = _ntwkOfferingSrvcDao.listByNetworkOfferingId(networkOfferingId);
 +
 +        for (NetworkOfferingServiceMapVO instance : map) {
 +            String service = instance.getService();
 +            Set<Provider> providers;
 +            providers = serviceProviderMap.get(Service.getService(service));
 +            if (providers == null) {
 +                providers = new HashSet<Provider>();
 +            }
 +            providers.add(Provider.getProvider(instance.getProvider()));
 +            serviceProviderMap.put(Service.getService(service), providers);
 +        }
 +
 +        return serviceProviderMap;
 +    }
 +
 +    @Override
 +    public boolean isProviderSupportServiceInNetwork(long networkId, Service service, Provider provider) {
 +        return _ntwkSrvcDao.canProviderSupportServiceInNetwork(networkId, service, provider);
 +    }
 +
 +    @Override
 +    public List<? extends Provider> listSupportedNetworkServiceProviders(String serviceName) {
 +        Network.Service service = null;
 +        if (serviceName != null) {
 +            service = Network.Service.getService(serviceName);
 +            if (service == null) {
 +                throw new InvalidParameterValueException("Invalid Network Service=" + serviceName);
 +            }
 +        }
 +
 +        Set<Provider> supportedProviders = new HashSet<Provider>();
 +
 +        if (service != null) {
 +            List<Provider> providers = s_serviceToImplementedProvidersMap.get(service);
 +            if (providers != null && !providers.isEmpty()) {
 +                supportedProviders.addAll(providers);
 +            }
 +        } else {
 +            for (List<Provider> pList : s_serviceToImplementedProvidersMap.values()) {
 +                supportedProviders.addAll(pList);
 +            }
 +        }
 +
 +        return new ArrayList<Provider>(supportedProviders);
 +    }
 +
 +    @Override
 +    public Provider getDefaultUniqueProviderForService(String serviceName) {
 +        List<? extends Provider> providers = listSupportedNetworkServiceProviders(serviceName);
 +        if (providers.isEmpty()) {
 +            throw new CloudRuntimeException("No providers supporting service " + serviceName + " found in cloudStack");
 +        }
 +        if (providers.size() > 1) {
 +            throw new CloudRuntimeException("More than 1 provider supporting service " + serviceName + " found in cloudStack");
 +        }
 +
 +        return providers.get(0);
 +    }
 +
 +    @Override
 +    public long findPhysicalNetworkId(long zoneId, String tag, TrafficType trafficType) {
 +        List<PhysicalNetworkVO> pNtwks = new ArrayList<PhysicalNetworkVO>();
 +        if (trafficType != null) {
 +            pNtwks = _physicalNetworkDao.listByZoneAndTrafficType(zoneId, trafficType);
 +        } else {
 +            pNtwks = _physicalNetworkDao.listByZone(zoneId);
 +        }
 +
 +        if (pNtwks.isEmpty()) {
 +            throw new InvalidParameterValueException("Unable to find physical network in zone id=" + zoneId);
 +        }
 +
 +        if (pNtwks.size() > 1) {
 +            if (tag == null) {
 +                throw new InvalidParameterValueException("More than one physical networks exist in zone id=" + zoneId +
 +                    " and no tags are specified in order to make a choice");
 +            }
 +
 +            Long pNtwkId = null;
 +            for (PhysicalNetwork pNtwk : pNtwks) {
 +                if (pNtwk.getTags().contains(tag)) {
 +                    s_logger.debug("Found physical network id=" + pNtwk.getId() + " based on requested tags " + tag);
 +                    pNtwkId = pNtwk.getId();
 +                    break;
 +                }
 +            }
 +            if (pNtwkId == null) {
 +                throw new InvalidParameterValueException("Unable to find physical network which match the tags " + tag);
 +            }
 +            return pNtwkId;
 +        } else {
 +            return pNtwks.get(0).getId();
 +        }
 +    }
 +
 +    @Override
 +    public List<Long> listNetworkOfferingsForUpgrade(long networkId) {
 +        List<Long> offeringsToReturn = new ArrayList<Long>();
 +        NetworkOffering originalOffering = _entityMgr.findById(NetworkOffering.class, getNetwork(networkId).getNetworkOfferingId());
 +
 +        boolean securityGroupSupportedByOriginalOff = areServicesSupportedByNetworkOffering(originalOffering.getId(), Service.SecurityGroup);
 +
 +        // security group supported property should be the same
 +
 +        List<Long> offerings = _networkOfferingDao.getOfferingIdsToUpgradeFrom(originalOffering);
 +
 +        for (Long offeringId : offerings) {
 +            if (areServicesSupportedByNetworkOffering(offeringId, Service.SecurityGroup) == securityGroupSupportedByOriginalOff) {
 +                offeringsToReturn.add(offeringId);
 +            }
 +        }
 +
 +        return offeringsToReturn;
 +    }
 +
 +    @Override
 +    public boolean isSecurityGroupSupportedInNetwork(Network network) {
 +        if (network.getTrafficType() != TrafficType.Guest) {
 +            s_logger.trace("Security group can be enabled for Guest networks only; and network " + network + " has a diff traffic type");
 +            return false;
 +        }
 +
 +        Long physicalNetworkId = network.getPhysicalNetworkId();
 +
 +        // physical network id can be null in Guest Network in Basic zone, so locate the physical network
 +        if (physicalNetworkId == null) {
 +            physicalNetworkId = findPhysicalNetworkId(network.getDataCenterId(), null, null);
 +        }
 +
 +        return isServiceEnabledInNetwork(physicalNetworkId, network.getId(), Service.SecurityGroup);
 +    }
 +
 +    @Override
 +    public PhysicalNetwork getDefaultPhysicalNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) {
 +
 +        List<PhysicalNetworkVO> networkList = _physicalNetworkDao.listByZoneAndTrafficType(zoneId, trafficType);
 +        DataCenter dc = ApiDBUtils.findZoneById(zoneId);
 +        String dcUuid = String.valueOf(zoneId);
 +        if (dc != null) {
 +            dcUuid = dc.getUuid();
 +        }
 +
 +        if (networkList.isEmpty()) {
 +            InvalidParameterValueException ex =
 +                new InvalidParameterValueException("Unable to find the default physical network with traffic=" + trafficType + " in the specified zone id");
 +            ex.addProxyObject(dcUuid, "zoneId");
 +            throw ex;
 +        }
 +
 +        if (networkList.size() > 1) {
 +            InvalidParameterValueException ex =
 +                new InvalidParameterValueException("More than one physical networks exist in zone id=" + zoneId + " with traffic type=" + trafficType);
 +            ex.addProxyObject(dcUuid, "zoneId");
 +            throw ex;
 +        }
 +
 +        return networkList.get(0);
 +    }
 +
 +    @Override
 +    public String getDefaultManagementTrafficLabel(long zoneId, HypervisorType hypervisorType) {
 +        try {
 +            PhysicalNetwork mgmtPhyNetwork = getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Management);
 +            PhysicalNetworkTrafficTypeVO mgmtTraffic = _pNTrafficTypeDao.findBy(mgmtPhyNetwork.getId(), TrafficType.Management);
 +            if (mgmtTraffic != null) {
 +                String label = null;
 +                switch (hypervisorType) {
 +                    case XenServer:
 +                        label = mgmtTraffic.getXenNetworkLabel();
 +                        break;
 +                    case KVM:
 +                        label = mgmtTraffic.getKvmNetworkLabel();
 +                        break;
 +                    case VMware:
 +                        label = mgmtTraffic.getVmwareNetworkLabel();
 +                        break;
 +                    case Hyperv:
 +                        label = mgmtTraffic.getHypervNetworkLabel();
 +                        break;
 +                    case Ovm3:
 +                        label = mgmtTraffic.getOvm3NetworkLabel();
 +                        break;
 +                }
 +                return label;
 +            }
 +        } catch (Exception ex) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Failed to retrive the default label for management traffic:" + "zone: " + zoneId + " hypervisor: " + hypervisorType + " due to:" +
 +                    ex.getMessage());
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public String getDefaultStorageTrafficLabel(long zoneId, HypervisorType hypervisorType) {
 +        try {
 +            PhysicalNetwork storagePhyNetwork = getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Storage);
 +            PhysicalNetworkTrafficTypeVO storageTraffic = _pNTrafficTypeDao.findBy(storagePhyNetwork.getId(), TrafficType.Storage);
 +            if (storageTraffic != null) {
 +                String label = null;
 +                switch (hypervisorType) {
 +                    case XenServer:
 +                        label = storageTraffic.getXenNetworkLabel();
 +                        break;
 +                    case KVM:
 +                        label = storageTraffic.getKvmNetworkLabel();
 +                        break;
 +                    case VMware:
 +                        label = storageTraffic.getVmwareNetworkLabel();
 +                        break;
 +                    case Hyperv:
 +                        label = storageTraffic.getHypervNetworkLabel();
 +                        break;
 +                    case Ovm3:
 +                        label = storageTraffic.getOvm3NetworkLabel();
 +                        break;
 +                }
 +                return label;
 +            }
 +        } catch (Exception ex) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Failed to retrive the default label for storage traffic:" + "zone: " + zoneId + " hypervisor: " + hypervisorType + " due to:" +
 +                    ex.getMessage());
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public List<PhysicalNetworkSetupInfo> getPhysicalNetworkInfo(long dcId, HypervisorType hypervisorType) {
 +        List<PhysicalNetworkSetupInfo> networkInfoList = new ArrayList<PhysicalNetworkSetupInfo>();
 +        List<PhysicalNetworkVO> physicalNtwkList = _physicalNetworkDao.listByZone(dcId);
 +        for (PhysicalNetworkVO pNtwk : physicalNtwkList) {
 +            String publicName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Public, hypervisorType);
 +            String privateName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Management, hypervisorType);
 +            String guestName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Guest, hypervisorType);
 +            String storageName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Storage, hypervisorType);
 +            // String controlName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Control, hypervisorType);
 +            PhysicalNetworkSetupInfo info = new PhysicalNetworkSetupInfo();
 +            info.setPhysicalNetworkId(pNtwk.getId());
 +            info.setGuestNetworkName(guestName);
 +            info.setPrivateNetworkName(privateName);
 +            info.setPublicNetworkName(publicName);
 +            info.setStorageNetworkName(storageName);
 +            PhysicalNetworkTrafficTypeVO mgmtTraffic = _pNTrafficTypeDao.findBy(pNtwk.getId(), TrafficType.Management);
 +            if (mgmtTraffic != null) {
 +                String vlan = mgmtTraffic.getVlan();
 +                info.setMgmtVlan(vlan);
 +            }
 +            networkInfoList.add(info);
 +        }
 +        return networkInfoList;
 +    }
 +
 +    @Override
 +    public boolean isProviderEnabledInPhysicalNetwork(long physicalNetowrkId, String providerName) {
 +        PhysicalNetworkServiceProviderVO ntwkSvcProvider = _pNSPDao.findByServiceProvider(physicalNetowrkId, providerName);
 +        if (ntwkSvcProvider == null) {
 +            s_logger.warn("Unable to find provider " + providerName + " in physical network id=" + physicalNetowrkId);
 +            return false;
 +        }
 +        return isProviderEnabled(ntwkSvcProvider);
 +    }
 +
 +    @Override
 +    public boolean isProviderEnabledInZone(long zoneId, String provider) {
 +        //the provider has to be enabled at least in one network in the zone
 +        for (PhysicalNetwork pNtwk : _physicalNetworkDao.listByZone(zoneId)) {
 +            if (isProviderEnabledInPhysicalNetwork(pNtwk.getId(), provider)) {
 +                return true;
 +            }
 +        }
 +
 +        return false;
 +    }
 +
 +    @Override
 +    public String getNetworkTag(HypervisorType hType, Network network) {
 +        // no network tag for control traffic type
 +        TrafficType effectiveTrafficType = network.getTrafficType();
 +        if (hType == HypervisorType.VMware && effectiveTrafficType == TrafficType.Control)
 +            effectiveTrafficType = TrafficType.Management;
 +
 +        if (effectiveTrafficType == TrafficType.Control) {
 +            return null;
 +        }
 +
 +        Long physicalNetworkId = null;
 +        if (effectiveTrafficType != TrafficType.Guest) {
 +            physicalNetworkId = getNonGuestNetworkPhysicalNetworkId(network, effectiveTrafficType);
 +        } else {
 +            NetworkOffering offering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
 +            physicalNetworkId = network.getPhysicalNetworkId();
 +            if (physicalNetworkId == null) {
 +                physicalNetworkId = findPhysicalNetworkId(network.getDataCenterId(), offering.getTags(), offering.getTrafficType());
 +            }
 +        }
 +
 +        if (physicalNetworkId == null) {
 +            assert (false) : "Can't get the physical network";
 +            s_logger.warn("Can't get the physical network");
 +            return null;
 +        }
 +
 +        return _pNTrafficTypeDao.getNetworkTag(physicalNetworkId, effectiveTrafficType, hType);
 +    }
 +
 +    @Override
 +    public NetworkVO getExclusiveGuestNetwork(long zoneId) {
 +        List<NetworkVO> networks = _networksDao.listBy(Account.ACCOUNT_ID_SYSTEM, zoneId, GuestType.Shared, TrafficType.Guest);
 +        if (networks == null || networks.isEmpty()) {
 +            throw new InvalidParameterValueException("Unable to find network with trafficType " + TrafficType.Guest + " and guestType " + GuestType.Shared + " in zone " +
 +                zoneId);
 +        }
 +
 +        if (networks.size() > 1) {
 +            throw new InvalidParameterValueException("Found more than 1 network with trafficType " + TrafficType.Guest + " and guestType " + GuestType.Shared +
 +                " in zone " + zoneId);
 +
 +        }
 +
 +        return networks.get(0);
 +    }
 +
 +    @Override
 +    public boolean isNetworkSystem(Network network) {
 +        NetworkOffering no = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId());
 +        if (no.isSystemOnly()) {
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public Long getPhysicalNetworkId(Network network) {
 +        if (network.getTrafficType() != TrafficType.Guest) {
 +            return getNonGuestNetworkPhysicalNetworkId(network);
 +        }
 +
 +        Long physicalNetworkId = network.getPhysicalNetworkId();
 +        NetworkOffering offering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
 +        if (physicalNetworkId == null) {
 +            physicalNetworkId = findPhysicalNetworkId(network.getDataCenterId(), offering.getTags(), offering.getTrafficType());
 +        }
 +        return physicalNetworkId;
 +    }
 +
 +    @Override
 +    public boolean getAllowSubdomainAccessGlobal() {
 +        return _allowSubdomainNetworkAccess;
 +    }
 +
 +    @Override
 +    public boolean isProviderForNetwork(Provider provider, long networkId) {
 +        if (_ntwkSrvcDao.isProviderForNetwork(networkId, provider) != null) {
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public boolean isProviderForNetworkOffering(Provider provider, long networkOfferingId) {
 +        if (_ntwkOfferingSrvcDao.isProviderForNetworkOffering(networkOfferingId, provider)) {
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public void canProviderSupportServices(Map<Provider, Set<Service>> providersMap) {
 +        for (Provider provider : providersMap.keySet()) {
 +            // check if services can be turned off
 +            NetworkElement element = getElementImplementingProvider(provider.getName());
 +            if (element == null) {
 +                throw new InvalidParameterValueException("Unable to find the Network Element implementing the Service Provider '" + provider.getName() + "'");
 +            }
 +
 +            Set<Service> enabledServices = new HashSet<Service>();
 +            enabledServices.addAll(providersMap.get(provider));
 +
 +            if (enabledServices != null && !enabledServices.isEmpty()) {
 +                if (!element.canEnableIndividualServices()) {
 +                    Set<Service> requiredServices = new HashSet<Service>();
 +                    requiredServices.addAll(element.getCapabilities().keySet());
 +
 +                    if (requiredServices.contains(Network.Service.Gateway)) {
 +                        requiredServices.remove(Network.Service.Gateway);
 +                    }
 +
 +                    if (requiredServices.contains(Network.Service.Firewall)) {
 +                        requiredServices.remove(Network.Service.Firewall);
 +                    }
 +
 +                    if (enabledServices.contains(Network.Service.Firewall)) {
 +                        enabledServices.remove(Network.Service.Firewall);
 +                    }
 +
 +                    // exclude gateway service
 +                    if (enabledServices.size() != requiredServices.size()) {
 +                        StringBuilder servicesSet = new StringBuilder();
 +
 +                        for (Service requiredService : requiredServices) {
 +                            // skip gateway service as we don't allow setting it via API
 +                            if (requiredService == Service.Gateway) {
 +                                continue;
 +                            }
 +                            servicesSet.append(requiredService.getName() + ", ");
 +                        }
 +                        servicesSet.delete(servicesSet.toString().length() - 2, servicesSet.toString().length());
 +
 +                        throw new InvalidParameterValueException("Cannot enable subset of Services, Please specify the complete list of Services: " +
 +                            servicesSet.toString() + "  for Service Provider " + provider.getName());
 +                    }
 +                }
 +                List<String> serviceList = new ArrayList<String>();
 +                for (Service service : enabledServices) {
 +                    // check if the service is provided by this Provider
 +                    if (!element.getCapabilities().containsKey(service)) {
 +                        throw new UnsupportedServiceException(provider.getName() + " Provider cannot provide service " + service.getName());
 +                    }
 +                    serviceList.add(service.getName());
 +                }
 +                if (!element.verifyServicesCombination(enabledServices)) {
 +                    throw new UnsupportedServiceException("Provider " + provider.getName() + " doesn't support services combination: " + serviceList);
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean canAddDefaultSecurityGroup() {
 +        String defaultAdding = _configDao.getValue(Config.SecurityGroupDefaultAdding.key());
 +        return (defaultAdding != null && defaultAdding.equalsIgnoreCase("true"));
 +    }
 +
 +    @Override
 +    public List<Service> listNetworkOfferingServices(long networkOfferingId) {
 +        List<Service> services = new ArrayList<Service>();
 +        List<String> servicesStr = _ntwkOfferingSrvcDao.listServicesForNetworkOffering(networkOfferingId);
 +        for (String serviceStr : servicesStr) {
 +            services.add(Service.getService(serviceStr));
 +        }
 +
 +        return services;
 +    }
 +
 +    @Override
 +    public boolean areServicesEnabledInZone(long zoneId, NetworkOffering offering, List<Service> services) {
 +        long physicalNtwkId = findPhysicalNetworkId(zoneId, offering.getTags(), offering.getTrafficType());
 +        boolean result = true;
 +        List<String> checkedProvider = new ArrayList<String>();
 +        for (Service service : services) {
 +            // get all the providers, and check if each provider is enabled
 +            List<String> providerNames = _ntwkOfferingSrvcDao.listProvidersForServiceForNetworkOffering(offering.getId(), service);
 +            for (String providerName : providerNames) {
 +                if (!checkedProvider.contains(providerName)) {
 +                    result = result && isProviderEnabledInPhysicalNetwork(physicalNtwkId, providerName);
 +                }
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean checkIpForService(IpAddress userIp, Service service, Long networkId) {
 +        if (networkId == null) {
 +            networkId = userIp.getAssociatedWithNetworkId();
 +        }
 +
 +        NetworkVO network = _networksDao.findById(networkId);
 +        NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +        if (offering.getGuestType() != GuestType.Isolated) {
 +            return true;
 +        }
 +        IPAddressVO ipVO = _ipAddressDao.findById(userIp.getId());
 +        PublicIp publicIp = PublicIp.createFromAddrAndVlan(ipVO, _vlanDao.findById(userIp.getVlanId()));
 +        if (!canIpUsedForService(publicIp, service, networkId)) {
 +            return false;
 +        }
 +        if (!offering.isConserveMode()) {
 +            return canIpUsedForNonConserveService(publicIp, service);
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean providerSupportsCapability(Set<Provider> providers, Service service, Capability cap) {
 +        for (Provider provider : providers) {
 +            NetworkElement element = getElementImplementingProvider(provider.getName());
 +            if (element != null) {
 +                Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
 +                if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
 +                    throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
 +                            " implementing Provider=" + provider.getName());
 +                }
 +                Map<Capability, String> serviceCapabilities = elementCapabilities.get(service);
 +                if (serviceCapabilities == null || serviceCapabilities.isEmpty()) {
 +                    throw new UnsupportedServiceException("Service " + service.getName() + " doesn't have capabilites for element=" + element.getName() +
 +                            " implementing Provider=" + provider.getName());
 +                }
 +
 +                if (serviceCapabilities.containsKey(cap)) {
 +                    return true;
 +                }
 +            } else {
 +                throw new UnsupportedServiceException("Unable to find network element for provider " + provider.getName());
 +            }
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public void checkCapabilityForProvider(Set<Provider> providers, Service service, Capability cap, String capValue) {
 +        for (Provider provider : providers) {
 +            NetworkElement element = getElementImplementingProvider(provider.getName());
 +            if (element != null) {
 +                Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
 +                if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
 +                    throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
 +                        " implementing Provider=" + provider.getName());
 +                }
 +                Map<Capability, String> serviceCapabilities = elementCapabilities.get(service);
 +                if (serviceCapabilities == null || serviceCapabilities.isEmpty()) {
 +                    throw new UnsupportedServiceException("Service " + service.getName() + " doesn't have capabilities for element=" + element.getName() +
 +                        " implementing Provider=" + provider.getName());
 +                }
 +
 +                String value = serviceCapabilities.get(cap);
 +                if (value == null || value.isEmpty()) {
 +                    throw new UnsupportedServiceException("Service " + service.getName() + " doesn't have capability " + cap.getName() + " for element=" +
 +                        element.getName() + " implementing Provider=" + provider.getName());
 +                }
 +
 +                if (!value.toLowerCase().contains(capValue.toLowerCase())) {
 +                    throw new UnsupportedServiceException("Service " + service.getName() + " doesn't support value " + capValue + " for capability " + cap.getName() +
 +                        " for element=" + element.getName() + " implementing Provider=" + provider.getName());
 +                }
 +            } else {
 +                throw new UnsupportedServiceException("Unable to find network element for provider " + provider.getName());
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void checkNetworkPermissions(Account owner, Network network) {
 +        // dahn 20140310: I was thinking of making this an assert but
 +        //                as we hardly ever test with asserts I think
 +        //                we better make sure at runtime.
 +        if (network == null) {
 +            throw new CloudRuntimeException("cannot check permissions on (Network) <null>");
 +        }
 +        // Perform account permission check
 +        if ((network.getGuestType() != GuestType.Shared && network.getGuestType() != GuestType.L2) ||
 +                (network.getGuestType() == GuestType.Shared && network.getAclType() == ACLType.Account)) {
 +            AccountVO networkOwner = _accountDao.findById(network.getAccountId());
 +            if (networkOwner == null)
 +                throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() +
 +                    ", network does not have an owner");
 +            if (owner.getType() != Account.ACCOUNT_TYPE_PROJECT && networkOwner.getType() == Account.ACCOUNT_TYPE_PROJECT) {
 +                if (!_projectAccountDao.canAccessProjectAccount(owner.getAccountId(), network.getAccountId())) {
 +                    throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() +
 +                        ", permission denied");
 +                }
 +            } else {
 +                List<NetworkVO> networkMap = _networksDao.listBy(owner.getId(), network.getId());
 +                if (networkMap == null || networkMap.isEmpty()) {
 +                    throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() +
 +                        ", permission denied");
 +                }
 +            }
 +
 +        } else {
 +            if (!isNetworkAvailableInDomain(network.getId(), owner.getDomainId())) {
 +                DomainVO ownerDomain = _domainDao.findById(owner.getDomainId());
 +                if (ownerDomain == null) {
 +                    throw new CloudRuntimeException("cannot check permission on account " + owner.getAccountName() + " whose domain does not exist");
 +                }
 +                throw new PermissionDeniedException("Shared network id=" + ((NetworkVO)network).getUuid() + " is not available in domain id=" +
 +                        ownerDomain.getUuid());
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public String getDefaultPublicTrafficLabel(long dcId, HypervisorType hypervisorType) {
 +        try {
 +            PhysicalNetwork publicPhyNetwork = getOnePhysicalNetworkByZoneAndTrafficType(dcId, TrafficType.Public);
 +            PhysicalNetworkTrafficTypeVO publicTraffic = _pNTrafficTypeDao.findBy(publicPhyNetwork.getId(), TrafficType.Public);
 +            if (publicTraffic != null) {
 +                String label = null;
 +                switch (hypervisorType) {
 +                    case XenServer:
 +                        label = publicTraffic.getXenNetworkLabel();
 +                        break;
 +                    case KVM:
 +                        label = publicTraffic.getKvmNetworkLabel();
 +                        break;
 +                    case VMware:
 +                        label = publicTraffic.getVmwareNetworkLabel();
 +                        break;
 +                    case Hyperv:
 +                        label = publicTraffic.getHypervNetworkLabel();
 +                        break;
 +                    case Ovm3:
 +                        label = publicTraffic.getOvm3NetworkLabel();
 +                        break;
 +                }
 +                return label;
 +            }
 +        } catch (Exception ex) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Failed to retrieve the default label for public traffic." + "zone: " + dcId + " hypervisor: " + hypervisorType + " due to: " +
 +                    ex.getMessage());
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public String getDefaultGuestTrafficLabel(long dcId, HypervisorType hypervisorType) {
 +        try {
 +            PhysicalNetwork guestPhyNetwork = getOnePhysicalNetworkByZoneAndTrafficType(dcId, TrafficType.Guest);
 +            PhysicalNetworkTrafficTypeVO guestTraffic = _pNTrafficTypeDao.findBy(guestPhyNetwork.getId(), TrafficType.Guest);
 +            if (guestTraffic != null) {
 +                String label = null;
 +                switch (hypervisorType) {
 +                    case XenServer:
 +                        label = guestTraffic.getXenNetworkLabel();
 +                        break;
 +                    case KVM:
 +                        label = guestTraffic.getKvmNetworkLabel();
 +                        break;
 +                    case VMware:
 +                        label = guestTraffic.getVmwareNetworkLabel();
 +                        break;
 +                    case Hyperv:
 +                        label = guestTraffic.getHypervNetworkLabel();
 +                        break;
 +                    case Ovm3:
 +                        label = guestTraffic.getOvm3NetworkLabel();
 +                        break;
 +                }
 +                return label;
 +            }
 +        } catch (Exception ex) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Failed to retrive the default label for guest traffic:" + "zone: " + dcId + " hypervisor: " + hypervisorType + " due to:" +
 +                    ex.getMessage());
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public List<? extends Network> listNetworksByVpc(long vpcId) {
 +        return _networksDao.listByVpc(vpcId);
 +    }
 +
 +    @Override
 +    public List<Provider> getNtwkOffDistinctProviders(long ntkwOffId) {
 +        List<String> providerNames = _ntwkOfferingSrvcDao.getDistinctProviders(ntkwOffId);
 +        List<Provider> providers = new ArrayList<Provider>();
 +        for (String providerName : providerNames) {
 +            providers.add(Network.Provider.getProvider(providerName));
 +        }
 +
 +        return providers;
 +    }
 +
 +    @Override
 +    public boolean isVmPartOfNetwork(long vmId, long ntwkId) {
 +        if (_nicDao.findNonReleasedByInstanceIdAndNetworkId(ntwkId, vmId) != null) {
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public List<? extends PhysicalNetwork> getPhysicalNtwksSupportingTrafficType(long zoneId, TrafficType trafficType) {
 +
 +        List<? extends PhysicalNetwork> pNtwks = _physicalNetworkDao.listByZone(zoneId);
 +
 +        Iterator<? extends PhysicalNetwork> it = pNtwks.iterator();
 +        while (it.hasNext()) {
 +            PhysicalNetwork pNtwk = it.next();
 +            if (!_pNTrafficTypeDao.isTrafficTypeSupported(pNtwk.getId(), trafficType)) {
 +                it.remove();
 +            }
 +        }
 +        return pNtwks;
 +    }
 +
 +    @Override
 +    public boolean isPrivateGateway(long ntwkId) {
 +        Network network = getNetwork(ntwkId);
 +        if (network.getTrafficType() != TrafficType.Guest || network.getNetworkOfferingId() != s_privateOfferingId.longValue()) {
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public List<NetworkOfferingVO> getSystemAccountNetworkOfferings(String... offeringNames) {
 +        List<NetworkOfferingVO> offerings = new ArrayList<NetworkOfferingVO>(offeringNames.length);
 +        for (String offeringName : offeringNames) {
 +            NetworkOfferingVO network = _systemNetworks.get(offeringName);
 +            if (network == null) {
 +                throw new CloudRuntimeException("Unable to find system network profile for " + offeringName);
 +            }
 +            offerings.add(network);
 +        }
 +        return offerings;
 +    }
 +
 +    @Override
 +    public boolean isNetworkAvailableInDomain(long networkId, long domainId) {
 +        Long networkDomainId = null;
 +        Network network = getNetwork(networkId);
 +        if (network.getGuestType() != GuestType.Shared && network.getGuestType() != GuestType.L2) {
 +            s_logger.trace("Network id=" + networkId + " is not shared or L2");
 +            return false;
 +        }
 +
 +        NetworkDomainVO networkDomainMap = _networkDomainDao.getDomainNetworkMapByNetworkId(networkId);
 +        if (networkDomainMap == null) {
 +            s_logger.trace("Network id=" + networkId + " is shared or L2, but not domain specific");
 +            return true;
 +        } else {
 +            networkDomainId = networkDomainMap.getDomainId();
 +        }
 +
 +        if (domainId == networkDomainId.longValue()) {
 +            return true;
 +        }
 +
 +        if (networkDomainMap.subdomainAccess) {
 +            Set<Long> parentDomains = _domainMgr.getDomainParentIds(domainId);
 +
 +            if (parentDomains.contains(networkDomainId)) {
 +                return true;
 +            }
 +        }
 +
 +        return false;
 +    }
 +
 +    @Override
 +    public Set<Long> getAvailableIps(Network network, String requestedIp) {
 +        if (network.getCidr() == null) {
 +            return Collections.emptySet();
 +        }
 +        String[] cidr = network.getCidr().split("/");
 +        List<String> ips = getUsedIpsInNetwork(network);
 +        Set<Long> usedIps = new TreeSet<Long>();
 +
 +        for (String ip : ips) {
 +            if (requestedIp != null && requestedIp.equals(ip)) {
 +                s_logger.warn("Requested ip address " + requestedIp + " is already in use in network" + network);
 +                return null;
 +            }
 +
 +            usedIps.add(NetUtils.ip2Long(ip));
 +        }
 +
 +        Set<Long> allPossibleIps = NetUtils.getAllIpsFromCidr(cidr[0], Integer.parseInt(cidr[1]), usedIps);
 +
 +        String gateway = network.getGateway();
 +        if ((gateway != null) && (allPossibleIps.contains(NetUtils.ip2Long(gateway))))
 +            allPossibleIps.remove(NetUtils.ip2Long(gateway));
 +
 +        return allPossibleIps;
 +    }
 +
 +    @Override
 +    public List<String> getUsedIpsInNetwork(Network network) {
 +        //Get all ips used by vms nics
 +        List<String> ips = _nicDao.listIpAddressInNetwork(network.getId());
 +        //Get all secondary ips for nics
 +        List<String> secondaryIps = _nicSecondaryIpDao.listSecondaryIpAddressInNetwork(network.getId());
 +        ips.addAll(secondaryIps);
 +        //Get ips used by load balancers
 +        List<String> lbIps = _appLbRuleDao.listLbIpsBySourceIpNetworkId(network.getId());
 +        ips.addAll(lbIps);
 +        return ips;
 +    }
 +
 +    @Override
 +    public String getDomainNetworkDomain(long domainId, long zoneId) {
 +        String networkDomain = null;
 +        Long searchDomainId = domainId;
 +        while (searchDomainId != null) {
 +            DomainVO domain = _domainDao.findById(searchDomainId);
 +            if (domain.getNetworkDomain() != null) {
 +                networkDomain = domain.getNetworkDomain();
 +                break;
 +            }
 +            searchDomainId = domain.getParent();
 +        }
 +        if (networkDomain == null) {
 +            return getZoneNetworkDomain(zoneId);
 +        }
 +        return networkDomain;
 +    }
 +
 +    boolean isProviderEnabled(PhysicalNetworkServiceProvider provider) {
 +        if (provider == null || provider.getState() != PhysicalNetworkServiceProvider.State.Enabled) { // TODO: check
 +            // for other states: Shutdown?
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    boolean isServiceEnabledInNetwork(long physicalNetworkId, long networkId, Service service) {
 +        // check if the service is supported in the network
 +        if (!areServicesSupportedInNetwork(networkId, service)) {
 +            s_logger.debug("Service " + service.getName() + " is not supported in the network id=" + networkId);
 +            return false;
 +        }
 +
 +        // get provider for the service and check if all of them are supported
 +        String provider = _ntwkSrvcDao.getProviderForServiceInNetwork(networkId, service);
 +        if (!isProviderEnabledInPhysicalNetwork(physicalNetworkId, provider)) {
 +            s_logger.debug("Provider " + provider + " is not enabled in physical network id=" + physicalNetworkId);
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public Nic getNicInNetworkIncludingRemoved(long vmId, long networkId) {
 +        return _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkId, vmId);
 +    }
 +
 +    String getZoneNetworkDomain(long zoneId) {
 +        return _dcDao.findById(zoneId).getDomain();
 +    }
 +
 +    PhysicalNetwork getOnePhysicalNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) {
 +        List<PhysicalNetworkVO> networkList = _physicalNetworkDao.listByZoneAndTrafficType(zoneId, trafficType);
 +
 +        if (networkList.isEmpty()) {
 +            throw new InvalidParameterValueException("Unable to find the default physical network with traffic=" + trafficType + " in zone id=" + zoneId + ". ");
 +        }
 +
 +        if (networkList.size() > 1) {
 +            s_logger.info("More than one physical networks exist in zone id=" + zoneId + " with traffic type=" + trafficType + ". ");
 +        }
 +
 +        return networkList.get(0);
 +    }
 +
 +    protected Long getNonGuestNetworkPhysicalNetworkId(Network network, TrafficType trafficType) {
 +        // VMware control network is management network
 +        // we need to retrieve traffic label information through physical network
 +        Long physicalNetworkId = network.getPhysicalNetworkId();
 +
 +        if (physicalNetworkId == null) {
 +            List<PhysicalNetworkVO> pNtwks = _physicalNetworkDao.listByZone(network.getDataCenterId());
 +            if (pNtwks.size() == 1) {
 +                physicalNetworkId = pNtwks.get(0).getId();
 +            } else {
 +                // locate physicalNetwork with supported traffic type
 +                // We can make this assumptions based on the fact that Public/Management/Control traffic types are
 +                // supported only in one physical network in the zone in 3.0
 +                for (PhysicalNetworkVO pNtwk : pNtwks) {
 +                    if (_pNTrafficTypeDao.isTrafficTypeSupported(pNtwk.getId(), trafficType)) {
 +                        physicalNetworkId = pNtwk.getId();
 +                        break;
 +                    }
 +                }
 +            }
 +        }
 +        return physicalNetworkId;
 +    }
 +
 +    protected Long getNonGuestNetworkPhysicalNetworkId(Network network) {
 +        // no physical network for control traffic type
 +
 +        // have to remove this sanity check as VMware control network is management network
 +        // we need to retrieve traffic label information through physical network
 +        /*
 +                if (network.getTrafficType() == TrafficType.Control) {
 +                    return null;
 +                }
 +        */
 +        Long physicalNetworkId = network.getPhysicalNetworkId();
 +
 +        if (physicalNetworkId == null) {
 +            List<PhysicalNetworkVO> pNtwks = _physicalNetworkDao.listByZone(network.getDataCenterId());
 +            if (pNtwks.size() == 1) {
 +                physicalNetworkId = pNtwks.get(0).getId();
 +            } else {
 +                // locate physicalNetwork with supported traffic type
 +                // We can make this assumptions based on the fact that Public/Management/Control traffic types are
 +                // supported only in one physical network in the zone in 3.0
 +                for (PhysicalNetworkVO pNtwk : pNtwks) {
 +                    if (_pNTrafficTypeDao.isTrafficTypeSupported(pNtwk.getId(), network.getTrafficType())) {
 +                        physicalNetworkId = pNtwk.getId();
 +                        break;
 +                    }
 +                }
 +            }
 +        }
 +        return physicalNetworkId;
 +    }
 +
 +    @Override
 +    public NicProfile getNicProfile(VirtualMachine vm, long networkId, String broadcastUri) {
 +        NicVO nic = null;
 +        if (broadcastUri != null) {
 +            nic = _nicDao.findByNetworkIdInstanceIdAndBroadcastUri(networkId, vm.getId(), broadcastUri);
 +        } else {
 +            nic = _nicDao.findByNtwkIdAndInstanceId(networkId, vm.getId());
 +        }
 +        if (nic == null) {
 +            return null;
 +        }
 +        NetworkVO network = _networksDao.findById(networkId);
 +        Integer networkRate = getNetworkRate(network.getId(), vm.getId());
 +
 +//        NetworkGuru guru = _networkGurus.get(network.getGuruName());
 +        NicProfile profile =
 +            new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, isSecurityGroupSupportedInNetwork(network), getNetworkTag(
 +                vm.getHypervisorType(), network));
 +//        guru.updateNicProfile(profile, network);
 +        return profile;
 +    }
 +
 +    @Override
 +    public boolean networkIsConfiguredForExternalNetworking(long zoneId, long networkId) {
 +        List<Provider> networkProviders = getNetworkProviders(networkId);
 +        for (Provider provider : networkProviders) {
 +            if (provider.isExternal()) {
 +                return true;
 +            }
 +        }
 +        return false;
 +    }
 +
 +    private List<Provider> getNetworkProviders(long networkId) {
 +        List<String> providerNames = _ntwkSrvcDao.getDistinctProviders(networkId);
 +        Map<String, Provider> providers = new HashMap<String, Provider>();
 +        for (String providerName : providerNames) {
 +            if (!providers.containsKey(providerName)) {
 +                providers.put(providerName, Network.Provider.getProvider(providerName));
 +            }
 +        }
 +
 +        return new ArrayList<Provider>(providers.values());
 +    }
 +
 +    @Override
 +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
 +        _configs = _configDao.getConfiguration("Network", params);
 +        _allowSubdomainNetworkAccess = Boolean.valueOf(_configs.get(Config.SubDomainNetworkAccess.key()));
 +        _executeInSequenceNtwkElmtCmd = Boolean.valueOf(_configs.get(Config.ExecuteInSequenceNetworkElementCommands.key()));
 +
 +        NetworkOfferingVO publicNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemPublicNetwork, TrafficType.Public, true);
 +        publicNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(publicNetworkOffering);
 +        _systemNetworks.put(NetworkOffering.SystemPublicNetwork, publicNetworkOffering);
 +        NetworkOfferingVO managementNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemManagementNetwork, TrafficType.Management, false);
 +        managementNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(managementNetworkOffering);
 +        _systemNetworks.put(NetworkOffering.SystemManagementNetwork, managementNetworkOffering);
 +        NetworkOfferingVO controlNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemControlNetwork, TrafficType.Control, false);
 +        controlNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(controlNetworkOffering);
 +        _systemNetworks.put(NetworkOffering.SystemControlNetwork, controlNetworkOffering);
 +        NetworkOfferingVO storageNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemStorageNetwork, TrafficType.Storage, true);
 +        storageNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(storageNetworkOffering);
 +        _systemNetworks.put(NetworkOffering.SystemStorageNetwork, storageNetworkOffering);
 +        NetworkOfferingVO privateGatewayNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOffering, GuestType.Isolated);
 +        privateGatewayNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(privateGatewayNetworkOffering);
 +        _systemNetworks.put(NetworkOffering.SystemPrivateGatewayNetworkOffering, privateGatewayNetworkOffering);
 +        s_privateOfferingId = privateGatewayNetworkOffering.getId();
 +
 +        IpAddressSearch = _ipAddressDao.createSearchBuilder();
 +        IpAddressSearch.and("accountId", IpAddressSearch.entity().getAllocatedToAccountId(), Op.EQ);
 +        IpAddressSearch.and("dataCenterId", IpAddressSearch.entity().getDataCenterId(), Op.EQ);
 +        IpAddressSearch.and("vpcId", IpAddressSearch.entity().getVpcId(), Op.EQ);
 +        IpAddressSearch.and("associatedWithNetworkId", IpAddressSearch.entity().getAssociatedWithNetworkId(), Op.EQ);
 +        SearchBuilder<VlanVO> virtualNetworkVlanSB = _vlanDao.createSearchBuilder();
 +        virtualNetworkVlanSB.and("vlanType", virtualNetworkVlanSB.entity().getVlanType(), Op.EQ);
 +        IpAddressSearch.join("virtualNetworkVlanSB", virtualNetworkVlanSB, IpAddressSearch.entity().getVlanId(), virtualNetworkVlanSB.entity().getId(),
 +            JoinBuilder.JoinType.INNER);
 +        IpAddressSearch.done();
 +
 +        NicForTrafficTypeSearch = _nicDao.createSearchBuilder();
 +        SearchBuilder<NetworkVO> networkSearch = _networksDao.createSearchBuilder();
 +        NicForTrafficTypeSearch.join("network", networkSearch, networkSearch.entity().getId(), NicForTrafficTypeSearch.entity().getNetworkId(), JoinType.INNER);
 +        NicForTrafficTypeSearch.and("instance", NicForTrafficTypeSearch.entity().getInstanceId(), Op.EQ);
 +        networkSearch.and("traffictype", networkSearch.entity().getTrafficType(), Op.EQ);
 +        NicForTrafficTypeSearch.done();
 +
 +        s_logger.info("Network Model is configured.");
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean start() {
 +        // populate s_serviceToImplementedProvidersMap & s_providerToNetworkElementMap with current _networkElements
 +        // Need to do this in start() since _networkElements are not completely configured until then.
 +        for (NetworkElement element : networkElements) {
 +            Map<Service, Map<Capability, String>> capabilities = element.getCapabilities();
 +            Provider implementedProvider = element.getProvider();
 +            if (implementedProvider != null) {
 +                if (s_providerToNetworkElementMap.containsKey(implementedProvider.getName())) {
 +                    s_logger.error("Cannot start NetworkModel: Provider <-> NetworkElement must be a one-to-one map, " + "multiple NetworkElements found for Provider: " +
 +                        implementedProvider.getName());
 +                    continue;
 +                }
 +                s_logger.info("Add provider <-> element map entry. " + implementedProvider.getName() + "-" + element.getName() + "-" + element.getClass().getSimpleName());
 +                s_providerToNetworkElementMap.put(implementedProvider.getName(), element.getName());
 +            }
 +            if (capabilities != null && implementedProvider != null) {
 +                for (Service service : capabilities.keySet()) {
 +                    if (s_serviceToImplementedProvidersMap.containsKey(service)) {
 +                        List<Provider> providers = s_serviceToImplementedProvidersMap.get(service);
 +                        providers.add(implementedProvider);
 +                    } else {
 +                        List<Provider> providers = new ArrayList<Provider>();
 +                        providers.add(implementedProvider);
 +                        s_serviceToImplementedProvidersMap.put(service, providers);
 +                    }
 +                }
 +            }
 +        }
 +        s_logger.info("Started Network Model");
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean stop() {
 +        return true;
 +    }
 +
 +    @Override
 +    public PublicIpAddress getSourceNatIpAddressForGuestNetwork(Account owner, Network guestNetwork) {
 +        List<? extends IpAddress> addrs = listPublicIpsAssignedToGuestNtwk(owner.getId(), guestNetwork.getId(), true);
 +
 +        IPAddressVO sourceNatIp = null;
 +        if (addrs.isEmpty()) {
 +            return null;
 +        } else {
 +            for (IpAddress addr : addrs) {
 +                if (addr.isSourceNat()) {
 +                    sourceNatIp = _ipAddressDao.findById(addr.getId());
 +                    return PublicIp.createFromAddrAndVlan(sourceNatIp, _vlanDao.findById(sourceNatIp.getVlanId()));
 +                }
 +            }
 +        }
 +
 +        return null;
 +    }
 +
 +    @Override
 +    public boolean isNetworkInlineMode(Network network) {
 +        NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +        return offering.isInline();
 +    }
 +
 +    @Override
 +    public void checkIp6Parameters(String startIPv6, String endIPv6, String ip6Gateway, String ip6Cidr) throws InvalidParameterValueException {
 +        if (!NetUtils.isValidIp6(startIPv6)) {
 +            throw new InvalidParameterValueException("Invalid format for the startIPv6 parameter");
 +        }
 +        if (!NetUtils.isValidIp6(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.isValidIp6(ip6Gateway)) {
 +            throw new InvalidParameterValueException("Invalid ip6Gateway");
 +        }
 +        if (!NetUtils.isValidIp6Cidr(ip6Cidr)) {
 +            throw new InvalidParameterValueException("Invalid ip6cidr");
 +        }
 +        if (!NetUtils.isIp6InNetwork(startIPv6, ip6Cidr)) {
 +            throw new InvalidParameterValueException("startIPv6 is not in ip6cidr indicated network!");
 +        }
 +        if (!NetUtils.isIp6InNetwork(endIPv6, ip6Cidr)) {
 +            throw new InvalidParameterValueException("endIPv6 is not in ip6cidr indicated network!");
 +        }
 +        if (!NetUtils.isIp6InNetwork(ip6Gateway, ip6Cidr)) {
 +            throw new InvalidParameterValueException("ip6Gateway is not in ip6cidr indicated network!");
 +        }
 +
 +        int cidrSize = NetUtils.getIp6CidrSize(ip6Cidr);
 +        // we only support cidr == 64
 +        if (cidrSize != 64) {
 +            throw new InvalidParameterValueException("The cidr size of IPv6 network must be 64 bits!");
 +        }
 +    }
 +
 +    @Override
 +    public void checkRequestedIpAddresses(long networkId, IpAddresses ips) throws InvalidParameterValueException {
 +        String ip4 = ips.getIp4Address();
 +        String ip6 = ips.getIp6Address();
 +        String mac = ips.getMacAddress();
 +        if (ip4 != null) {
 +            if (!NetUtils.isValidIp4(ip4)) {
 +                throw new InvalidParameterValueException("Invalid specified IPv4 address " + ip4);
 +            }
 +            //Other checks for ipv4 are done in assignPublicIpAddress()
 +        }
 +        if (ip6 != null) {
 +            if (!NetUtils.isValidIp6(ip6)) {
 +                throw new InvalidParameterValueException("Invalid specified IPv6 address " + ip6);
 +            }
 +            if (_ipv6Dao.findByNetworkIdAndIp(networkId, ip6) != null) {
 +                throw new InvalidParameterValueException("The requested IP is already taken!");
 +            }
 +            List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
 +            if (vlans == null) {
 +                throw new CloudRuntimeException("Cannot find related vlan attached to network " + networkId);
 +            }
 +            Vlan ipVlan = null;
 +            for (Vlan vlan : vlans) {
 +                if (NetUtils.isIp6InRange(ip6, vlan.getIp6Range())) {
 +                    ipVlan = vlan;
 +                    break;
 +                }
 +            }
 +            if (ipVlan == null) {
 +                throw new InvalidParameterValueException("Requested IPv6 is not in the predefined range!");
 +            }
 +        }
 +        if (mac != null) {
 +            if(!NetUtils.isValidMac(mac)) {
 +                throw new InvalidParameterValueException("Invalid specified MAC address " + mac);
 +            }
 +            if (_nicDao.findByNetworkIdAndMacAddress(networkId, mac) != null) {
 +                throw new InvalidParameterValueException("The requested Mac address is already taken! " + mac);
 +            }
 +
 +        }
 +    }
 +
 +    @Override
 +    public String getStartIpv6Address(long networkId) {
 +        List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(networkId);
 +        if (vlans == null) {
 +            return null;
 +        }
 +        String startIpv6 = null;
 +        // Get the start ip of first create vlan(not the lowest, because if you add a lower vlan, lowest vlan would change)
 +        for (Vlan vlan : vlans) {
 +            if (vlan.getIp6Range() != null) {
 +                startIpv6 = vlan.getIp6Range().split("-")[0];
 +                break;
 +            }
 +        }
 +        return startIpv6;
 +    }
 +
 +    @Override
 +    public NicVO getPlaceholderNicForRouter(Network network, Long podId) {
 +        List<NicVO> nics = _nicDao.listPlaceholderNicsByNetworkIdAndVmType(network.getId(), VirtualMachine.Type.DomainRouter);
 +        List<? extends Vlan> vlans = new ArrayList<VlanVO>();
 +        if (podId != null) {
 +            vlans = _vlanDao.listVlansForPod(podId);
 +        }
 +        for (NicVO nic : nics) {
 +            if (nic.getReserver() == null && (nic.getIPv4Address() != null || nic.getIPv6Address() != null)) {
 +                if (podId == null) {
 +                    return nic;
 +                } else {
 +                    IpAddress ip = null;
 +                    UserIpv6AddressVO ipv6 = null;
 +
 +                    if (nic.getIPv4Address() != null) {
 +                        ip = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), nic.getIPv4Address());
 +                    } else {
 +                        ipv6 = _ipv6Dao.findByNetworkIdAndIp(network.getId(), nic.getIPv6Address());
 +                    }
 +                    //return nic only when its ip address belong to the pod range (for the Basic zone case)
 +                    for (Vlan vlan : vlans) {
 +                        if (ip != null && ip.getVlanId() == vlan.getId()) {
 +                            return nic;
 +                        } else if (ipv6 != null && ipv6.getVlanId() == vlan.getId()) {
 +                            return nic;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public IpAddress getPublicIpAddress(String ipAddress, long zoneId) {
 +        List<? extends Network> networks = _networksDao.listByZoneAndTrafficType(zoneId, TrafficType.Public);
 +        if (networks.isEmpty() || networks.size() > 1) {
 +            throw new CloudRuntimeException("Can't find public network in the zone specified");
 +        }
 +
 +        return _ipAddressDao.findByIpAndSourceNetworkId(networks.get(0).getId(), ipAddress);
 +    }
 +
 +    @Override
 +    public Map<Detail, String> getNtwkOffDetails(long offId) {
 +        return _ntwkOffDetailsDao.getNtwkOffDetails(offId);
 +    }
 +
 +    @Override
 +    public Networks.IsolationType[] listNetworkIsolationMethods() {
 +        return Networks.IsolationType.values();
 +    }
 +
 +    @Override
 +    public boolean getExecuteInSeqNtwkElmtCmd() {
 +        return _executeInSequenceNtwkElmtCmd;
 +    }
 +
 +    @Override
 +    public boolean isNetworkReadyForGc(long networkId) {
 +        Network network = getNetwork(networkId);
 +        List<Long> networkIds = _networksDao.findNetworksToGarbageCollect();
 +        List<String> secondaryIps = _nicSecondaryIpDao.listSecondaryIpAddressInNetwork(networkId);
 +        if (!networkIds.contains(networkId)) {
 +            return false;
 +        }
 +
 +        // add an exception for networks that use external networking devices and has secondary guest IP's allocated.
 +        // On network GC, when network goes through implement phase a new vlan is allocated, based on the acquired VLAN
 +        // id cidr of the network is decided in case of external networking case. While NIC uses reservation strategy 'Start'
 +        // which ensures that new primary ip is allocated for the NiC from the new CIDR. Secondary IP's have hardcoded IP's in
 +        // network rules. So prevent network GC.
 +        if (secondaryIps != null && !secondaryIps.isEmpty() && networkIsConfiguredForExternalNetworking(network.getDataCenterId(), networkId)) {
 +            return false;
 +        }
 +
 +        //if the network has vms in Starting state (nics for those might not be allocated yet as Starting state also used when vm is being Created)
 +        //don't GC
 +        if (_nicDao.countNicsForStartingVms(networkId) > 0) {
 +            s_logger.debug("Network id=" + networkId + " is not ready for GC as it has vms that are Starting at the moment");
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean getNetworkEgressDefaultPolicy(Long networkId) {
 +        NetworkVO network = _networksDao.findById(networkId);
 +
 +        if (network != null) {
 +            NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +            return offering.getEgressDefaultPolicy();
 +        } else {
 +            InvalidParameterValueException ex = new InvalidParameterValueException("network with network id does not exist");
 +            throw ex;
 +        }
 +    }
 +
 +    @Override
-     public List<String[]> generateVmData(String userData, String serviceOffering, String zoneName,
-                                          String vmName, long vmId, String publicKey, String password, Boolean isWindows) {
++    public List<String[]> generateVmData(String userData, String serviceOffering, long datacenterId,
++                                         String vmName, long vmId, String vmUuid,
++                                         String guestIpAddress, String publicKey, String password, Boolean isWindows) {
++
++        DataCenterVO dcVo = _dcDao.findById(datacenterId);
++        final String zoneName = dcVo.getName();
++
++        IPAddressVO publicIp = _ipAddressDao.findByAssociatedVmId(vmId);
++
 +        final List<String[]> vmData = new ArrayList<String[]>();
 +
 +        if (userData != null) {
-             vmData.add(new String[]{USERDATA_DIR, USERDATA_FILE, new String(Base64.decodeBase64(userData),StringUtils.getPreferredCharset())});
++            vmData.add(new String[]{USERDATA_DIR, USERDATA_FILE, userData});
 +        }
 +        vmData.add(new String[]{METATDATA_DIR, SERVICE_OFFERING_FILE, StringUtils.unicodeEscape(serviceOffering)});
 +        vmData.add(new String[]{METATDATA_DIR, AVAILABILITY_ZONE_FILE, StringUtils.unicodeEscape(zoneName)});
 +        vmData.add(new String[]{METATDATA_DIR, LOCAL_HOSTNAME_FILE, StringUtils.unicodeEscape(vmName)});
-         vmData.add(new String[]{METATDATA_DIR, INSTANCE_ID_FILE, vmName});
-         vmData.add(new String[]{METATDATA_DIR, VM_ID_FILE, String.valueOf(vmId)});
++        vmData.add(new String[]{METATDATA_DIR, LOCAL_IPV4_FILE, guestIpAddress});
++
++        String publicIpAddress = guestIpAddress;
++        String publicHostName = StringUtils.unicodeEscape(vmName);
++
++        if (dcVo.getNetworkType() != DataCenter.NetworkType.Basic) {
++            if (publicIp != null) {
++                publicIpAddress = publicIp.getAddress().addr();
++                publicHostName = publicIp.getAddress().addr();
++            } else {
++                publicHostName = null;
++            }
++        }
++        vmData.add(new String[]{METATDATA_DIR, PUBLIC_IPV4_FILE, publicIpAddress});
++        vmData.add(new String[]{METATDATA_DIR, PUBLIC_HOSTNAME_FILE, publicHostName});
++
++        if (vmUuid == null) {
++            vmData.add(new String[]{METATDATA_DIR, INSTANCE_ID_FILE, vmName});
++            vmData.add(new String[]{METATDATA_DIR, VM_ID_FILE, String.valueOf(vmId)});
++        } else {
++            vmData.add(new String[]{METATDATA_DIR, INSTANCE_ID_FILE, vmUuid});
++            vmData.add(new String[]{METATDATA_DIR, VM_ID_FILE, vmUuid});
++        }
++
 +        vmData.add(new String[]{METATDATA_DIR, PUBLIC_KEYS_FILE, publicKey});
 +
 +        String cloudIdentifier = _configDao.getValue("cloud.identifier");
 +        if (cloudIdentifier == null) {
 +            cloudIdentifier = "";
 +        } else {
 +            cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}";
 +        }
 +        vmData.add(new String[]{METATDATA_DIR, CLOUD_IDENTIFIER_FILE, cloudIdentifier});
 +
 +        if (password != null && !password.isEmpty() && !password.equals("saved_password")) {
 +
 +            // Here we are calculating MD5 checksum to reduce the over head of calculating MD5 checksum
 +            // in windows VM in password reset script.
 +
 +            if (isWindows) {
 +                MessageDigest md5 = null;
 +                try {
 +                    md5 = MessageDigest.getInstance("MD5");
 +                } catch (NoSuchAlgorithmException e) {
 +                    s_logger.error("Unexpected exception " + e.getMessage(), e);
 +                    throw new CloudRuntimeException("Unable to get MD5 MessageDigest", e);
 +                }
 +                md5.reset();
 +                md5.update(password.getBytes(StringUtils.getPreferredCharset()));
 +                byte[] digest = md5.digest();
 +                BigInteger bigInt = new BigInteger(1, digest);
 +                String hashtext = bigInt.toString(16);
 +
 +                vmData.add(new String[]{PASSWORD_DIR, PASSWORD_CHECKSUM_FILE, hashtext});
 +            }
 +
 +            vmData.add(new String[]{PASSWORD_DIR, PASSWORD_FILE, password});
 +        }
 +
 +        return vmData;
 +    }
 +
 +    @Override
 +    public String getConfigComponentName() {
 +        return NetworkModel.class.getSimpleName();
 +    }
 +
 +    @Override
 +    public ConfigKey<?>[] getConfigKeys() {
 +        return new ConfigKey<?>[] {MACIdentifier};
 +    }
 +
 +    @Override
 +    public String getValidNetworkCidr(Network guestNetwork) {
 +        String networkCidr = guestNetwork.getNetworkCidr();
 +        return networkCidr == null ? guestNetwork.getCidr() : networkCidr;
 +    }
 +}
diff --cc server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index df81dd3,0000000..7558419
mode 100644,000000..100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@@ -1,6429 -1,0 +1,6428 @@@
 +// 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.vm;
 +
 +import java.io.UnsupportedEncodingException;
 +import java.net.URLDecoder;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.Date;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.LinkedHashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Map.Entry;
 +import java.util.Set;
 +import java.util.UUID;
 +import java.util.concurrent.ConcurrentHashMap;
 +import java.util.concurrent.ExecutorService;
 +import java.util.concurrent.Executors;
 +import java.util.concurrent.ScheduledExecutorService;
 +import java.util.concurrent.TimeUnit;
 +import java.util.stream.Collectors;
 +
 +import javax.inject.Inject;
 +import javax.naming.ConfigurationException;
 +
 +import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 +import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 +import org.apache.cloudstack.affinity.AffinityGroupService;
 +import org.apache.cloudstack.affinity.AffinityGroupVO;
 +import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
 +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
 +import org.apache.cloudstack.api.ApiConstants;
 +import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
 +import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
 +import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
 +import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
 +import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.SecurityGroupAction;
 +import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
 +import org.apache.cloudstack.api.command.user.vm.UpdateVmNicIpCmd;
 +import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
 +import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd;
 +import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd;
 +import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
 +import org.apache.cloudstack.context.CallContext;
 +import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
 +import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
 +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 +import org.apache.cloudstack.engine.service.api.OrchestrationService;
 +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
 +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
 +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
 +import org.apache.cloudstack.framework.async.AsyncCallFuture;
 +import org.apache.cloudstack.framework.config.ConfigKey;
 +import org.apache.cloudstack.framework.config.Configurable;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 +import org.apache.cloudstack.storage.command.DeleteCommand;
 +import org.apache.cloudstack.storage.command.DettachCommand;
 +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 +import org.apache.commons.codec.binary.Base64;
 +import org.apache.commons.collections.MapUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.log4j.Logger;
 +
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.GetVmDiskStatsAnswer;
 +import com.cloud.agent.api.GetVmDiskStatsCommand;
 +import com.cloud.agent.api.GetVmIpAddressCommand;
 +import com.cloud.agent.api.GetVmNetworkStatsAnswer;
 +import com.cloud.agent.api.GetVmNetworkStatsCommand;
 +import com.cloud.agent.api.GetVmStatsAnswer;
 +import com.cloud.agent.api.GetVmStatsCommand;
 +import com.cloud.agent.api.GetVolumeStatsAnswer;
 +import com.cloud.agent.api.GetVolumeStatsCommand;
 +import com.cloud.agent.api.ModifyTargetsCommand;
 +import com.cloud.agent.api.PvlanSetupCommand;
 +import com.cloud.agent.api.RestoreVMSnapshotAnswer;
 +import com.cloud.agent.api.RestoreVMSnapshotCommand;
 +import com.cloud.agent.api.StartAnswer;
 +import com.cloud.agent.api.VmDiskStatsEntry;
 +import com.cloud.agent.api.VmNetworkStatsEntry;
 +import com.cloud.agent.api.VmStatsEntry;
 +import com.cloud.agent.api.VolumeStatsEntry;
 +import com.cloud.agent.api.to.DiskTO;
 +import com.cloud.agent.api.to.NicTO;
 +import com.cloud.agent.api.to.VirtualMachineTO;
 +import com.cloud.agent.manager.Commands;
 +import com.cloud.alert.AlertManager;
 +import com.cloud.api.ApiDBUtils;
 +import com.cloud.capacity.Capacity;
 +import com.cloud.capacity.CapacityManager;
 +import com.cloud.configuration.Config;
 +import com.cloud.configuration.ConfigurationManager;
 +import com.cloud.configuration.Resource.ResourceType;
 +import com.cloud.dc.DataCenter;
 +import com.cloud.dc.DataCenter.NetworkType;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.DedicatedResourceVO;
 +import com.cloud.dc.HostPodVO;
 +import com.cloud.dc.Vlan;
 +import com.cloud.dc.Vlan.VlanType;
 +import com.cloud.dc.VlanVO;
 +import com.cloud.dc.dao.ClusterDao;
 +import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.dc.dao.DedicatedResourceDao;
 +import com.cloud.dc.dao.HostPodDao;
 +import com.cloud.dc.dao.VlanDao;
 +import com.cloud.deploy.DataCenterDeployment;
 +import com.cloud.deploy.DeployDestination;
 +import com.cloud.deploy.DeploymentPlanner;
 +import com.cloud.deploy.DeploymentPlanner.ExcludeList;
 +import com.cloud.deploy.DeploymentPlanningManager;
 +import com.cloud.deploy.PlannerHostReservationVO;
 +import com.cloud.deploy.dao.PlannerHostReservationDao;
 +import com.cloud.domain.Domain;
 +import com.cloud.domain.DomainVO;
 +import com.cloud.domain.dao.DomainDao;
 +import com.cloud.event.ActionEvent;
 +import com.cloud.event.ActionEventUtils;
 +import com.cloud.event.EventTypes;
 +import com.cloud.event.UsageEventUtils;
 +import com.cloud.event.UsageEventVO;
 +import com.cloud.event.dao.UsageEventDao;
 +import com.cloud.exception.AgentUnavailableException;
 +import com.cloud.exception.CloudException;
 +import com.cloud.exception.ConcurrentOperationException;
 +import com.cloud.exception.InsufficientAddressCapacityException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.ManagementServerException;
 +import com.cloud.exception.OperationTimedoutException;
 +import com.cloud.exception.PermissionDeniedException;
 +import com.cloud.exception.ResourceAllocationException;
 +import com.cloud.exception.ResourceUnavailableException;
 +import com.cloud.exception.StorageUnavailableException;
 +import com.cloud.exception.VirtualMachineMigrationException;
 +import com.cloud.gpu.GPU;
 +import com.cloud.ha.HighAvailabilityManager;
 +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.hypervisor.HypervisorCapabilitiesVO;
 +import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
 +import com.cloud.network.IpAddressManager;
 +import com.cloud.network.Network;
 +import com.cloud.network.Network.IpAddresses;
 +import com.cloud.network.Network.Provider;
 +import com.cloud.network.Network.Service;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.Networks.TrafficType;
 +import com.cloud.network.PhysicalNetwork;
 +import com.cloud.network.dao.FirewallRulesDao;
 +import com.cloud.network.dao.IPAddressDao;
 +import com.cloud.network.dao.IPAddressVO;
 +import com.cloud.network.dao.LoadBalancerVMMapDao;
 +import com.cloud.network.dao.LoadBalancerVMMapVO;
 +import com.cloud.network.dao.NetworkDao;
 +import com.cloud.network.dao.NetworkServiceMapDao;
 +import com.cloud.network.dao.NetworkVO;
 +import com.cloud.network.dao.PhysicalNetworkDao;
 +import com.cloud.network.element.UserDataServiceProvider;
 +import com.cloud.network.guru.NetworkGuru;
 +import com.cloud.network.lb.LoadBalancingRulesManager;
 +import com.cloud.network.router.VpcVirtualNetworkApplianceManager;
 +import com.cloud.network.rules.FirewallManager;
 +import com.cloud.network.rules.FirewallRuleVO;
 +import com.cloud.network.rules.PortForwardingRuleVO;
 +import com.cloud.network.rules.RulesManager;
 +import com.cloud.network.rules.dao.PortForwardingRulesDao;
 +import com.cloud.network.security.SecurityGroup;
 +import com.cloud.network.security.SecurityGroupManager;
 +import com.cloud.network.security.dao.SecurityGroupDao;
 +import com.cloud.network.vpc.VpcManager;
 +import com.cloud.offering.DiskOffering;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offering.NetworkOffering.Availability;
 +import com.cloud.offering.ServiceOffering;
 +import com.cloud.offerings.NetworkOfferingVO;
 +import com.cloud.offerings.dao.NetworkOfferingDao;
 +import com.cloud.org.Cluster;
 +import com.cloud.org.Grouping;
 +import com.cloud.resource.ResourceManager;
 +import com.cloud.resource.ResourceState;
 +import com.cloud.server.ManagementService;
 +import com.cloud.service.ServiceOfferingVO;
 +import com.cloud.service.dao.ServiceOfferingDao;
 +import com.cloud.service.dao.ServiceOfferingDetailsDao;
 +import com.cloud.storage.DataStoreRole;
 +import com.cloud.storage.DiskOfferingVO;
 +import com.cloud.storage.GuestOSCategoryVO;
 +import com.cloud.storage.GuestOSVO;
 +import com.cloud.storage.Snapshot;
 +import com.cloud.storage.SnapshotVO;
 +import com.cloud.storage.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.StoragePoolStatus;
 +import com.cloud.storage.VMTemplateStorageResourceAssoc;
 +import com.cloud.storage.VMTemplateVO;
 +import com.cloud.storage.VMTemplateZoneVO;
 +import com.cloud.storage.Volume;
 +import com.cloud.storage.VolumeApiService;
 +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.VMTemplateDao;
 +import com.cloud.storage.dao.VMTemplateZoneDao;
 +import com.cloud.storage.dao.VolumeDao;
 +import com.cloud.template.TemplateApiService;
 +import com.cloud.template.TemplateManager;
 +import com.cloud.template.VirtualMachineTemplate;
 +import com.cloud.user.Account;
 +import com.cloud.user.AccountManager;
 +import com.cloud.user.AccountService;
 +import com.cloud.user.ResourceLimitService;
 +import com.cloud.user.SSHKeyPair;
 +import com.cloud.user.SSHKeyPairVO;
 +import com.cloud.user.User;
 +import com.cloud.user.UserStatisticsVO;
 +import com.cloud.user.UserVO;
 +import com.cloud.user.VmDiskStatisticsVO;
 +import com.cloud.user.dao.AccountDao;
 +import com.cloud.user.dao.SSHKeyPairDao;
 +import com.cloud.user.dao.UserDao;
 +import com.cloud.user.dao.UserStatisticsDao;
 +import com.cloud.user.dao.VmDiskStatisticsDao;
 +import com.cloud.uservm.UserVm;
 +import com.cloud.utils.DateUtil;
 +import com.cloud.utils.Journal;
 +import com.cloud.utils.NumbersUtil;
 +import com.cloud.utils.Pair;
 +import com.cloud.utils.component.ManagerBase;
 +import com.cloud.utils.concurrency.NamedThreadFactory;
 +import com.cloud.utils.crypt.DBEncryptionUtil;
 +import com.cloud.utils.crypt.RSAHelper;
 +import com.cloud.utils.db.DB;
 +import com.cloud.utils.db.EntityManager;
 +import com.cloud.utils.db.GlobalLock;
 +import com.cloud.utils.db.SearchCriteria;
 +import com.cloud.utils.db.Transaction;
 +import com.cloud.utils.db.TransactionCallbackNoReturn;
 +import com.cloud.utils.db.TransactionCallbackWithException;
 +import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
 +import com.cloud.utils.db.TransactionStatus;
 +import com.cloud.utils.db.UUIDManager;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +import com.cloud.utils.exception.ExecutionException;
 +import com.cloud.utils.fsm.NoTransitionException;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.vm.VirtualMachine.State;
 +import com.cloud.vm.dao.DomainRouterDao;
 +import com.cloud.vm.dao.InstanceGroupDao;
 +import com.cloud.vm.dao.InstanceGroupVMMapDao;
 +import com.cloud.vm.dao.NicDao;
 +import com.cloud.vm.dao.NicExtraDhcpOptionDao;
 +import com.cloud.vm.dao.UserVmDao;
 +import com.cloud.vm.dao.UserVmDetailsDao;
 +import com.cloud.vm.dao.VMInstanceDao;
 +import com.cloud.vm.snapshot.VMSnapshotManager;
 +import com.cloud.vm.snapshot.VMSnapshotVO;
 +import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 +
 +
 +public class UserVmManagerImpl extends ManagerBase implements UserVmManager, VirtualMachineGuru, UserVmService, Configurable {
 +    private static final Logger s_logger = Logger.getLogger(UserVmManagerImpl.class);
 +
 +    /**
 +     * The number of seconds to wait before timing out when trying to acquire a global lock.
 +     */
 +    private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3;
 +
 +    private static final long GiB_TO_BYTES = 1024 * 1024 * 1024;
 +
 +    @Inject
 +    private EntityManager _entityMgr;
 +    @Inject
 +    private HostDao _hostDao;
 +    @Inject
 +    private ServiceOfferingDao _offeringDao;
 +    @Inject
 +    private DiskOfferingDao _diskOfferingDao;
 +    @Inject
 +    private VMTemplateDao _templateDao;
 +    @Inject
 +    private VMTemplateZoneDao _templateZoneDao;
 +    @Inject
 +    private TemplateDataStoreDao _templateStoreDao;
 +    @Inject
 +    private DomainDao _domainDao;
 +    @Inject
 +    private UserVmDao _vmDao;
 +    @Inject
 +    private VolumeDao _volsDao;
 +    @Inject
 +    private DataCenterDao _dcDao;
 +    @Inject
 +    private FirewallRulesDao _rulesDao;
 +    @Inject
 +    private LoadBalancerVMMapDao _loadBalancerVMMapDao;
 +    @Inject
 +    private PortForwardingRulesDao _portForwardingDao;
 +    @Inject
 +    private IPAddressDao _ipAddressDao;
 +    @Inject
 +    private HostPodDao _podDao;
 +    @Inject
 +    private NetworkModel _networkModel;
 +    @Inject
 +    private NetworkOrchestrationService _networkMgr;
 +    @Inject
 +    private AgentManager _agentMgr;
 +    @Inject
 +    private ConfigurationManager _configMgr;
 +    @Inject
 +    private AccountDao _accountDao;
 +    @Inject
 +    private UserDao _userDao;
 +    @Inject
 +    private SnapshotDao _snapshotDao;
 +    @Inject
 +    private GuestOSDao _guestOSDao;
 +    @Inject
 +    private HighAvailabilityManager _haMgr;
 +    @Inject
 +    private AlertManager _alertMgr;
 +    @Inject
 +    private AccountManager _accountMgr;
 +    @Inject
 +    private AccountService _accountService;
 +    @Inject
 +    private ClusterDao _clusterDao;
 +    @Inject
 +    private PrimaryDataStoreDao _storagePoolDao;
 +    @Inject
 +    private SecurityGroupManager _securityGroupMgr;
 +    @Inject
 +    private ServiceOfferingDao _serviceOfferingDao;
 +    @Inject
 +    private NetworkOfferingDao _networkOfferingDao;
 +    @Inject
 +    private InstanceGroupDao _vmGroupDao;
 +    @Inject
 +    private InstanceGroupVMMapDao _groupVMMapDao;
 +    @Inject
 +    private VirtualMachineManager _itMgr;
 +    @Inject
 +    private NetworkDao _networkDao;
 +    @Inject
 +    private NicDao _nicDao;
 +    @Inject
 +    private RulesManager _rulesMgr;
 +    @Inject
 +    private LoadBalancingRulesManager _lbMgr;
 +    @Inject
 +    private SSHKeyPairDao _sshKeyPairDao;
 +    @Inject
 +    private UserVmDetailsDao _vmDetailsDao;
 +    @Inject
 +    private HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
 +    @Inject
 +    private SecurityGroupDao _securityGroupDao;
 +    @Inject
 +    private CapacityManager _capacityMgr;
 +    @Inject
 +    private VMInstanceDao _vmInstanceDao;
 +    @Inject
 +    private ResourceLimitService _resourceLimitMgr;
 +    @Inject
 +    private FirewallManager _firewallMgr;
 +    @Inject
 +    private ResourceManager _resourceMgr;
 +    @Inject
 +    private NetworkServiceMapDao _ntwkSrvcDao;
 +    @Inject
 +    private PhysicalNetworkDao _physicalNetworkDao;
 +    @Inject
 +    private VpcManager _vpcMgr;
 +    @Inject
 +    private TemplateManager _templateMgr;
 +    @Inject
 +    private GuestOSCategoryDao _guestOSCategoryDao;
 +    @Inject
 +    private UsageEventDao _usageEventDao;
 +    @Inject
 +    private VmDiskStatisticsDao _vmDiskStatsDao;
 +    @Inject
 +    private VMSnapshotDao _vmSnapshotDao;
 +    @Inject
 +    private VMSnapshotManager _vmSnapshotMgr;
 +    @Inject
 +    private AffinityGroupVMMapDao _affinityGroupVMMapDao;
 +    @Inject
 +    private AffinityGroupDao _affinityGroupDao;
 +    @Inject
 +    private DedicatedResourceDao _dedicatedDao;
 +    @Inject
 +    private AffinityGroupService _affinityGroupService;
 +    @Inject
 +    private PlannerHostReservationDao _plannerHostReservationDao;
 +    @Inject
 +    private ServiceOfferingDetailsDao serviceOfferingDetailsDao;
 +    @Inject
 +    private UserStatisticsDao _userStatsDao;
 +    @Inject
 +    private VlanDao _vlanDao;
 +    @Inject
 +    private VolumeService _volService;
 +    @Inject
 +    private VolumeDataFactory volFactory;
 +    @Inject
 +    private UserVmDetailsDao _uservmDetailsDao;
 +    @Inject
 +    private UUIDManager _uuidMgr;
 +    @Inject
 +    private DeploymentPlanningManager _planningMgr;
 +    @Inject
 +    private VolumeApiService _volumeService;
 +    @Inject
 +    private DataStoreManager _dataStoreMgr;
 +    @Inject
 +    private VpcVirtualNetworkApplianceManager _virtualNetAppliance;
 +    @Inject
 +    private DomainRouterDao _routerDao;
 +    @Inject
 +    private VMNetworkMapDao _vmNetworkMapDao;
 +    @Inject
 +    private IpAddressManager _ipAddrMgr;
 +    @Inject
 +    private NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
 +    @Inject
 +    private TemplateApiService _tmplService;
 +    @Inject
 +    private ConfigurationDao _configDao;
 +
 +    private ScheduledExecutorService _executor = null;
 +    private ScheduledExecutorService _vmIpFetchExecutor = null;
 +    private int _expungeInterval;
 +    private int _expungeDelay;
 +    private boolean _dailyOrHourly = false;
 +    private int capacityReleaseInterval;
 +    private ExecutorService _vmIpFetchThreadExecutor;
 +
 +
 +    private String _instance;
 +    private boolean _instanceNameFlag;
 +    private int _scaleRetry;
 +    private Map<Long, VmAndCountDetails> vmIdCountMap = new ConcurrentHashMap<>();
 +
 +    private static final int MAX_HTTP_GET_LENGTH = 2 * MAX_USER_DATA_LENGTH_BYTES;
 +    private static final int MAX_HTTP_POST_LENGTH = 16 * MAX_USER_DATA_LENGTH_BYTES;
 +
 +    @Inject
 +    private OrchestrationService _orchSrvc;
 +
 +    @Inject
 +    private VolumeOrchestrationService volumeMgr;
 +
 +    @Inject
 +    private ManagementService _mgr;
 +
 +    private static final ConfigKey<Integer> VmIpFetchWaitInterval = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180",
 +            "Wait Interval (in seconds) for shared network vm dhcp ip addr fetch for next iteration ", true);
 +
 +    private static final ConfigKey<Integer> VmIpFetchTrialMax = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmip.max.retry", "10",
 +            "The max number of retrieval times for shared entwork vm dhcp ip fetch, in case of failures", true);
 +
 +    private static final ConfigKey<Integer> VmIpFetchThreadPoolMax = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmipFetch.threadPool.max", "10",
 +            "number of threads for fetching vms ip address", true);
 +
 +    private static final ConfigKey<Integer> VmIpFetchTaskWorkers = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmipfetchtask.workers", "10",
 +            "number of worker threads for vm ip fetch task ", true);
 +
 +    private static final ConfigKey<Boolean> AllowDeployVmIfGivenHostFails = new ConfigKey<Boolean>("Advanced", Boolean.class, "allow.deploy.vm.if.deploy.on.given.host.fails", "false",
 +            "allow vm to deploy on different host if vm fails to deploy on the given host ", true);
 +
 +
 +    @Override
 +    public UserVmVO getVirtualMachine(long vmId) {
 +        return _vmDao.findById(vmId);
 +    }
 +
 +    @Override
 +    public List<? extends UserVm> getVirtualMachines(long hostId) {
 +        return _vmDao.listByHostId(hostId);
 +    }
 +
 +    private void resourceLimitCheck(Account owner, Boolean displayVm, Long cpu, Long memory) throws ResourceAllocationException {
 +        _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm, displayVm);
 +        _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, displayVm, cpu);
 +        _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, displayVm, memory);
 +    }
 +
 +    private void resourceCountIncrement(long accountId, Boolean displayVm, Long cpu, Long memory) {
 +        _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm, displayVm);
 +        _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.cpu, displayVm, cpu);
 +        _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.memory, displayVm, memory);
 +    }
 +
 +    private void resourceCountDecrement(long accountId, Boolean displayVm, Long cpu, Long memory) {
 +        _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.user_vm, displayVm);
 +        _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.cpu, displayVm, cpu);
 +        _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.memory, displayVm, memory);
 +    }
 +
 +    public class VmAndCountDetails {
 +        long vmId;
 +        int  retrievalCount = VmIpFetchTrialMax.value();
 +
 +
 +        public VmAndCountDetails() {
 +        }
 +
 +        public VmAndCountDetails (long vmId, int retrievalCount) {
 +            this.vmId = vmId;
 +            this.retrievalCount = retrievalCount;
 +        }
 +
 +        public VmAndCountDetails (long vmId) {
 +            this.vmId = vmId;
 +        }
 +
 +        public int getRetrievalCount() {
 +            return retrievalCount;
 +        }
 +
 +        public void setRetrievalCount(int retrievalCount) {
 +            this.retrievalCount = retrievalCount;
 +        }
 +
 +        public long getVmId() {
 +            return vmId;
 +        }
 +
 +        public void setVmId(long vmId) {
 +            this.vmId = vmId;
 +        }
 +
 +        public void decrementCount() {
 +            this.retrievalCount--;
 +
 +        }
 +    }
 +
 +    private class VmIpAddrFetchThread extends ManagedContextRunnable {
 +
 +
 +        long nicId;
 +        long vmId;
 +        String vmName;
 +        boolean isWindows;
 +        Long hostId;
 +        String networkCidr;
 +
 +        public VmIpAddrFetchThread(long vmId, long nicId, String instanceName, boolean windows, Long hostId, String networkCidr) {
 +            this.vmId = vmId;
 +            this.nicId = nicId;
 +            this.vmName = instanceName;
 +            this.isWindows = windows;
 +            this.hostId = hostId;
 +            this.networkCidr = networkCidr;
 +        }
 +
 +        @Override
 +        protected void runInContext() {
 +            GetVmIpAddressCommand cmd = new GetVmIpAddressCommand(vmName, networkCidr, isWindows);
 +            boolean decrementCount = true;
 +
 +            try {
 +                s_logger.debug("Trying for vm "+ vmId +" nic Id "+nicId +" ip retrieval ...");
 +                Answer answer = _agentMgr.send(hostId, cmd);
 +                NicVO nic = _nicDao.findById(nicId);
 +                if (answer.getResult()) {
 +                    String vmIp = answer.getDetails();
 +
 +                    if (NetUtils.isValidIp4(vmIp)) {
 +                        // set this vm ip addr in vm nic.
 +                        if (nic != null) {
 +                            nic.setIPv4Address(vmIp);
 +                            _nicDao.update(nicId, nic);
 +                            s_logger.debug("Vm "+ vmId +" IP "+vmIp +" got retrieved successfully");
 +                            vmIdCountMap.remove(nicId);
 +                            decrementCount = false;
 +                            ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
 +                                    Domain.ROOT_DOMAIN, EventTypes.EVENT_NETWORK_EXTERNAL_DHCP_VM_IPFETCH,
 +                                    "VM " + vmId + " nic id " + nicId + " ip address " + vmIp + " got fetched successfully");
 +                        }
 +                    }
 +                } else {
 +                    //previously vm has ip and nic table has ip address. After vm restart or stop/start
 +                    //if vm doesnot get the ip then set the ip in nic table to null
 +                    if (nic.getIPv4Address() != null) {
 +                        nic.setIPv4Address(null);
 +                        _nicDao.update(nicId, nic);
 +                    }
 +                    if (answer.getDetails() != null) {
 +                        s_logger.debug("Failed to get vm ip for Vm "+ vmId + answer.getDetails());
 +                    }
 +                }
 +            } catch (OperationTimedoutException e) {
 +                s_logger.warn("Timed Out", e);
 +            } catch (AgentUnavailableException e) {
 +                s_logger.warn("Agent Unavailable ", e);
 +            } finally {
 +                if (decrementCount) {
 +                    VmAndCountDetails vmAndCount = vmIdCountMap.get(nicId);
 +                    vmAndCount.decrementCount();
 +                    s_logger.debug("Ip is not retrieved for VM " + vmId +" nic "+nicId + " ... decremented count to "+vmAndCount.getRetrievalCount());
 +                    vmIdCountMap.put(nicId, vmAndCount);
 +                }
 +            }
 +        }
 +    }
 +
 +
 +
 +
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD, eventDescription = "resetting Vm password", async = true)
 +    public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException {
 +        Account caller = CallContext.current().getCallingAccount();
 +        Long vmId = cmd.getId();
 +        UserVmVO userVm = _vmDao.findById(cmd.getId());
 +
 +        // Do parameters input validation
 +        if (userVm == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + cmd.getId());
 +        }
 +
 +        _vmDao.loadDetails(userVm);
 +
 +        VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId());
 +        if (template == null || !template.getEnablePassword()) {
 +            throw new InvalidParameterValueException("Fail to reset password for the virtual machine, the template is not password enabled");
 +        }
 +
 +        if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) {
 +            s_logger.error("vm is not in the right state: " + vmId);
 +            throw new InvalidParameterValueException("Vm with id " + vmId + " is not in the right state");
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, userVm);
 +
 +        boolean result = resetVMPasswordInternal(vmId, password);
 +
 +        if (result) {
 +            userVm.setPassword(password);
 +            // update the password in vm_details table too
 +            // Check if an SSH key pair was selected for the instance and if so
 +            // use it to encrypt & save the vm password
 +            encryptAndStorePassword(userVm, password);
 +        } else {
 +            throw new CloudRuntimeException("Failed to reset password for the virtual machine ");
 +        }
 +
 +        return userVm;
 +    }
 +
 +    private boolean resetVMPasswordInternal(Long vmId, String password) throws ResourceUnavailableException, InsufficientCapacityException {
 +        Long userId = CallContext.current().getCallingUserId();
 +        VMInstanceVO vmInstance = _vmDao.findById(vmId);
 +
 +        if (password == null || password.equals("")) {
 +            return false;
 +        }
 +
 +        VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId());
 +        if (template.getEnablePassword()) {
 +            Nic defaultNic = _networkModel.getDefaultNic(vmId);
 +            if (defaultNic == null) {
 +                s_logger.error("Unable to reset password for vm " + vmInstance + " as the instance doesn't have default nic");
 +                return false;
 +            }
 +
 +            Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId());
 +            NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork),
 +                    _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork));
 +            VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance);
 +            vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password);
 +
 +            UserDataServiceProvider element = _networkMgr.getPasswordResetProvider(defaultNetwork);
 +            if (element == null) {
 +                throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for password reset");
 +            }
 +
 +            boolean result = element.savePassword(defaultNetwork, defaultNicProfile, vmProfile);
 +
 +            // Need to reboot the virtual machine so that the password gets
 +            // redownloaded from the DomR, and reset on the VM
 +            if (!result) {
 +                s_logger.debug("Failed to reset password for the virtual machine; no need to reboot the vm");
 +                return false;
 +            } else {
 +                if (vmInstance.getState() == State.Stopped) {
 +                    s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of password reset");
 +                    return true;
 +                }
 +
 +                if (rebootVirtualMachine(userId, vmId) == null) {
 +                    s_logger.warn("Failed to reboot the vm " + vmInstance);
 +                    return false;
 +                } else {
 +                    s_logger.debug("Vm " + vmInstance + " is rebooted successfully as a part of password reset");
 +                    return true;
 +                }
 +            }
 +        } else {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Reset password called for a vm that is not using a password enabled template");
 +            }
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_RESETSSHKEY, eventDescription = "resetting Vm SSHKey", async = true)
 +    public UserVm resetVMSSHKey(ResetVMSSHKeyCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException {
 +
 +        Account caller = CallContext.current().getCallingAccount();
 +        Account owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
 +        Long vmId = cmd.getId();
 +
 +        UserVmVO userVm = _vmDao.findById(cmd.getId());
 +        if (userVm == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine by id" + cmd.getId());
 +        }
 +
 +        _vmDao.loadDetails(userVm);
 +        VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId());
 +
 +        // Do parameters input validation
 +
 +        if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) {
 +            s_logger.error("vm is not in the right state: " + vmId);
 +            throw new InvalidParameterValueException("Vm with specified id is not in the right state");
 +        }
 +        if (userVm.getState() != State.Stopped) {
 +            s_logger.error("vm is not in the right state: " + vmId);
 +            throw new InvalidParameterValueException("Vm " + userVm + " should be stopped to do SSH Key reset");
 +        }
 +
 +        SSHKeyPairVO s = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), cmd.getName());
 +        if (s == null) {
 +            throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + owner.getAccountName()
 +            + " in specified domain id");
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, userVm);
 +        String password = null;
 +        String sshPublicKey = s.getPublicKey();
 +        if (template != null && template.getEnablePassword()) {
 +            password = _mgr.generateRandomPassword();
 +        }
 +
 +        boolean result = resetVMSSHKeyInternal(vmId, sshPublicKey, password);
 +
 +        if (result) {
 +            userVm.setDetail("SSH.PublicKey", sshPublicKey);
 +            if (template != null && template.getEnablePassword()) {
 +                userVm.setPassword(password);
 +                //update the encrypted password in vm_details table too
 +                encryptAndStorePassword(userVm, password);
 +            }
 +            _vmDao.saveDetails(userVm);
 +        } else {
 +            throw new CloudRuntimeException("Failed to reset SSH Key for the virtual machine ");
 +        }
 +        return userVm;
 +    }
 +
 +    private boolean resetVMSSHKeyInternal(Long vmId, String sshPublicKey, String password) throws ResourceUnavailableException, InsufficientCapacityException {
 +        Long userId = CallContext.current().getCallingUserId();
 +        VMInstanceVO vmInstance = _vmDao.findById(vmId);
 +
 +        VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId());
 +        Nic defaultNic = _networkModel.getDefaultNic(vmId);
 +        if (defaultNic == null) {
 +            s_logger.error("Unable to reset SSH Key for vm " + vmInstance + " as the instance doesn't have default nic");
 +            return false;
 +        }
 +
 +        Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId());
 +        NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork),
 +                _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork));
 +
 +        VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance);
 +
 +        if (template.getEnablePassword()) {
 +            vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password);
 +        }
 +
 +        UserDataServiceProvider element = _networkMgr.getSSHKeyResetProvider(defaultNetwork);
 +        if (element == null) {
 +            throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for SSH Key reset");
 +        }
 +        boolean result = element.saveSSHKey(defaultNetwork, defaultNicProfile, vmProfile, sshPublicKey);
 +
 +        // Need to reboot the virtual machine so that the password gets redownloaded from the DomR, and reset on the VM
 +        if (!result) {
 +            s_logger.debug("Failed to reset SSH Key for the virtual machine; no need to reboot the vm");
 +            return false;
 +        } else {
 +            if (vmInstance.getState() == State.Stopped) {
 +                s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of SSH Key reset");
 +                return true;
 +            }
 +            if (rebootVirtualMachine(userId, vmId) == null) {
 +                s_logger.warn("Failed to reboot the vm " + vmInstance);
 +                return false;
 +            } else {
 +                s_logger.debug("Vm " + vmInstance + " is rebooted successfully as a part of SSH Key reset");
 +                return true;
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean stopVirtualMachine(long userId, long vmId) {
 +        boolean status = false;
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Stopping vm=" + vmId);
 +        }
 +        UserVmVO vm = _vmDao.findById(vmId);
 +        if (vm == null || vm.getRemoved() != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("VM is either removed or deleted.");
 +            }
 +            return true;
 +        }
 +
 +        _userDao.findById(userId);
 +        try {
 +            VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
 +            status = vmEntity.stop(Long.toString(userId));
 +        } catch (ResourceUnavailableException e) {
 +            s_logger.debug("Unable to stop due to ", e);
 +            status = false;
 +        } catch (CloudException e) {
 +            throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e);
 +        }
 +        return status;
 +    }
 +
 +    private UserVm rebootVirtualMachine(long userId, long vmId) throws InsufficientCapacityException, ResourceUnavailableException {
 +        UserVmVO vm = _vmDao.findById(vmId);
 +
 +        if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) {
 +            s_logger.warn("Vm id=" + vmId + " doesn't exist");
 +            return null;
 +        }
 +
 +        if (vm.getState() == State.Running && vm.getHostId() != null) {
 +            collectVmDiskStatistics(vm);
 +            collectVmNetworkStatistics(vm);
 +            DataCenterVO dc = _dcDao.findById(vm.getDataCenterId());
 +            try {
 +                if (dc.getNetworkType() == DataCenter.NetworkType.Advanced) {
 +                    //List all networks of vm
 +                    List<Long> vmNetworks = _vmNetworkMapDao.getNetworks(vmId);
 +                    List<DomainRouterVO> routers = new ArrayList<DomainRouterVO>();
 +                    //List the stopped routers
 +                    for(long vmNetworkId : vmNetworks) {
 +                        List<DomainRouterVO> router = _routerDao.listStopped(vmNetworkId);
 +                        routers.addAll(router);
 +                    }
 +                    //A vm may not have many nics attached and even fewer routers might be stopped (only in exceptional cases)
 +                    //Safe to start the stopped router serially, this is consistent with the way how multiple networks are added to vm during deploy
 +                    //and routers are started serially ,may revisit to make this process parallel
 +                    for(DomainRouterVO routerToStart : routers) {
 +                        s_logger.warn("Trying to start router " + routerToStart.getInstanceName() + " as part of vm: " + vm.getInstanceName() + " reboot");
 +                        _virtualNetAppliance.startRouter(routerToStart.getId(),true);
 +                    }
 +                }
 +            } catch (ConcurrentOperationException e) {
 +                throw new CloudRuntimeException("Concurrent operations on starting router. " + e);
 +            } catch (Exception ex){
 +                throw new CloudRuntimeException("Router start failed due to" + ex);
 +            }finally {
 +                s_logger.info("Rebooting vm " + vm.getInstanceName());
 +                _itMgr.reboot(vm.getUuid(), null);
 +            }
 +            return _vmDao.findById(vmId);
 +        } else {
 +            s_logger.error("Vm id=" + vmId + " is not in Running state, failed to reboot");
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "upgrading Vm")
 +    /*
 +     * TODO: cleanup eventually - Refactored API call
 +     */
 +    // This method will be deprecated as we use ScaleVMCmd for both stopped VMs and running VMs
 +    public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) throws ResourceAllocationException {
 +        Long vmId = cmd.getId();
 +        Long svcOffId = cmd.getServiceOfferingId();
 +        Account caller = CallContext.current().getCallingAccount();
 +
 +        // Verify input parameters
 +        //UserVmVO vmInstance = _vmDao.findById(vmId);
 +        VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
 +        } else if (!(vmInstance.getState().equals(State.Stopped))) {
 +            throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + " in state " + vmInstance.getState()
 +            + "; make sure the virtual machine is stopped");
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        // Check resource limits for CPU and Memory.
 +        Map<String, String> customParameters = cmd.getDetails();
 +        ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId);
 +        if (newServiceOffering.isDynamic()) {
 +            newServiceOffering.setDynamicFlag(true);
 +            validateCustomParameters(newServiceOffering, cmd.getDetails());
 +            newServiceOffering = _offeringDao.getcomputeOffering(newServiceOffering, customParameters);
 +        }
 +        ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId());
 +
 +        int newCpu = newServiceOffering.getCpu();
 +        int newMemory = newServiceOffering.getRamSize();
 +        int currentCpu = currentServiceOffering.getCpu();
 +        int currentMemory = currentServiceOffering.getRamSize();
 +
 +        if (newCpu > currentCpu) {
 +            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu, newCpu - currentCpu);
 +        }
 +        if (newMemory > currentMemory) {
 +            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, newMemory - currentMemory);
 +        }
 +
 +        // Check that the specified service offering ID is valid
 +        _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering);
 +
 +        _itMgr.upgradeVmDb(vmId, svcOffId);
 +        if (newServiceOffering.isDynamic()) {
 +            //save the custom values to the database.
 +            saveCustomOfferingDetails(vmId, newServiceOffering);
 +        }
 +        if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) {
 +            removeCustomOfferingDetails(vmId);
 +        }
 +
 +        // Increment or decrement CPU and Memory count accordingly.
 +        if (newCpu > currentCpu) {
 +            _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu));
 +        } else if (currentCpu > newCpu) {
 +            _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(currentCpu - newCpu));
 +        }
 +        if (newMemory > currentMemory) {
 +            _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory));
 +        } else if (currentMemory > newMemory) {
 +            _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(currentMemory - newMemory));
 +        }
 +
 +        // Generate usage event for VM upgrade
 +        UserVmVO userVm = _vmDao.findById(vmId);
 +        generateUsageEvent( userVm, userVm.isDisplayVm(), EventTypes.EVENT_VM_UPGRADE);
 +
 +        return userVm;
 +    }
 +
 +    @Override
 +    public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map<String, String> customParameters) {
 +        if (customParameters.size() != 0) {
 +            if (serviceOffering.getCpu() == null) {
 +                String cpuNumber = customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name());
 +                if ((cpuNumber == null) || (NumbersUtil.parseInt(cpuNumber, -1) <= 0)) {
 +                    throw new InvalidParameterValueException("Invalid cpu cores value, specify a value between 1 and " + Integer.MAX_VALUE);
 +                }
 +            } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) {
 +                throw new InvalidParameterValueException("The cpu cores of this offering id:" + serviceOffering.getId()
 +                + " is not customizable. This is predefined in the template.");
 +            }
 +
 +            if (serviceOffering.getSpeed() == null) {
 +                String cpuSpeed = customParameters.get(UsageEventVO.DynamicParameters.cpuSpeed.name());
 +                if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) {
 +                    throw new InvalidParameterValueException("Invalid cpu speed value, specify a value between 1 and " + Integer.MAX_VALUE);
 +                }
 +            } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
 +                throw new InvalidParameterValueException("The cpu speed of this offering id:" + serviceOffering.getId()
 +                + " is not customizable. This is predefined in the template.");
 +            }
 +
 +            if (serviceOffering.getRamSize() == null) {
 +                String memory = customParameters.get(UsageEventVO.DynamicParameters.memory.name());
 +                if (memory == null || (NumbersUtil.parseInt(memory, -1) < 32)) {
 +                    throw new InvalidParameterValueException("Invalid memory value, specify a value between 32 and " + Integer.MAX_VALUE + " MB");
 +                }
 +            } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) {
 +                throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getId() + " is not customizable. This is predefined in the template.");
 +            }
 +        } else {
 +            throw new InvalidParameterValueException("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering");
 +        }
 +    }
 +
 +    private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map<String, String> customParameters) throws ResourceAllocationException {
 +        Account caller = CallContext.current().getCallingAccount();
 +
 +        // Verify input parameters
 +        //UserVmVO vmInstance = _vmDao.findById(vmId);
 +        VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        // Check resource limits for CPU and Memory.
 +        ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId);
 +        if (newServiceOffering.isDynamic()) {
 +            newServiceOffering.setDynamicFlag(true);
 +            validateCustomParameters(newServiceOffering, customParameters);
 +            newServiceOffering = _offeringDao.getcomputeOffering(newServiceOffering, customParameters);
 +        }
 +        ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId());
 +
 +        int newCpu = newServiceOffering.getCpu();
 +        int newMemory = newServiceOffering.getRamSize();
 +        int currentCpu = currentServiceOffering.getCpu();
 +        int currentMemory = currentServiceOffering.getRamSize();
 +
 +        if (newCpu > currentCpu) {
 +            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu, newCpu - currentCpu);
 +        }
 +        if (newMemory > currentMemory) {
 +            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, newMemory - currentMemory);
 +        }
 +
 +        // Check that the specified service offering ID is valid
 +        _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering);
 +
 +        DiskOfferingVO newROOTDiskOffering = _diskOfferingDao.findById(newServiceOffering.getId());
 +
 +        List<VolumeVO> vols = _volsDao.findReadyRootVolumesByInstance(vmInstance.getId());
 +
 +        for (final VolumeVO rootVolumeOfVm : vols) {
 +            rootVolumeOfVm.setDiskOfferingId(newROOTDiskOffering.getId());
 +
 +            _volsDao.update(rootVolumeOfVm.getId(), rootVolumeOfVm);
 +
 +            ResizeVolumeCmd resizeVolumeCmd = new ResizeVolumeCmd(rootVolumeOfVm.getId(), newROOTDiskOffering.getMinIops(), newROOTDiskOffering.getMaxIops());
 +
 +            _volumeService.resizeVolume(resizeVolumeCmd);
 +        }
 +
 +        // Check if the new service offering can be applied to vm instance
 +        ServiceOffering newSvcOffering = _offeringDao.findById(svcOffId);
 +        Account owner = _accountMgr.getActiveAccountById(vmInstance.getAccountId());
 +        _accountMgr.checkAccess(owner, newSvcOffering);
 +
 +        _itMgr.upgradeVmDb(vmId, svcOffId);
 +        if (newServiceOffering.isDynamic()) {
 +            //save the custom values to the database.
 +            saveCustomOfferingDetails(vmId, newServiceOffering);
 +        }
 +        if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) {
 +            removeCustomOfferingDetails(vmId);
 +        }
 +
 +        // Increment or decrement CPU and Memory count accordingly.
 +        if (newCpu > currentCpu) {
 +            _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu));
 +        } else if (currentCpu > newCpu) {
 +            _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(currentCpu - newCpu));
 +        }
 +        if (newMemory > currentMemory) {
 +            _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory));
 +        } else if (currentMemory > newMemory) {
 +            _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(currentMemory - newMemory));
 +        }
 +
 +        return _vmDao.findById(vmInstance.getId());
 +
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_NIC_CREATE, eventDescription = "Creating Nic", async = true)
 +    public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException {
 +        Long vmId = cmd.getVmId();
 +        Long networkId = cmd.getNetworkId();
 +        String ipAddress = cmd.getIpAddress();
 +        String macAddress = cmd.getMacAddress();
 +        Account caller = CallContext.current().getCallingAccount();
 +
 +        UserVmVO vmInstance = _vmDao.findById(vmId);
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        // Check that Vm does not have VM Snapshots
 +        if (_vmSnapshotDao.findByVm(vmId).size() > 0) {
 +            throw new InvalidParameterValueException("NIC cannot be added to VM with VM Snapshots");
 +        }
 +
 +        NetworkVO network = _networkDao.findById(networkId);
 +        if (network == null) {
 +            throw new InvalidParameterValueException("unable to find a network with id " + networkId);
 +        }
 +
 +        if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
 +            if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain)
 +                    && !(network.getAclType() == ACLType.Account && network.getAccountId() == vmInstance.getAccountId())) {
 +                throw new InvalidParameterValueException("only shared network or isolated network with the same account_id can be added to vmId: " + vmId);
 +            }
 +        }
 +
 +        List<NicVO> allNics = _nicDao.listByVmId(vmInstance.getId());
 +        for (NicVO nic : allNics) {
 +            if (nic.getNetworkId() == network.getId()) {
 +                throw new CloudRuntimeException("A NIC already exists for VM:" + vmInstance.getInstanceName() + " in network: " + network.getUuid());
 +            }
 +        }
 +
 +        if(_nicDao.findByNetworkIdAndMacAddress(networkId, macAddress) != null) {
 +            throw new CloudRuntimeException("A NIC with this MAC address exists for network: " + network.getUuid());
 +        }
 +
 +        NicProfile profile = new NicProfile(ipAddress, null, macAddress);
 +        if (ipAddress != null) {
 +            if (!(NetUtils.isValidIp4(ipAddress) || NetUtils.isValidIp6(ipAddress))) {
 +                throw new InvalidParameterValueException("Invalid format for IP address parameter: " + ipAddress);
 +            }
 +        }
 +
 +        // Perform permission check on VM
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        // Verify that zone is not Basic
 +        DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterId());
 +        if (dc.getNetworkType() == DataCenter.NetworkType.Basic) {
 +            throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterId() + ", has a NetworkType of Basic. Can't add a new NIC to a VM on a Basic Network");
 +        }
 +
 +        // Perform account permission check on network
 +        _accountMgr.checkAccess(caller, AccessType.UseEntry, false, network);
 +
 +        //ensure network belongs in zone
 +        if (network.getDataCenterId() != vmInstance.getDataCenterId()) {
 +            throw new CloudRuntimeException(vmInstance + " is in zone:" + vmInstance.getDataCenterId() + " but " + network + " is in zone:" + network.getDataCenterId());
 +        }
 +
 +        // Get all vms hostNames in the network
 +        List<String> hostNames = _vmInstanceDao.listDistinctHostNames(network.getId());
 +        // verify that there are no duplicates, listDistictHostNames could return hostNames even if the NIC
 +        //in the network is removed, so also check if the NIC is present and then throw an exception.
 +        //This will also check if there are multiple nics of same vm in the network
 +        if (hostNames.contains(vmInstance.getHostName())) {
 +            for (String hostName : hostNames) {
 +                VMInstanceVO vm = _vmInstanceDao.findVMByHostName(hostName);
 +                if (_networkModel.getNicInNetwork(vm.getId(), network.getId()) != null && vm.getHostName().equals(vmInstance.getHostName())) {
 +                    throw new CloudRuntimeException(network + " already has a vm with host name: " + vmInstance.getHostName());
 +                }
 +            }
 +        }
 +
 +        NicProfile guestNic = null;
 +        boolean cleanUp = true;
 +
 +        try {
 +            guestNic = _itMgr.addVmToNetwork(vmInstance, network, profile);
 +            saveExtraDhcpOptions(guestNic.getId(), cmd.getDhcpOptionsMap());
 +            _networkMgr.configureExtraDhcpOptions(network, guestNic.getId(), cmd.getDhcpOptionsMap());
 +            cleanUp = false;
 +        } catch (ResourceUnavailableException e) {
 +            throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e);
 +        } catch (InsufficientCapacityException e) {
 +            throw new CloudRuntimeException("Insufficient capacity when adding NIC to " + vmInstance + ": " + e);
 +        } catch (ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Concurrent operations on adding NIC to " + vmInstance + ": " + e);
 +        } finally {
 +            if(cleanUp) {
 +                try {
 +                    _itMgr.removeVmFromNetwork(vmInstance, network, null);
 +                } catch (ResourceUnavailableException e) {
 +                    throw new CloudRuntimeException("Error while cleaning up NIC " + e);
 +                }
 +            }
 +        }
 +        CallContext.current().putContextParameter(Nic.class, guestNic.getUuid());
 +        s_logger.debug("Successful addition of " + network + " from " + vmInstance);
 +        return _vmDao.findById(vmInstance.getId());
 +    }
 +
 +
 +    private void saveExtraDhcpOptions(long nicId, Map<Integer, String> dhcpOptions) {
 +        List<NicExtraDhcpOptionVO> nicExtraDhcpOptionVOList = dhcpOptions
 +                .entrySet()
 +                .stream()
 +                .map(entry -> new NicExtraDhcpOptionVO(nicId, entry.getKey(), entry.getValue()))
 +                .collect(Collectors.toList());
 +
 +        _nicExtraDhcpOptionDao.saveExtraDhcpOptions(nicExtraDhcpOptionVOList);
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_NIC_DELETE, eventDescription = "Removing Nic", async = true)
 +    public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException {
 +        Long vmId = cmd.getVmId();
 +        Long nicId = cmd.getNicId();
 +        Account caller = CallContext.current().getCallingAccount();
 +
 +        UserVmVO vmInstance = _vmDao.findById(vmId);
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        // Check that Vm does not have VM Snapshots
 +        if (_vmSnapshotDao.findByVm(vmId).size() > 0) {
 +            throw new InvalidParameterValueException("NIC cannot be removed from VM with VM Snapshots");
 +        }
 +
 +        NicVO nic = _nicDao.findById(nicId);
 +        if (nic == null) {
 +            throw new InvalidParameterValueException("Unable to find a nic with id " + nicId);
 +        }
 +
 +        NetworkVO network = _networkDao.findById(nic.getNetworkId());
 +        if (network == null) {
 +            throw new InvalidParameterValueException("Unable to find a network with id " + nic.getNetworkId());
 +        }
 +
 +        // Perform permission check on VM
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        // Verify that zone is not Basic
 +        DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterId());
 +        if (dc.getNetworkType() == DataCenter.NetworkType.Basic) {
 +            throw new InvalidParameterValueException("Zone " + vmInstance.getDataCenterId() + ", has a NetworkType of Basic. Can't remove a NIC from a VM on a Basic Network");
 +        }
 +
 +        // check to see if nic is attached to VM
 +        if (nic.getInstanceId() != vmId) {
 +            throw new InvalidParameterValueException(nic + " is not a nic on " + vmInstance);
 +        }
 +
 +        // Perform account permission check on network
 +        _accountMgr.checkAccess(caller, AccessType.UseEntry, false, network);
 +
 +        // don't delete default NIC on a user VM
 +        if (nic.isDefaultNic() && vmInstance.getType() == VirtualMachine.Type.User) {
 +            throw new InvalidParameterValueException("Unable to remove nic from " + vmInstance + " in " + network + ", nic is default.");
 +        }
 +
 +        // if specified nic is associated with PF/LB/Static NAT
 +        if (_rulesMgr.listAssociatedRulesForGuestNic(nic).size() > 0) {
 +            throw new InvalidParameterValueException("Unable to remove nic from " + vmInstance + " in " + network + ", nic has associated Port forwarding or Load balancer or Static NAT rules.");
 +        }
 +
 +        boolean nicremoved = false;
 +        try {
 +            nicremoved = _itMgr.removeNicFromVm(vmInstance, nic);
 +        } catch (ResourceUnavailableException e) {
 +            throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance + ": " + e);
 +
 +        } catch (ConcurrentOperationException e) {
 +            throw new CloudRuntimeException("Concurrent operations on removing " + network + " from " + vmInstance + ": " + e);
 +        }
 +
 +        if (!nicremoved) {
 +            throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance);
 +        }
 +
 +        s_logger.debug("Successful removal of " + network + " from " + vmInstance);
 +        return _vmDao.findById(vmInstance.getId());
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_NIC_UPDATE, eventDescription = "Creating Nic", async = true)
 +    public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) throws InvalidParameterValueException, CloudRuntimeException {
 +        Long vmId = cmd.getVmId();
 +        Long nicId = cmd.getNicId();
 +        Account caller = CallContext.current().getCallingAccount();
 +
 +        UserVmVO vmInstance = _vmDao.findById(vmId);
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        // Check that Vm does not have VM Snapshots
 +        if (_vmSnapshotDao.findByVm(vmId).size() > 0) {
 +            throw new InvalidParameterValueException("NIC cannot be updated for VM with VM Snapshots");
 +        }
 +
 +        NicVO nic = _nicDao.findById(nicId);
 +        if (nic == null) {
 +            throw new InvalidParameterValueException("unable to find a nic with id " + nicId);
 +        }
 +        NetworkVO network = _networkDao.findById(nic.getNetworkId());
 +        if (network == null) {
 +            throw new InvalidParameterValueException("unable to find a network with id " + nic.getNetworkId());
 +        }
 +
 +        // Perform permission check on VM
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        // Verify that zone is not Basic
 +        DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterId());
 +        if (dc.getNetworkType() == DataCenter.NetworkType.Basic) {
 +            throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterId() + ", has a NetworkType of Basic. Can't change default NIC on a Basic Network");
 +        }
 +
 +        // no need to check permissions for network, we'll enumerate the ones they already have access to
 +        Network existingdefaultnet = _networkModel.getDefaultNetworkForVm(vmId);
 +
 +        //check to see if nic is attached to VM
 +        if (nic.getInstanceId() != vmId) {
 +            throw new InvalidParameterValueException(nic + " is not a nic on  " + vmInstance);
 +        }
 +        // if current default equals chosen new default, Throw an exception
 +        if (nic.isDefaultNic()) {
 +            throw new CloudRuntimeException("refusing to set default nic because chosen nic is already the default");
 +        }
 +
 +        //make sure the VM is Running or Stopped
 +        if ((vmInstance.getState() != State.Running) && (vmInstance.getState() != State.Stopped)) {
 +            throw new CloudRuntimeException("refusing to set default " + vmInstance + " is not Running or Stopped");
 +        }
 +
 +        NicProfile existing = null;
 +        List<NicProfile> nicProfiles = _networkMgr.getNicProfiles(vmInstance);
 +        for (NicProfile nicProfile : nicProfiles) {
 +            if (nicProfile.isDefaultNic() && existingdefaultnet != null && nicProfile.getNetworkId() == existingdefaultnet.getId()) {
 +                existing = nicProfile;
 +            }
 +        }
 +
 +        if (existing == null) {
 +            s_logger.warn("Failed to update default nic, no nic profile found for existing default network");
 +            throw new CloudRuntimeException("Failed to find a nic profile for the existing default network. This is bad and probably means some sort of configuration corruption");
 +        }
 +
 +        Network oldDefaultNetwork = null;
 +        oldDefaultNetwork = _networkModel.getDefaultNetworkForVm(vmId);
 +        String oldNicIdString = Long.toString(_networkModel.getDefaultNic(vmId).getId());
 +        long oldNetworkOfferingId = -1L;
 +
 +        if (oldDefaultNetwork != null) {
 +            oldNetworkOfferingId = oldDefaultNetwork.getNetworkOfferingId();
 +        }
 +        NicVO existingVO = _nicDao.findById(existing.id);
 +        Integer chosenID = nic.getDeviceId();
 +        Integer existingID = existing.getDeviceId();
 +
 +        nic.setDefaultNic(true);
 +        nic.setDeviceId(existingID);
 +        existingVO.setDefaultNic(false);
 +        existingVO.setDeviceId(chosenID);
 +
 +        nic = _nicDao.persist(nic);
 +        existingVO = _nicDao.persist(existingVO);
 +
 +        Network newdefault = null;
 +        newdefault = _networkModel.getDefaultNetworkForVm(vmId);
 +
 +        if (newdefault == null) {
 +            nic.setDefaultNic(false);
 +            nic.setDeviceId(chosenID);
 +            existingVO.setDefaultNic(true);
 +            existingVO.setDeviceId(existingID);
 +
 +            nic = _nicDao.persist(nic);
 +            _nicDao.persist(existingVO);
 +
 +            newdefault = _networkModel.getDefaultNetworkForVm(vmId);
 +            if (newdefault.getId() == existingdefaultnet.getId()) {
 +                throw new CloudRuntimeException("Setting a default nic failed, and we had no default nic, but we were able to set it back to the original");
 +            }
 +            throw new CloudRuntimeException("Failed to change default nic to " + nic + " and now we have no default");
 +        } else if (newdefault.getId() == nic.getNetworkId()) {
 +            s_logger.debug("successfully set default network to " + network + " for " + vmInstance);
 +            String nicIdString = Long.toString(nic.getId());
 +            long newNetworkOfferingId = network.getNetworkOfferingId();
 +            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(),
 +                    oldNicIdString, oldNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid(), vmInstance.isDisplay());
 +            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), nicIdString,
 +                    newNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid(), vmInstance.isDisplay());
 +            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), nicIdString,
 +                    newNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid(), vmInstance.isDisplay());
 +            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(),
 +                    oldNicIdString, oldNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid(), vmInstance.isDisplay());
 +            return _vmDao.findById(vmInstance.getId());
 +        }
 +
 +        throw new CloudRuntimeException("something strange happened, new default network(" + newdefault.getId() + ") is not null, and is not equal to the network("
 +                + nic.getNetworkId() + ") of the chosen nic");
 +    }
 +
 +    @Override
 +    public UserVm updateNicIpForVirtualMachine(UpdateVmNicIpCmd cmd) {
 +        Long nicId = cmd.getNicId();
 +        String ipaddr = cmd.getIpaddress();
 +        Account caller = CallContext.current().getCallingAccount();
 +
 +        //check whether the nic belongs to user vm.
 +        NicVO nicVO = _nicDao.findById(nicId);
 +        if (nicVO == null) {
 +            throw new InvalidParameterValueException("There is no nic for the " + nicId);
 +        }
 +
 +        if (nicVO.getVmType() != VirtualMachine.Type.User) {
 +            throw new InvalidParameterValueException("The nic is not belongs to user vm");
 +        }
 +
 +        UserVm vm = _vmDao.findById(nicVO.getInstanceId());
 +        if (vm == null) {
 +            throw new InvalidParameterValueException("There is no vm with the nic");
 +        }
 +
 +        Network network = _networkDao.findById(nicVO.getNetworkId());
 +        if (network == null) {
 +            throw new InvalidParameterValueException("There is no network with the nic");
 +        }
 +        // Don't allow to update vm nic ip if network is not in Implemented/Setup/Allocated state
 +        if (!(network.getState() == Network.State.Allocated || network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup)) {
 +            throw new InvalidParameterValueException("Network is not in the right state to update vm nic ip. Correct states are: " + Network.State.Allocated + ", " + Network.State.Implemented + ", "
 +                    + Network.State.Setup);
 +        }
 +
 +        NetworkOfferingVO offering = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId());
 +        if (offering == null) {
 +            throw new InvalidParameterValueException("There is no network offering with the network");
 +        }
 +        if (!_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty() && vm.getState() != State.Stopped) {
 +            InvalidParameterValueException ex = new InvalidParameterValueException(
 +                    "VM is not Stopped, unable to update the vm nic having the specified id");
 +            ex.addProxyObject(vm.getUuid(), "vmId");
 +            throw ex;
 +        }
 +
 +        // verify permissions
 +        _accountMgr.checkAccess(caller, null, true, vm);
 +        Account ipOwner = _accountDao.findByIdIncludingRemoved(vm.getAccountId());
 +
 +        // verify ip address
 +        s_logger.debug("Calling the ip allocation ...");
 +        DataCenter dc = _dcDao.findById(network.getDataCenterId());
 +        if (dc == null) {
 +            throw new InvalidParameterValueException("There is no dc with the nic");
 +        }
 +        if (dc.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Isolated) {
 +            try {
 +                ipaddr = _ipAddrMgr.allocateGuestIP(network, ipaddr);
 +            } catch (InsufficientAddressCapacityException e) {
 +                throw new InvalidParameterValueException("Allocating ip to guest nic " + nicVO.getUuid() + " failed, for insufficient address capacity");
 +            }
 +            if (ipaddr == null) {
 +                throw new InvalidParameterValueException("Allocating ip to guest nic " + nicVO.getUuid() + " failed, please choose another ip");
 +            }
 +
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.StaticNat)) {
 +                IPAddressVO oldIP = _ipAddressDao.findByAssociatedVmId(vm.getId());
 +                if (oldIP != null) {
 +                    oldIP.setVmIp(ipaddr);
 +                    _ipAddressDao.persist(oldIP);
 +                }
 +            }
 +            // implementing the network elements and resources as a part of vm nic ip update if network has services and it is in Implemented state
 +            if (!_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty() && network.getState() == Network.State.Implemented) {
 +                User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
 +                ReservationContext context = new ReservationContextImpl(null, null, callerUser, caller);
 +                DeployDestination dest = new DeployDestination(_dcDao.findById(network.getDataCenterId()), null, null, null);
 +
 +                s_logger.debug("Implementing the network " + network + " elements and resources as a part of vm nic ip update");
 +                try {
 +                    // implement the network elements and rules again
 +                    _networkMgr.implementNetworkElementsAndResources(dest, context, network, offering);
 +                } catch (Exception ex) {
 +                    s_logger.warn("Failed to implement network " + network + " elements and resources as a part of vm nic ip update due to ", ex);
 +                    CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id) elements and resources as a part of vm nic ip update");
 +                    e.addProxyObject(network.getUuid(), "networkId");
 +                    // restore to old ip address
 +                    if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.StaticNat)) {
 +                        IPAddressVO oldIP = _ipAddressDao.findByAssociatedVmId(vm.getId());
 +                        if (oldIP != null) {
 +                            oldIP.setVmIp(nicVO.getIPv4Address());
 +                            _ipAddressDao.persist(oldIP);
 +                        }
 +                    }
 +                    throw e;
 +                }
 +            }
 +        } else if (dc.getNetworkType() == NetworkType.Basic || network.getGuestType()  == Network.GuestType.Shared) {
 +            //handle the basic networks here
 +            //for basic zone, need to provide the podId to ensure proper ip alloation
 +            Long podId = null;
 +            if (dc.getNetworkType() == NetworkType.Basic) {
 +                podId = vm.getPodIdToDeployIn();
 +                if (podId == null) {
 +                    throw new InvalidParameterValueException("vm pod id is null in Basic zone; can't decide the range for ip allocation");
 +                }
 +            }
 +
 +            try {
 +                ipaddr = _ipAddrMgr.allocatePublicIpForGuestNic(network, podId, ipOwner, ipaddr);
 +                if (ipaddr == null) {
 +                    throw new InvalidParameterValueException("Allocating ip to guest nic " + nicVO.getUuid() + " failed, please choose another ip");
 +                }
 +
 +                final IPAddressVO newIp = _ipAddressDao.findByIpAndDcId(dc.getId(), ipaddr);
 +                final Vlan vlan = _vlanDao.findById(newIp.getVlanId());
 +                nicVO.setIPv4Gateway(vlan.getVlanGateway());
 +                nicVO.setIPv4Netmask(vlan.getVlanNetmask());
 +
 +                final IPAddressVO ip = _ipAddressDao.findByIpAndSourceNetworkId(nicVO.getNetworkId(), nicVO.getIPv4Address());
 +                if (ip != null) {
 +                    Transaction.execute(new TransactionCallbackNoReturn() {
 +                        @Override
 +                        public void doInTransactionWithoutResult(TransactionStatus status) {
 +                            _ipAddrMgr.markIpAsUnavailable(ip.getId());
 +                            _ipAddressDao.unassignIpAddress(ip.getId());
 +                        }
 +                    });
 +                }
 +            } catch (InsufficientAddressCapacityException e) {
 +                s_logger.error("Allocating ip to guest nic " + nicVO.getUuid() + " failed, for insufficient address capacity");
 +                return null;
 +            }
 +        } else {
 +            s_logger.error("UpdateVmNicIpCmd is not supported in this network...");
 +            return null;
 +        }
 +
 +        s_logger.debug("Updating IPv4 address of NIC " + nicVO + " to " + ipaddr + "/" + nicVO.getIPv4Netmask() + " with gateway " + nicVO.getIPv4Gateway());
 +        nicVO.setIPv4Address(ipaddr);
 +        _nicDao.persist(nicVO);
 +
 +        return vm;
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "Upgrading VM", async = true)
 +    public UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException,
 +    VirtualMachineMigrationException {
 +
 +        Long vmId = cmd.getId();
 +        Long newServiceOfferingId = cmd.getServiceOfferingId();
 +        CallContext.current().setEventDetails("Vm Id: " + vmId);
 +
 +        boolean result = upgradeVirtualMachine(vmId, newServiceOfferingId, cmd.getDetails());
 +        if (result) {
 +            UserVmVO vmInstance = _vmDao.findById(vmId);
 +            if (vmInstance.getState().equals(State.Stopped)) {
 +                // Generate usage event for VM upgrade
 +                generateUsageEvent(vmInstance, vmInstance.isDisplayVm(), EventTypes.EVENT_VM_UPGRADE);
 +            }
 +            if (vmInstance.getState().equals(State.Running)) {
 +                // Generate usage event for Dynamic scaling of VM
 +                generateUsageEvent( vmInstance, vmInstance.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
 +            }
 +            return vmInstance;
 +        } else {
 +            throw new CloudRuntimeException("Failed to scale the VM");
 +        }
 +    }
 +
 +    @Override
 +    public HashMap<Long, List<VmDiskStatsEntry>> getVmDiskStatistics(long hostId, String hostName, List<Long> vmIds) throws CloudRuntimeException {
 +        HashMap<Long, List<VmDiskStatsEntry>> vmDiskStatsById = new HashMap<Long, List<VmDiskStatsEntry>>();
 +
 +        if (vmIds.isEmpty()) {
 +            return vmDiskStatsById;
 +        }
 +
 +        List<String> vmNames = new ArrayList<String>();
 +
 +        for (Long vmId : vmIds) {
 +            UserVmVO vm = _vmDao.findById(vmId);
 +            vmNames.add(vm.getInstanceName());
 +        }
 +
 +        Answer answer = _agentMgr.easySend(hostId, new GetVmDiskStatsCommand(vmNames, _hostDao.findById(hostId).getGuid(), hostName));
 +        if (answer == null || !answer.getResult()) {
 +            s_logger.warn("Unable to obtain VM disk statistics.");
 +            return null;
 +        } else {
 +            HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsByName = ((GetVmDiskStatsAnswer)answer).getVmDiskStatsMap();
 +
 +            if (vmDiskStatsByName == null) {
 +                s_logger.warn("Unable to obtain VM disk statistics.");
 +                return null;
 +            }
 +
 +            for (Map.Entry<String, List<VmDiskStatsEntry>> entry: vmDiskStatsByName.entrySet()) {
 +                vmDiskStatsById.put(vmIds.get(vmNames.indexOf(entry.getKey())), entry.getValue());
 +            }
 +        }
 +
 +        return vmDiskStatsById;
 +    }
 +
 +    @Override
 +    public boolean upgradeVirtualMachine(Long vmId, Long newServiceOfferingId, Map<String, String> customParameters) throws ResourceUnavailableException,
 +    ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
 +
 +        // Verify input parameters
 +        VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
 +
 +        if (vmInstance != null) {
 +            if (vmInstance.getState().equals(State.Stopped)) {
 +                upgradeStoppedVirtualMachine(vmId, newServiceOfferingId, customParameters);
 +                return true;
 +            }
 +            if (vmInstance.getState().equals(State.Running)) {
 +                return upgradeRunningVirtualMachine(vmId, newServiceOfferingId, customParameters);
 +            }
 +        }
 +        return false;
 +    }
 +
 +    private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingId, Map<String, String> customParameters) throws ResourceUnavailableException,
 +    ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
 +
 +        Account caller = CallContext.current().getCallingAccount();
 +        VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
 +        if (vmInstance.getHypervisorType() != HypervisorType.XenServer && vmInstance.getHypervisorType() != HypervisorType.VMware && vmInstance.getHypervisorType() != HypervisorType.Simulator) {
 +            s_logger.info("Scaling the VM dynamically is not supported for VMs running on Hypervisor "+vmInstance.getHypervisorType());
 +            throw new InvalidParameterValueException("Scaling the VM dynamically is not supported for VMs running on Hypervisor "+vmInstance.getHypervisorType());
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        //Check if its a scale "up"
 +        ServiceOfferingVO newServiceOffering = _offeringDao.findById(newServiceOfferingId);
 +        if (newServiceOffering.isDynamic()) {
 +            newServiceOffering.setDynamicFlag(true);
 +            validateCustomParameters(newServiceOffering, customParameters);
 +            newServiceOffering = _offeringDao.getcomputeOffering(newServiceOffering, customParameters);
 +        }
 +
... 8416 lines suppressed ...

-- 
To stop receiving notification emails like this one, please contact
rafael@apache.org.