cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ro...@apache.org
Subject [cloudstack] 01/01: Merge remote-tracking branch 'origin/4.11' into 4.12
Date Mon, 03 Jun 2019 11:46:36 GMT
This is an automated email from the ASF dual-hosted git repository.

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

commit b2b99ca63eecab6c0be40015ee54c37b55e07bac
Merge: fb555b1 c9ce3e2
Author: Rohit Yadav <rohit.yadav@shapeblue.com>
AuthorDate: Mon Jun 3 17:15:41 2019 +0530

    Merge remote-tracking branch 'origin/4.11' into 4.12
    
    Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>

 .../cloud/network/element/DhcpServiceProvider.java |  2 ++
 .../cloud/agent/api/routing/DhcpEntryCommand.java  |  9 ++++++
 .../virtualnetwork/facade/DhcpEntryConfigItem.java |  2 +-
 .../virtualnetwork/model/VmDhcpConfig.java         | 14 ++++++++-
 .../service/NetworkOrchestrationService.java       |  4 +++
 .../engine/orchestration/NetworkOrchestrator.java  | 28 +++++++++++++++++
 .../networkservice/BaremetalDhcpElement.java       |  5 ++++
 .../contrail/management/ContrailElementImpl.java   |  5 ++++
 .../cloud/network/resource/JuniperSrxResource.java |  9 +++---
 .../com/cloud/network/element/NuageVspElement.java |  5 ++++
 .../network/element/VirtualRouterElement.java      | 28 +++++++++++++++++
 .../cloud/network/router/CommandSetupHelper.java   |  5 ++--
 .../com/cloud/network/rules/DhcpEntryRules.java    | 10 +++++++
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 14 ++++++---
 .../network/topology/AdvancedNetworkTopology.java  | 15 ++++++++++
 .../network/topology/AdvancedNetworkVisitor.java   |  3 +-
 .../network/topology/BasicNetworkTopology.java     | 21 +++++++++++++
 .../network/topology/BasicNetworkVisitor.java      |  3 +-
 .../network/topology/NetworkTopology.java          |  2 ++
 .../java/com/cloud/vpc/MockNetworkManagerImpl.java |  4 +++
 systemvm/debian/opt/cloud/bin/cs/CsDhcp.py         | 35 ++++++++++++++++++----
 systemvm/debian/opt/cloud/bin/cs_dhcp.py           |  4 +++
 22 files changed, 208 insertions(+), 19 deletions(-)

diff --cc engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 5c0e50b,0000000..2b2460e
mode 100644,000000..100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@@ -1,3932 -1,0 +1,3960 @@@
 +// 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.engine.orchestration;
 +
 +
 +import java.net.URI;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.Comparator;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.LinkedHashMap;
 +import java.util.LinkedList;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import java.util.UUID;
 +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.context.CallContext;
 +import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO;
 +import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
 +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 +import org.apache.cloudstack.framework.config.ConfigKey;
 +import org.apache.cloudstack.framework.config.ConfigKey.Scope;
 +import org.apache.cloudstack.framework.config.Configurable;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.framework.messagebus.MessageBus;
 +import org.apache.cloudstack.framework.messagebus.PublishScope;
 +import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 +import org.apache.log4j.Logger;
 +
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.Listener;
 +import com.cloud.agent.api.AgentControlAnswer;
 +import com.cloud.agent.api.AgentControlCommand;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.CheckNetworkAnswer;
 +import com.cloud.agent.api.CheckNetworkCommand;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.StartupCommand;
 +import com.cloud.agent.api.StartupRoutingCommand;
 +import com.cloud.agent.api.routing.NetworkElementCommand;
 +import com.cloud.agent.api.to.NicTO;
 +import com.cloud.alert.AlertManager;
 +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.DataCenterVnetVO;
 +import com.cloud.dc.PodVlanMapVO;
 +import com.cloud.dc.Vlan;
 +import com.cloud.dc.VlanVO;
 +import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.dc.dao.DataCenterVnetDao;
 +import com.cloud.dc.dao.PodVlanMapDao;
 +import com.cloud.dc.dao.VlanDao;
 +import com.cloud.deploy.DataCenterDeployment;
 +import com.cloud.deploy.DeployDestination;
 +import com.cloud.deploy.DeploymentPlan;
 +import com.cloud.domain.Domain;
 +import com.cloud.exception.ConcurrentOperationException;
 +import com.cloud.exception.ConnectionException;
 +import com.cloud.exception.InsufficientAddressCapacityException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.ResourceAllocationException;
 +import com.cloud.exception.ResourceUnavailableException;
 +import com.cloud.exception.UnsupportedServiceException;
 +import com.cloud.host.Host;
 +import com.cloud.host.Status;
 +import com.cloud.host.dao.HostDao;
 +import com.cloud.hypervisor.Hypervisor.HypervisorType;
 +import com.cloud.network.IpAddress;
 +import com.cloud.network.IpAddressManager;
 +import com.cloud.network.Network;
 +import com.cloud.network.Network.Capability;
 +import com.cloud.network.Network.Event;
 +import com.cloud.network.Network.GuestType;
 +import com.cloud.network.Network.Provider;
 +import com.cloud.network.Network.Service;
 +import com.cloud.network.NetworkMigrationResponder;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.NetworkProfile;
 +import com.cloud.network.NetworkStateListener;
 +import com.cloud.network.Networks;
 +import com.cloud.network.Networks.BroadcastDomainType;
 +import com.cloud.network.Networks.TrafficType;
 +import com.cloud.network.PhysicalNetwork;
 +import com.cloud.network.PhysicalNetworkSetupInfo;
 +import com.cloud.network.RemoteAccessVpn;
 +import com.cloud.network.VpcVirtualNetworkApplianceService;
 +import com.cloud.network.addr.PublicIp;
 +import com.cloud.network.dao.AccountGuestVlanMapDao;
 +import com.cloud.network.dao.AccountGuestVlanMapVO;
 +import com.cloud.network.dao.FirewallRulesDao;
 +import com.cloud.network.dao.IPAddressDao;
 +import com.cloud.network.dao.IPAddressVO;
 +import com.cloud.network.dao.NetworkAccountDao;
 +import com.cloud.network.dao.NetworkAccountVO;
 +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.PhysicalNetworkTrafficTypeDao;
 +import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
 +import com.cloud.network.dao.PhysicalNetworkVO;
 +import com.cloud.network.dao.RemoteAccessVpnDao;
 +import com.cloud.network.dao.RemoteAccessVpnVO;
 +import com.cloud.network.element.AggregatedCommandExecutor;
 +import com.cloud.network.element.DhcpServiceProvider;
 +import com.cloud.network.element.DnsServiceProvider;
 +import com.cloud.network.element.IpDeployer;
 +import com.cloud.network.element.LoadBalancingServiceProvider;
 +import com.cloud.network.element.NetworkElement;
 +import com.cloud.network.element.RedundantResource;
 +import com.cloud.network.element.StaticNatServiceProvider;
 +import com.cloud.network.element.UserDataServiceProvider;
 +import com.cloud.network.guru.NetworkGuru;
 +import com.cloud.network.guru.NetworkGuruAdditionalFunctions;
 +import com.cloud.network.lb.LoadBalancingRulesManager;
 +import com.cloud.network.router.VirtualRouter;
 +import com.cloud.network.rules.FirewallManager;
 +import com.cloud.network.rules.FirewallRule;
 +import com.cloud.network.rules.FirewallRule.Purpose;
 +import com.cloud.network.rules.FirewallRuleVO;
 +import com.cloud.network.rules.LoadBalancerContainer.Scheme;
 +import com.cloud.network.rules.PortForwardingRuleVO;
 +import com.cloud.network.rules.RulesManager;
 +import com.cloud.network.rules.StaticNatRule;
 +import com.cloud.network.rules.StaticNatRuleImpl;
 +import com.cloud.network.rules.dao.PortForwardingRulesDao;
 +import com.cloud.network.vpc.NetworkACLManager;
 +import com.cloud.network.vpc.Vpc;
 +import com.cloud.network.vpc.VpcManager;
 +import com.cloud.network.vpc.dao.PrivateIpDao;
 +import com.cloud.network.vpn.RemoteAccessVpnService;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offering.NetworkOffering.Availability;
 +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.user.Account;
 +import com.cloud.user.ResourceLimitService;
 +import com.cloud.user.User;
 +import com.cloud.user.dao.AccountDao;
 +import com.cloud.utils.NumbersUtil;
 +import com.cloud.utils.Pair;
 +import com.cloud.utils.StringUtils;
 +import com.cloud.utils.UuidUtils;
 +import com.cloud.utils.component.AdapterBase;
 +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.JoinBuilder.JoinType;
 +import com.cloud.utils.db.SearchBuilder;
 +import com.cloud.utils.db.SearchCriteria.Op;
 +import com.cloud.utils.db.Transaction;
 +import com.cloud.utils.db.TransactionCallback;
 +import com.cloud.utils.db.TransactionCallbackNoReturn;
 +import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
 +import com.cloud.utils.db.TransactionStatus;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +import com.cloud.utils.fsm.NoTransitionException;
 +import com.cloud.utils.fsm.StateMachine2;
 +import com.cloud.utils.net.Dhcp;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.vm.DomainRouterVO;
 +import com.cloud.vm.Nic;
 +import com.cloud.vm.Nic.ReservationStrategy;
 +import com.cloud.vm.NicExtraDhcpOptionVO;
 +import com.cloud.vm.NicIpAlias;
 +import com.cloud.vm.NicProfile;
 +import com.cloud.vm.NicVO;
 +import com.cloud.vm.ReservationContext;
 +import com.cloud.vm.ReservationContextImpl;
 +import com.cloud.vm.UserVmVO;
 +import com.cloud.vm.VMInstanceVO;
 +import com.cloud.vm.VirtualMachine;
 +import com.cloud.vm.VirtualMachine.Type;
 +import com.cloud.vm.VirtualMachineProfile;
 +import com.cloud.vm.dao.DomainRouterDao;
 +import com.cloud.vm.dao.NicDao;
 +import com.cloud.vm.dao.NicExtraDhcpOptionDao;
 +import com.cloud.vm.dao.NicIpAliasDao;
 +import com.cloud.vm.dao.NicIpAliasVO;
 +import com.cloud.vm.dao.NicSecondaryIpDao;
 +import com.cloud.vm.dao.NicSecondaryIpVO;
 +import com.cloud.vm.dao.UserVmDao;
 +import com.cloud.vm.dao.VMInstanceDao;
 +import com.google.common.base.Strings;
 +
 +/**
 + * NetworkManagerImpl implements NetworkManager.
 + */
 +public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestrationService, Listener, Configurable {
 +    static final Logger s_logger = Logger.getLogger(NetworkOrchestrator.class);
 +
 +    @Inject
 +    EntityManager _entityMgr;
 +    @Inject
 +    DataCenterDao _dcDao;
 +    @Inject
 +    VlanDao _vlanDao;
 +    @Inject
 +    IPAddressDao _ipAddressDao;
 +    @Inject
 +    AccountDao _accountDao;
 +    @Inject
 +    ConfigurationDao _configDao;
 +    @Inject
 +    UserVmDao _userVmDao;
 +    @Inject
 +    AlertManager _alertMgr;
 +    @Inject
 +    ConfigurationManager _configMgr;
 +    @Inject
 +    NetworkOfferingDao _networkOfferingDao;
 +    @Inject
 +    NetworkDao _networksDao;
 +    @Inject
 +    NicDao _nicDao;
 +    @Inject
 +    RulesManager _rulesMgr;
 +    @Inject
 +    LoadBalancingRulesManager _lbMgr;
 +    @Inject
 +    RemoteAccessVpnService _vpnMgr;
 +    @Inject
 +    PodVlanMapDao _podVlanMapDao;
 +    @Inject
 +    NetworkOfferingDetailsDao _ntwkOffDetailsDao;
 +    @Inject
 +    AccountGuestVlanMapDao _accountGuestVlanMapDao;
 +    @Inject
 +    DataCenterVnetDao _datacenterVnetDao;
 +    @Inject
 +    NetworkAccountDao _networkAccountDao;
 +    @Inject
 +    protected NicIpAliasDao _nicIpAliasDao;
 +    @Inject
 +    protected NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
 +    @Inject
 +    protected IPAddressDao _publicIpAddressDao;
 +    @Inject
 +    protected IpAddressManager _ipAddrMgr;
 +    @Inject
 +    MessageBus _messageBus;
 +    @Inject
 +    VMNetworkMapDao _vmNetworkMapDao;
 +    @Inject
 +    DomainRouterDao _routerDao;
 +    @Inject
 +    RemoteAccessVpnDao _remoteAccessVpnDao;
 +    @Inject
 +    VpcVirtualNetworkApplianceService _routerService;
 +
 +    List<NetworkGuru> networkGurus;
 +
 +    @Override
 +    public List<NetworkGuru> getNetworkGurus() {
 +        return networkGurus;
 +    }
 +
 +    public void setNetworkGurus(final List<NetworkGuru> networkGurus) {
 +        this.networkGurus = networkGurus;
 +    }
 +
 +    List<NetworkElement> networkElements;
 +
 +    public List<NetworkElement> getNetworkElements() {
 +        return networkElements;
 +    }
 +
 +    public void setNetworkElements(final List<NetworkElement> networkElements) {
 +        this.networkElements = networkElements;
 +    }
 +
 +    @Inject
 +    NetworkDomainDao _networkDomainDao;
 +
 +    List<IpDeployer> ipDeployers;
 +
 +    public List<IpDeployer> getIpDeployers() {
 +        return ipDeployers;
 +    }
 +
 +    public void setIpDeployers(final List<IpDeployer> ipDeployers) {
 +        this.ipDeployers = ipDeployers;
 +    }
 +
 +    List<DhcpServiceProvider> _dhcpProviders;
 +
 +    public List<DhcpServiceProvider> getDhcpProviders() {
 +        return _dhcpProviders;
 +    }
 +
 +    public void setDhcpProviders(final List<DhcpServiceProvider> dhcpProviders) {
 +        _dhcpProviders = dhcpProviders;
 +    }
 +
 +    @Inject
 +    VMInstanceDao _vmDao;
 +    @Inject
 +    FirewallManager _firewallMgr;
 +    @Inject
 +    FirewallRulesDao _firewallDao;
 +    @Inject
 +    ResourceLimitService _resourceLimitMgr;
 +
 +    @Inject
 +    NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
 +    @Inject
 +    PhysicalNetworkDao _physicalNetworkDao;
 +    @Inject
 +    PhysicalNetworkServiceProviderDao _pNSPDao;
 +    @Inject
 +    PortForwardingRulesDao _portForwardingRulesDao;
 +    @Inject
 +    PhysicalNetworkTrafficTypeDao _pNTrafficTypeDao;
 +    @Inject
 +    AgentManager _agentMgr;
 +    @Inject
 +    HostDao _hostDao;
 +    @Inject
 +    NetworkServiceMapDao _ntwkSrvcDao;
 +    @Inject
 +    VpcManager _vpcMgr;
 +    @Inject
 +    PrivateIpDao _privateIpDao;
 +    @Inject
 +    NetworkACLManager _networkACLMgr;
 +    @Inject
 +    NetworkModel _networkModel;
 +    @Inject
 +    NicSecondaryIpDao _nicSecondaryIpDao;
 +
 +    protected StateMachine2<Network.State, Network.Event, Network> _stateMachine;
 +    ScheduledExecutorService _executor;
 +
 +    SearchBuilder<IPAddressVO> AssignIpAddressSearch;
 +    SearchBuilder<IPAddressVO> AssignIpAddressFromPodVlanSearch;
 +
 +    HashMap<Long, Long> _lastNetworkIdsToFree = new HashMap<Long, Long>();
 +
 +    @Override
 +    @DB
 +    public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
 +        // populate providers
 +        final Map<Network.Service, Set<Network.Provider>> defaultSharedNetworkOfferingProviders = new HashMap<Network.Service, Set<Network.Provider>>();
 +        final Set<Network.Provider> defaultProviders = new HashSet<Network.Provider>();
 +
 +        defaultProviders.add(Network.Provider.VirtualRouter);
 +        defaultSharedNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
 +        defaultSharedNetworkOfferingProviders.put(Service.Dns, defaultProviders);
 +        defaultSharedNetworkOfferingProviders.put(Service.UserData, defaultProviders);
 +
 +        final Map<Network.Service, Set<Network.Provider>> defaultIsolatedNetworkOfferingProviders = defaultSharedNetworkOfferingProviders;
 +        defaultIsolatedNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.Dns, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.UserData, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.Firewall, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.Gateway, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.Lb, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.StaticNat, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.PortForwarding, defaultProviders);
 +        defaultIsolatedNetworkOfferingProviders.put(Service.Vpn, defaultProviders);
 +
 +        final Map<Network.Service, Set<Network.Provider>> defaultSharedSGEnabledNetworkOfferingProviders = new HashMap<Network.Service, Set<Network.Provider>>();
 +        defaultSharedSGEnabledNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
 +        defaultSharedSGEnabledNetworkOfferingProviders.put(Service.Dns, defaultProviders);
 +        defaultSharedSGEnabledNetworkOfferingProviders.put(Service.UserData, defaultProviders);
 +        final Set<Provider> sgProviders = new HashSet<Provider>();
 +        sgProviders.add(Provider.SecurityGroupProvider);
 +        defaultSharedSGEnabledNetworkOfferingProviders.put(Service.SecurityGroup, sgProviders);
 +
 +        final Map<Network.Service, Set<Network.Provider>> defaultIsolatedSourceNatEnabledNetworkOfferingProviders = new HashMap<Network.Service, Set<Network.Provider>>();
 +        defaultProviders.clear();
 +        defaultProviders.add(Network.Provider.VirtualRouter);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Dns, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.UserData, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Firewall, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Gateway, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Lb, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.SourceNat, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.StaticNat, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.PortForwarding, defaultProviders);
 +        defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Vpn, defaultProviders);
 +
 +        final Map<Network.Service, Set<Network.Provider>> defaultVPCOffProviders = new HashMap<Network.Service, Set<Network.Provider>>();
 +        defaultProviders.clear();
 +        defaultProviders.add(Network.Provider.VPCVirtualRouter);
 +        defaultVPCOffProviders.put(Service.Dhcp, defaultProviders);
 +        defaultVPCOffProviders.put(Service.Dns, defaultProviders);
 +        defaultVPCOffProviders.put(Service.UserData, defaultProviders);
 +        defaultVPCOffProviders.put(Service.NetworkACL, defaultProviders);
 +        defaultVPCOffProviders.put(Service.Gateway, defaultProviders);
 +        defaultVPCOffProviders.put(Service.Lb, defaultProviders);
 +        defaultVPCOffProviders.put(Service.SourceNat, defaultProviders);
 +        defaultVPCOffProviders.put(Service.StaticNat, defaultProviders);
 +        defaultVPCOffProviders.put(Service.PortForwarding, defaultProviders);
 +        defaultVPCOffProviders.put(Service.Vpn, defaultProviders);
 +
 +        Transaction.execute(new TransactionCallbackNoReturn() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus status) {
 +                NetworkOfferingVO offering = null;
 +                //#1 - quick cloud network offering
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true,
 +                            Availability.Optional, null, new HashMap<Network.Service, Set<Network.Provider>>(), true, Network.GuestType.Shared, false, null, true, null, true,
 +                            false, null, false, null, true, false);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#2 - SG enabled network offering
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks",
 +                            TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true,
 +                            null, true, false, null, false, null, true, false);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#3 - shared network offering with no SG service
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOffering) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true,
 +                            Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false,
 +                            null, true, false);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#4 - default isolated offering with Source nat service
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService,
 +                            "Offering for Isolated networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Required, null,
 +                            defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null,
 +                            true, false);
 +
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#5 - default vpc offering with LB service
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks,
 +                            "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null,
 +                            defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#6 - default vpc offering with no LB service
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB) == null) {
 +                    //remove LB service
 +                    defaultVPCOffProviders.remove(Service.Lb);
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB,
 +                            "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, Availability.Optional,
 +                            null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#7 - isolated offering with source nat disabled
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOffering) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service",
 +                            TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null,
 +                            true, null, true, false, null, false, null, true, false);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                //#8 - network offering with internal lb service
 +                final Map<Network.Service, Set<Network.Provider>> internalLbOffProviders = new HashMap<Network.Service, Set<Network.Provider>>();
 +                final Set<Network.Provider> defaultVpcProvider = new HashSet<Network.Provider>();
 +                defaultVpcProvider.add(Network.Provider.VPCVirtualRouter);
 +
 +                final Set<Network.Provider> defaultInternalLbProvider = new HashSet<Network.Provider>();
 +                defaultInternalLbProvider.add(Network.Provider.InternalLbVm);
 +
 +                internalLbOffProviders.put(Service.Dhcp, defaultVpcProvider);
 +                internalLbOffProviders.put(Service.Dns, defaultVpcProvider);
 +                internalLbOffProviders.put(Service.UserData, defaultVpcProvider);
 +                internalLbOffProviders.put(Service.NetworkACL, defaultVpcProvider);
 +                internalLbOffProviders.put(Service.Gateway, defaultVpcProvider);
 +                internalLbOffProviders.put(Service.Lb, defaultInternalLbProvider);
 +                internalLbOffProviders.put(Service.SourceNat, defaultVpcProvider);
 +
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB,
 +                            "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, internalLbOffProviders,
 +                            true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    offering.setInternalLb(true);
 +                    offering.setPublicLb(false);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                final Map<Network.Service, Set<Network.Provider>> netscalerServiceProviders = new HashMap<Network.Service, Set<Network.Provider>>();
 +                final Set<Network.Provider> vrProvider = new HashSet<Network.Provider>();
 +                vrProvider.add(Provider.VirtualRouter);
 +                final Set<Network.Provider> sgProvider = new HashSet<Network.Provider>();
 +                sgProvider.add(Provider.SecurityGroupProvider);
 +                final Set<Network.Provider> nsProvider = new HashSet<Network.Provider>();
 +                nsProvider.add(Provider.Netscaler);
 +                netscalerServiceProviders.put(Service.Dhcp, vrProvider);
 +                netscalerServiceProviders.put(Service.Dns, vrProvider);
 +                netscalerServiceProviders.put(Service.UserData, vrProvider);
 +                netscalerServiceProviders.put(Service.SecurityGroup, sgProvider);
 +                netscalerServiceProviders.put(Service.StaticNat, nsProvider);
 +                netscalerServiceProviders.put(Service.Lb, nsProvider);
 +
 +                final Map<Service, Map<Capability, String>> serviceCapabilityMap = new HashMap<Service, Map<Capability, String>>();
 +                final Map<Capability, String> elb = new HashMap<Capability, String>();
 +                elb.put(Capability.ElasticLb, "true");
 +                final Map<Capability, String> eip = new HashMap<Capability, String>();
 +                eip.put(Capability.ElasticIp, "true");
 +                serviceCapabilityMap.put(Service.Lb, elb);
 +                serviceCapabilityMap.put(Service.StaticNat, eip);
 +
 +                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedEIPandELBNetworkOffering) == null) {
 +                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering,
 +                            "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, null, true, Availability.Optional, null,
 +                            netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false);
 +                    offering.setState(NetworkOffering.State.Enabled);
 +                    offering.setDedicatedLB(false);
 +                    _networkOfferingDao.update(offering.getId(), offering);
 +                }
 +
 +                _networkOfferingDao.persistDefaultL2NetworkOfferings();
 +            }
 +        });
 +
 +        AssignIpAddressSearch = _ipAddressDao.createSearchBuilder();
 +        AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ);
 +        AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL);
 +        AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN);
 +        final SearchBuilder<VlanVO> vlanSearch = _vlanDao.createSearchBuilder();
 +        vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ);
 +        vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ);
 +        AssignIpAddressSearch.join("vlan", vlanSearch, vlanSearch.entity().getId(), AssignIpAddressSearch.entity().getVlanId(), JoinType.INNER);
 +        AssignIpAddressSearch.done();
 +
 +        AssignIpAddressFromPodVlanSearch = _ipAddressDao.createSearchBuilder();
 +        AssignIpAddressFromPodVlanSearch.and("dc", AssignIpAddressFromPodVlanSearch.entity().getDataCenterId(), Op.EQ);
 +        AssignIpAddressFromPodVlanSearch.and("allocated", AssignIpAddressFromPodVlanSearch.entity().getAllocatedTime(), Op.NULL);
 +        AssignIpAddressFromPodVlanSearch.and("vlanId", AssignIpAddressFromPodVlanSearch.entity().getVlanId(), Op.IN);
 +
 +        final SearchBuilder<VlanVO> podVlanSearch = _vlanDao.createSearchBuilder();
 +        podVlanSearch.and("type", podVlanSearch.entity().getVlanType(), Op.EQ);
 +        podVlanSearch.and("networkId", podVlanSearch.entity().getNetworkId(), Op.EQ);
 +        final SearchBuilder<PodVlanMapVO> podVlanMapSB = _podVlanMapDao.createSearchBuilder();
 +        podVlanMapSB.and("podId", podVlanMapSB.entity().getPodId(), Op.EQ);
 +        AssignIpAddressFromPodVlanSearch.join("podVlanMapSB", podVlanMapSB, podVlanMapSB.entity().getVlanDbId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(),
 +                JoinType.INNER);
 +        AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), JoinType.INNER);
 +
 +        AssignIpAddressFromPodVlanSearch.done();
 +
 +        _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Network-Scavenger"));
 +
 +        _agentMgr.registerForHostEvents(this, true, false, true);
 +
 +        Network.State.getStateMachine().registerListener(new NetworkStateListener(_configDao));
 +
 +        s_logger.info("Network Manager is configured.");
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean start() {
 +        final int netGcInterval = NumbersUtil.parseInt(_configDao.getValue(NetworkGcInterval.key()), 60);
 +        s_logger.info("Network Manager will run the NetworkGarbageCollector every '" + netGcInterval + "' seconds.");
 +
 +        _executor.scheduleWithFixedDelay(new NetworkGarbageCollector(), netGcInterval, netGcInterval, TimeUnit.SECONDS);
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean stop() {
 +        return true;
 +    }
 +
 +    protected NetworkOrchestrator() {
 +        setStateMachine();
 +    }
 +
 +    @Override
 +    public List<? extends Network> setupNetwork(final Account owner, final NetworkOffering offering, final DeploymentPlan plan, final String name, final String displayText, final boolean isDefault)
 +            throws ConcurrentOperationException {
 +        return setupNetwork(owner, offering, null, plan, name, displayText, false, null, null, null, null, true);
 +    }
 +
 +    @Override
 +    @DB
 +    public List<? extends Network> setupNetwork(final Account owner, final NetworkOffering offering, final Network predefined, final DeploymentPlan plan, final String name,
 +            final String displayText, final boolean errorIfAlreadySetup, final Long domainId, final ACLType aclType, final Boolean subdomainAccess, final Long vpcId,
 +            final Boolean isDisplayNetworkEnabled) throws ConcurrentOperationException {
 +
 +        final Account locked = _accountDao.acquireInLockTable(owner.getId());
 +        if (locked == null) {
 +            throw new ConcurrentOperationException("Unable to acquire lock on " + owner);
 +        }
 +
 +        try {
 +            if (predefined == null
 +                    || offering.getTrafficType() != TrafficType.Guest && predefined.getCidr() == null && predefined.getBroadcastUri() == null && !(predefined
 +                            .getBroadcastDomainType() == BroadcastDomainType.Vlan || predefined.getBroadcastDomainType() == BroadcastDomainType.Lswitch || predefined
 +                            .getBroadcastDomainType() == BroadcastDomainType.Vxlan)) {
 +                final List<NetworkVO> configs = _networksDao.listBy(owner.getId(), offering.getId(), plan.getDataCenterId());
 +                if (configs.size() > 0) {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Found existing network configuration for offering " + offering + ": " + configs.get(0));
 +                    }
 +
 +                    if (errorIfAlreadySetup) {
 +                        final InvalidParameterValueException ex = new InvalidParameterValueException(
 +                                "Found existing network configuration (with specified id) for offering (with specified id)");
 +                        ex.addProxyObject(offering.getUuid(), "offeringId");
 +                        ex.addProxyObject(configs.get(0).getUuid(), "networkConfigId");
 +                        throw ex;
 +                    } else {
 +                        return configs;
 +                    }
 +                }
 +            }
 +
 +            final List<NetworkVO> networks = new ArrayList<NetworkVO>();
 +
 +            long related = -1;
 +
 +            for (final NetworkGuru guru : networkGurus) {
 +                final Network network = guru.design(offering, plan, predefined, owner);
 +                if (network == null) {
 +                    continue;
 +                }
 +
 +                if (network.getId() != -1) {
 +                    if (network instanceof NetworkVO) {
 +                        networks.add((NetworkVO)network);
 +                    } else {
 +                        networks.add(_networksDao.findById(network.getId()));
 +                    }
 +                    continue;
 +                }
 +
 +                final long id = _networksDao.getNextInSequence(Long.class, "id");
 +                if (related == -1) {
 +                    related = id;
 +                }
 +
 +                final long relatedFile = related;
 +                Transaction.execute(new TransactionCallbackNoReturn() {
 +                    @Override
 +                    public void doInTransactionWithoutResult(final TransactionStatus status) {
 +                        final NetworkVO vo = new NetworkVO(id, network, offering.getId(), guru.getName(), owner.getDomainId(), owner.getId(), relatedFile, name, displayText, predefined
 +                                .getNetworkDomain(), offering.getGuestType(), plan.getDataCenterId(), plan.getPhysicalNetworkId(), aclType, offering.isSpecifyIpRanges(),
 +                                vpcId, offering.isRedundantRouter(), predefined.getExternalId());
 +                        vo.setDisplayNetwork(isDisplayNetworkEnabled == null ? true : isDisplayNetworkEnabled);
 +                        vo.setStrechedL2Network(offering.isSupportingStrechedL2());
 +                        final NetworkVO networkPersisted = _networksDao.persist(vo, vo.getGuestType() == Network.GuestType.Isolated,
 +                                finalizeServicesAndProvidersForNetwork(offering, plan.getPhysicalNetworkId()));
 +                        networks.add(networkPersisted);
 +
 +                        if (predefined instanceof NetworkVO && guru instanceof NetworkGuruAdditionalFunctions){
 +                            final NetworkGuruAdditionalFunctions functions = (NetworkGuruAdditionalFunctions) guru;
 +                            functions.finalizeNetworkDesign(networkPersisted.getId(), ((NetworkVO)predefined).getVlanIdAsUUID());
 +                        }
 +
 +                        if (domainId != null && aclType == ACLType.Domain) {
 +                            _networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null ? true : subdomainAccess);
 +                        }
 +                    }
 +                });
 +            }
 +
 +            if (networks.size() < 1) {
 +                // see networkOfferingVO.java
 +                final CloudRuntimeException ex = new CloudRuntimeException("Unable to convert network offering with specified id to network profile");
 +                ex.addProxyObject(offering.getUuid(), "offeringId");
 +                throw ex;
 +            }
 +
 +            return networks;
 +        } finally {
 +            s_logger.debug("Releasing lock for " + locked);
 +            _accountDao.releaseFromLockTable(locked.getId());
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public void allocate(final VirtualMachineProfile vm, final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final Map<String, Map<Integer, String>> extraDhcpOptions) throws InsufficientCapacityException,
 +    ConcurrentOperationException {
 +
 +        Transaction.execute(new TransactionCallbackWithExceptionNoReturn<InsufficientCapacityException>() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus status) throws InsufficientCapacityException {
 +                int deviceId = 0;
 +                int size = 0;
 +                for (final Network ntwk : networks.keySet()) {
 +                    final List<? extends NicProfile> profiles = networks.get(ntwk);
 +                    if (profiles != null && !profiles.isEmpty()) {
 +                        size = size + profiles.size();
 +                    } else {
 +                        size = size + 1;
 +                    }
 +                }
 +
 +                final boolean[] deviceIds = new boolean[size];
 +                Arrays.fill(deviceIds, false);
 +
 +                final List<NicProfile> nics = new ArrayList<NicProfile>(size);
 +                NicProfile defaultNic = null;
 +
 +                for (final Map.Entry<? extends Network, List<? extends NicProfile>> network : networks.entrySet()) {
 +                    final Network config = network.getKey();
 +                    List<? extends NicProfile> requestedProfiles = network.getValue();
 +                    if (requestedProfiles == null) {
 +                        requestedProfiles = new ArrayList<NicProfile>();
 +                    }
 +                    if (requestedProfiles.isEmpty()) {
 +                        requestedProfiles.add(null);
 +                    }
 +
 +                    for (final NicProfile requested : requestedProfiles) {
 +                        Boolean isDefaultNic = false;
 +                        if (vm != null && requested != null && requested.isDefaultNic()) {
 +                            isDefaultNic = true;
 +                        }
 +
 +                        while (deviceIds[deviceId] && deviceId < deviceIds.length) {
 +                            deviceId++;
 +                        }
 +
 +                        final Pair<NicProfile, Integer> vmNicPair = allocateNic(requested, config, isDefaultNic, deviceId, vm);
 +                        NicProfile vmNic = null;
 +                        if (vmNicPair != null) {
 +                            vmNic = vmNicPair.first();
 +                            if (vmNic == null) {
 +                                continue;
 +                            }
 +                            deviceId = vmNicPair.second();
 +                        }
 +
 +                        final int devId = vmNic.getDeviceId();
 +                        if (devId >= deviceIds.length) {
 +                            throw new IllegalArgumentException("Device id for nic is too large: " + vmNic);
 +                        }
 +                        if (deviceIds[devId]) {
 +                            throw new IllegalArgumentException("Conflicting device id for two different nics: " + vmNic);
 +                        }
 +
 +                        deviceIds[devId] = true;
 +
 +                        if (vmNic.isDefaultNic()) {
 +                            if (defaultNic != null) {
 +                                throw new IllegalArgumentException("You cannot specify two nics as default nics: nic 1 = " + defaultNic + "; nic 2 = " + vmNic);
 +                            }
 +                            defaultNic = vmNic;
 +                        }
 +
 +                        nics.add(vmNic);
 +                        vm.addNic(vmNic);
 +                        saveExtraDhcpOptions(config.getUuid(), vmNic.getId(), extraDhcpOptions);
 +                    }
 +                }
 +                if (nics.size() != size) {
 +                    s_logger.warn("Number of nics " + nics.size() + " doesn't match number of requested nics " + size);
 +                    throw new CloudRuntimeException("Number of nics " + nics.size() + " doesn't match number of requested networks " + size);
 +                }
 +
 +                if (nics.size() == 1) {
 +                    nics.get(0).setDefaultNic(true);
 +                }
 +            }
 +        });
 +    }
 +
 +    @Override
 +    public void saveExtraDhcpOptions(final String networkUuid, final Long nicId, final Map<String, Map<Integer, String>> extraDhcpOptionMap) {
 +
 +        if(extraDhcpOptionMap != null) {
 +            Map<Integer, String> extraDhcpOption = extraDhcpOptionMap.get(networkUuid);
 +            if(extraDhcpOption != null) {
 +                List<NicExtraDhcpOptionVO> nicExtraDhcpOptionList = new LinkedList<>();
 +
 +                for (Integer code : extraDhcpOption.keySet()) {
 +                    Dhcp.DhcpOptionCode.valueOfInt(code); //check if code is supported or not.
 +                    NicExtraDhcpOptionVO nicExtraDhcpOptionVO = new NicExtraDhcpOptionVO(nicId, code, extraDhcpOption.get(code));
 +                    nicExtraDhcpOptionList.add(nicExtraDhcpOptionVO);
 +                }
 +                _nicExtraDhcpOptionDao.saveExtraDhcpOptions(nicExtraDhcpOptionList);
 +            }
 +        }
 +    }
 +
 +    @DB
 +    @Override
 +    public Pair<NicProfile, Integer> allocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm)
 +            throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException {
 +
 +        final NetworkVO ntwkVO = _networksDao.findById(network.getId());
 +        s_logger.debug("Allocating nic for vm " + vm.getVirtualMachine() + " in network " + network + " with requested profile " + requested);
 +        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, ntwkVO.getGuruName());
 +
 +        if (requested != null && requested.getMode() == null) {
 +            requested.setMode(network.getMode());
 +        }
 +        final NicProfile profile = guru.allocate(network, requested, vm);
 +        if (profile == null) {
 +            return null;
 +        }
 +
 +        if (isDefaultNic != null) {
 +            profile.setDefaultNic(isDefaultNic);
 +        }
 +
 +        if (requested != null && requested.getMode() == null) {
 +            profile.setMode(requested.getMode());
 +        } else {
 +            profile.setMode(network.getMode());
 +        }
 +
 +        NicVO vo = new NicVO(guru.getName(), vm.getId(), network.getId(), vm.getType());
 +
 +        DataCenterVO dcVo = _dcDao.findById(network.getDataCenterId());
 +        if (dcVo.getNetworkType() == NetworkType.Basic) {
 +            configureNicProfileBasedOnRequestedIp(requested, profile, network);
 +        }
 +
 +        deviceId = applyProfileToNic(vo, profile, deviceId);
 +
 +        vo = _nicDao.persist(vo);
 +
 +        final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
 +        final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
 +                _networkModel.getNetworkTag(vm.getHypervisorType(), network));
 +
 +        return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
 +    }
 +
 +    /**
 +     * If the requested IPv4 address from the NicProfile was configured then it configures the IPv4 address, Netmask and Gateway to deploy the VM with the requested IP.
 +     */
 +    protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProfile, NicProfile nicProfile, Network network) {
 +        if (requestedNicProfile == null) {
 +            return;
 +        }
 +        String requestedIpv4Address = requestedNicProfile.getRequestedIPv4();
 +        if (requestedIpv4Address == null) {
 +            return;
 +        }
 +        if (!NetUtils.isValidIp4(requestedIpv4Address)) {
 +            throw new InvalidParameterValueException(String.format("The requested [IPv4 address='%s'] is not a valid IP address", requestedIpv4Address));
 +        }
 +
 +        VlanVO vlanVo = _vlanDao.findByNetworkIdAndIpv4(network.getId(), requestedIpv4Address);
 +        if (vlanVo == null) {
 +            throw new InvalidParameterValueException(String.format("Trying to configure a Nic with the requested [IPv4='%s'] but cannot find a Vlan for the [network id='%s']",
 +                    requestedIpv4Address, network.getId()));
 +        }
 +
 +        String ipv4Gateway = vlanVo.getVlanGateway();
 +        String ipv4Netmask = vlanVo.getVlanNetmask();
 +
 +        if (!NetUtils.isValidIp4(ipv4Gateway)) {
 +            throw new InvalidParameterValueException(String.format("The [IPv4Gateway='%s'] from [VlanId='%s'] is not valid", ipv4Gateway, vlanVo.getId()));
 +        }
 +        if (!NetUtils.isValidIp4Netmask(ipv4Netmask)) {
 +            throw new InvalidParameterValueException(String.format("The [IPv4Netmask='%s'] from [VlanId='%s'] is not valid", ipv4Netmask, vlanVo.getId()));
 +        }
 +
 +        acquireLockAndCheckIfIpv4IsFree(network, requestedIpv4Address);
 +
 +        nicProfile.setIPv4Address(requestedIpv4Address);
 +        nicProfile.setIPv4Gateway(ipv4Gateway);
 +        nicProfile.setIPv4Netmask(ipv4Netmask);
 +
 +        if (nicProfile.getMacAddress() == null) {
 +            try {
 +                String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId());
 +                nicProfile.setMacAddress(macAddress);
 +            } catch (InsufficientAddressCapacityException e) {
 +                throw new CloudRuntimeException(String.format("Cannot get next available mac address in [network id='%s']", network.getId()), e);
 +            }
 +        }
 +    }
 +
 +    /**
 +     *  Acquires lock in "user_ip_address" and checks if the requested IPv4 address is Free.
 +     */
 +    protected void acquireLockAndCheckIfIpv4IsFree(Network network, String requestedIpv4Address) {
 +        IPAddressVO ipVO = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), requestedIpv4Address);
 +        if (ipVO == null) {
 +            throw new InvalidParameterValueException(
 +                    String.format("Cannot find IPAddressVO for guest [IPv4 address='%s'] and [network id='%s']", requestedIpv4Address, network.getId()));
 +        }
 +        try {
 +            IPAddressVO lockedIpVO = _ipAddressDao.acquireInLockTable(ipVO.getId());
 +            validateLockedRequestedIp(ipVO, lockedIpVO);
 +            lockedIpVO.setState(IPAddressVO.State.Allocated);
 +            _ipAddressDao.update(lockedIpVO.getId(), lockedIpVO);
 +        } finally {
 +            _ipAddressDao.releaseFromLockTable(ipVO.getId());
 +        }
 +    }
 +
 +    /**
 +     * Validates the locked IP, throwing an exeption if the locked IP is null or the locked IP is not in 'Free' state.
 +     */
 +    protected void validateLockedRequestedIp(IPAddressVO ipVO, IPAddressVO lockedIpVO) {
 +        if (lockedIpVO == null) {
 +            throw new InvalidParameterValueException(String.format("Cannot acquire guest [IPv4 address='%s'] as it was removed while acquiring lock", ipVO.getAddress()));
 +        }
 +        if (lockedIpVO.getState() != IPAddressVO.State.Free) {
 +            throw new InvalidParameterValueException(
 +                    String.format("Cannot acquire guest [IPv4 address='%s']; The Ip address is in [state='%s']", ipVO.getAddress(), lockedIpVO.getState().toString()));
 +        }
 +    }
 +
 +    protected Integer applyProfileToNic(final NicVO vo, final NicProfile profile, Integer deviceId) {
 +        if (profile.getDeviceId() != null) {
 +            vo.setDeviceId(profile.getDeviceId());
 +        } else if (deviceId != null) {
 +            vo.setDeviceId(deviceId++);
 +        }
 +
 +        if (profile.getReservationStrategy() != null) {
 +            vo.setReservationStrategy(profile.getReservationStrategy());
 +        }
 +
 +        vo.setDefaultNic(profile.isDefaultNic());
 +
 +        vo.setIPv4Address(profile.getIPv4Address());
 +        vo.setAddressFormat(profile.getFormat());
 +
 +        if (profile.getMacAddress() != null) {
 +            vo.setMacAddress(profile.getMacAddress());
 +        }
 +
 +        vo.setMode(profile.getMode());
 +        vo.setIPv4Netmask(profile.getIPv4Netmask());
 +        vo.setIPv4Gateway(profile.getIPv4Gateway());
 +
 +        if (profile.getBroadCastUri() != null) {
 +            vo.setBroadcastUri(profile.getBroadCastUri());
 +        }
 +
 +        if (profile.getIsolationUri() != null) {
 +            vo.setIsolationUri(profile.getIsolationUri());
 +        }
 +
 +        vo.setState(Nic.State.Allocated);
 +
 +        vo.setIPv6Address(profile.getIPv6Address());
 +        vo.setIPv6Gateway(profile.getIPv6Gateway());
 +        vo.setIPv6Cidr(profile.getIPv6Cidr());
 +
 +        return deviceId;
 +    }
 +
 +    protected void applyProfileToNicForRelease(final NicVO vo, final NicProfile profile) {
 +        vo.setIPv4Gateway(profile.getIPv4Gateway());
 +        vo.setAddressFormat(profile.getFormat());
 +        vo.setIPv4Address(profile.getIPv4Address());
 +        vo.setIPv6Address(profile.getIPv6Address());
 +        vo.setMacAddress(profile.getMacAddress());
 +        if (profile.getReservationStrategy() != null) {
 +            vo.setReservationStrategy(profile.getReservationStrategy());
 +        }
 +        vo.setBroadcastUri(profile.getBroadCastUri());
 +        vo.setIsolationUri(profile.getIsolationUri());
 +        vo.setIPv4Netmask(profile.getIPv4Netmask());
 +    }
 +
 +    protected void applyProfileToNetwork(final NetworkVO network, final NetworkProfile profile) {
 +        network.setBroadcastUri(profile.getBroadcastUri());
 +        network.setDns1(profile.getDns1());
 +        network.setDns2(profile.getDns2());
 +        network.setPhysicalNetworkId(profile.getPhysicalNetworkId());
 +    }
 +
 +    protected NicTO toNicTO(final NicVO nic, final NicProfile profile, final NetworkVO config) {
 +        final NicTO to = new NicTO();
 +        to.setDeviceId(nic.getDeviceId());
 +        to.setBroadcastType(config.getBroadcastDomainType());
 +        to.setType(config.getTrafficType());
 +        to.setIp(nic.getIPv4Address());
 +        to.setNetmask(nic.getIPv4Netmask());
 +        to.setMac(nic.getMacAddress());
 +        to.setDns1(profile.getIPv4Dns1());
 +        to.setDns2(profile.getIPv4Dns2());
 +        if (nic.getIPv4Gateway() != null) {
 +            to.setGateway(nic.getIPv4Gateway());
 +        } else {
 +            to.setGateway(config.getGateway());
 +        }
 +        if (nic.getVmType() != VirtualMachine.Type.User) {
 +            to.setPxeDisable(true);
 +        }
 +        to.setDefaultNic(nic.isDefaultNic());
 +        to.setBroadcastUri(nic.getBroadcastUri());
 +        to.setIsolationuri(nic.getIsolationUri());
 +        if (profile != null) {
 +            to.setDns1(profile.getIPv4Dns1());
 +            to.setDns2(profile.getIPv4Dns2());
 +        }
 +
 +        final Integer networkRate = _networkModel.getNetworkRate(config.getId(), null);
 +        to.setNetworkRateMbps(networkRate);
 +
 +        to.setUuid(config.getUuid());
 +
 +        return to;
 +    }
 +
 +    boolean isNetworkImplemented(final NetworkVO network) {
 +        final Network.State state = network.getState();
 +        if (state == Network.State.Implemented) {
 +            return true;
 +        } else if (state == Network.State.Setup) {
 +            final DataCenterVO zone = _dcDao.findById(network.getDataCenterId());
 +            if (!isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()) || zone.getNetworkType() == NetworkType.Basic) {
 +                return true;
 +            }
 +        }
 +        return false;
 +    }
 +
 +    Pair<NetworkGuru, NetworkVO> implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context, final boolean isRouter) throws ConcurrentOperationException,
 +    ResourceUnavailableException, InsufficientCapacityException {
 +        Pair<NetworkGuru, NetworkVO> implemented = null;
 +        if (!isRouter) {
 +            implemented = implementNetwork(networkId, dest, context);
 +        } else {
 +            // At the time of implementing network (using implementNetwork() method), if the VR needs to be deployed then
 +            // it follows the same path of regular VM deployment. This leads to a nested call to implementNetwork() while
 +            // preparing VR nics. This flow creates issues in dealing with network state transitions. The original call
 +            // puts network in "Implementing" state and then the nested call again tries to put it into same state resulting
 +            // in issues. In order to avoid it, implementNetwork() call for VR is replaced with below code.
 +            final NetworkVO network = _networksDao.findById(networkId);
 +            final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +            implemented = new Pair<NetworkGuru, NetworkVO>(guru, network);
 +        }
 +        return implemented;
 +    }
 +
 +    @Override
 +    @DB
 +    public Pair<NetworkGuru, NetworkVO> implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException,
 +    ResourceUnavailableException, InsufficientCapacityException {
 +        final Pair<NetworkGuru, NetworkVO> implemented = new Pair<NetworkGuru, NetworkVO>(null, null);
 +
 +        NetworkVO network = _networksDao.findById(networkId);
 +        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +        if (isNetworkImplemented(network)) {
 +            s_logger.debug("Network id=" + networkId + " is already implemented");
 +            implemented.set(guru, network);
 +            return implemented;
 +        }
 +
 +        // Acquire lock only when network needs to be implemented
 +        network = _networksDao.acquireInLockTable(networkId, NetworkLockTimeout.value());
 +        if (network == null) {
 +            // see NetworkVO.java
 +            final ConcurrentOperationException ex = new ConcurrentOperationException("Unable to acquire network configuration");
 +            ex.addProxyObject(_entityMgr.findById(Network.class, networkId).getUuid());
 +            throw ex;
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Lock is acquired for network id " + networkId + " as a part of network implement");
 +        }
 +
 +        try {
 +            if (isNetworkImplemented(network)) {
 +                s_logger.debug("Network id=" + networkId + " is already implemented");
 +                implemented.set(guru, network);
 +                return implemented;
 +            }
 +
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Asking " + guru.getName() + " to implement " + network);
 +            }
 +
 +            final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +
 +            network.setReservationId(context.getReservationId());
 +            if (isSharedNetworkWithServices(network)) {
 +                network.setState(Network.State.Implementing);
 +            } else {
 +                stateTransitTo(network, Event.ImplementNetwork);
 +            }
 +
 +            final Network result = guru.implement(network, offering, dest, context);
 +            network.setCidr(result.getCidr());
 +            network.setBroadcastUri(result.getBroadcastUri());
 +            network.setGateway(result.getGateway());
 +            network.setMode(result.getMode());
 +            network.setPhysicalNetworkId(result.getPhysicalNetworkId());
 +            _networksDao.update(networkId, network);
 +
 +            // implement network elements and re-apply all the network rules
 +            implementNetworkElementsAndResources(dest, context, network, offering);
 +
 +            if (isSharedNetworkWithServices(network)) {
 +                network.setState(Network.State.Implemented);
 +            } else {
 +                stateTransitTo(network, Event.OperationSucceeded);
 +            }
 +
 +            network.setRestartRequired(false);
 +            _networksDao.update(network.getId(), network);
 +            implemented.set(guru, network);
 +            return implemented;
 +        } catch (final NoTransitionException e) {
 +            s_logger.error(e.getMessage());
 +            return null;
 +        } catch (final CloudRuntimeException e) {
 +            s_logger.error("Caught exception: " + e.getMessage());
 +            return null;
 +        } finally {
 +            if (implemented.first() == null) {
 +                s_logger.debug("Cleaning up because we're unable to implement the network " + network);
 +                try {
 +                    if (isSharedNetworkWithServices(network)) {
 +                        network.setState(Network.State.Shutdown);
 +                        _networksDao.update(networkId, network);
 +                    } else {
 +                        stateTransitTo(network, Event.OperationFailed);
 +                    }
 +                } catch (final NoTransitionException e) {
 +                    s_logger.error(e.getMessage());
 +                }
 +
 +                try {
 +                    shutdownNetwork(networkId, context, false);
 +                } catch (final Exception e) {
 +                    // Don't throw this exception as it would hide the original thrown exception, just log
 +                    s_logger.error("Exception caught while shutting down a network as part of a failed implementation", e);
 +                }
 +            }
 +
 +            _networksDao.releaseFromLockTable(networkId);
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Lock is released for network id " + networkId + " as a part of network implement");
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void implementNetworkElementsAndResources(final DeployDestination dest, final ReservationContext context, final Network network, final NetworkOffering offering)
 +            throws ConcurrentOperationException, InsufficientAddressCapacityException, ResourceUnavailableException, InsufficientCapacityException {
 +
 +        // Associate a source NAT IP (if one isn't already associated with the network) if this is a
 +        //     1) 'Isolated' or 'Shared' guest virtual network in the advance zone
 +        //     2) network has sourceNat service
 +        //     3) network offering does not support a shared source NAT rule
 +
 +        final boolean sharedSourceNat = offering.isSharedSourceNat();
 +        final DataCenter zone = _dcDao.findById(network.getDataCenterId());
 +
 +        if (!sharedSourceNat && _networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat)
 +                && (network.getGuestType() == Network.GuestType.Isolated || network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) {
 +
 +            List<IPAddressVO> ips = null;
 +            final Account owner = _entityMgr.findById(Account.class, network.getAccountId());
 +            if (network.getVpcId() != null) {
 +                ips = _ipAddressDao.listByAssociatedVpc(network.getVpcId(), true);
 +                if (ips.isEmpty()) {
 +                    final Vpc vpc = _vpcMgr.getActiveVpc(network.getVpcId());
 +                    s_logger.debug("Creating a source nat ip for vpc " + vpc);
 +                    _vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc);
 +                }
 +            } else {
 +                ips = _ipAddressDao.listByAssociatedNetwork(network.getId(), true);
 +                if (ips.isEmpty()) {
 +                    s_logger.debug("Creating a source nat ip for network " + network);
 +                    _ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network);
 +                }
 +            }
 +        }
 +        // get providers to implement
 +        final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +        implementNetworkElements(dest, context, network, offering, providersToImplement);
 +
 +        //Reset the extra DHCP option that may have been cleared per nic.
 +        List<NicVO> nicVOs = _nicDao.listByNetworkId(network.getId());
 +        for(NicVO nicVO : nicVOs) {
 +            if(nicVO.getState() == Nic.State.Reserved) {
 +                configureExtraDhcpOptions(network, nicVO.getId());
 +            }
 +        }
 +
 +        for (final NetworkElement element : networkElements) {
 +            if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
 +                ((AggregatedCommandExecutor)element).prepareAggregatedExecution(network, dest);
 +            }
 +        }
 +
 +        try {
 +            // reapply all the firewall/staticNat/lb rules
 +            s_logger.debug("Reprogramming network " + network + " as a part of network implement");
 +            if (!reprogramNetworkRules(network.getId(), CallContext.current().getCallingAccount(), network)) {
 +                s_logger.warn("Failed to re-program the network as a part of network " + network + " implement");
 +                // see DataCenterVO.java
 +                final ResourceUnavailableException ex = new ResourceUnavailableException("Unable to apply network rules as a part of network " + network + " implement", DataCenter.class,
 +                        network.getDataCenterId());
 +                ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid());
 +                throw ex;
 +            }
 +            for (final NetworkElement element : networkElements) {
 +                if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
 +                    if (!((AggregatedCommandExecutor)element).completeAggregatedExecution(network, dest)) {
 +                        s_logger.warn("Failed to re-program the network as a part of network " + network + " implement due to aggregated commands execution failure!");
 +                        // see DataCenterVO.java
 +                        final ResourceUnavailableException ex = new ResourceUnavailableException("Unable to apply network rules as a part of network " + network + " implement", DataCenter.class,
 +                                network.getDataCenterId());
 +                        ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid());
 +                        throw ex;
 +                    }
 +                }
 +            }
 +        } finally {
 +            for (final NetworkElement element : networkElements) {
 +                if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
 +                    ((AggregatedCommandExecutor)element).cleanupAggregatedExecution(network, dest);
 +                }
 +            }
 +        }
 +    }
 +
 +    private void implementNetworkElements(final DeployDestination dest, final ReservationContext context, final Network network, final NetworkOffering offering, final List<Provider> providersToImplement)
 +            throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
 +        for (NetworkElement element : networkElements) {
 +            if (providersToImplement.contains(element.getProvider())) {
 +                if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
 +                    // The physicalNetworkId will not get translated into a uuid by the reponse serializer,
 +                    // because the serializer would look up the NetworkVO class's table and retrieve the
 +                    // network id instead of the physical network id.
 +                    // So just throw this exception as is. We may need to TBD by changing the serializer.
 +                    throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
 +                            + network.getPhysicalNetworkId());
 +                }
 +
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Asking " + element.getName() + " to implemenet " + network);
 +                }
 +
 +                if (!element.implement(network, offering, dest, context)) {
 +                    CloudRuntimeException ex = new CloudRuntimeException("Failed to implement provider " + element.getProvider().getName() + " for network with specified id");
 +                    ex.addProxyObject(network.getUuid(), "networkId");
 +                    throw ex;
 +                }
 +            }
 +        }
 +    }
 +
 +    // This method re-programs the rules/ips for existing network
 +    protected boolean reprogramNetworkRules(final long networkId, final Account caller, final Network network) throws ResourceUnavailableException {
 +        boolean success = true;
 +
 +        //Apply egress rules first to effect the egress policy early on the guest traffic
 +        final List<FirewallRuleVO> firewallEgressRulesToApply = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Egress);
 +        final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +        final DataCenter zone = _dcDao.findById(network.getDataCenterId());
 +        if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) && _networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall)
 +                && (network.getGuestType() == Network.GuestType.Isolated || network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) {
 +            // add default egress rule to accept the traffic
 +            _firewallMgr.applyDefaultEgressFirewallRule(network.getId(), offering.isEgressDefaultPolicy(), true);
 +        }
 +        if (!_firewallMgr.applyFirewallRules(firewallEgressRulesToApply, false, caller)) {
 +            s_logger.warn("Failed to reapply firewall Egress rule(s) as a part of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // associate all ip addresses
 +        if (!_ipAddrMgr.applyIpAssociations(network, false)) {
 +            s_logger.warn("Failed to apply ip addresses as a part of network id" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply static nat
 +        if (!_rulesMgr.applyStaticNatsForNetwork(networkId, false, caller)) {
 +            s_logger.warn("Failed to apply static nats a part of network id" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply firewall rules
 +        final List<FirewallRuleVO> firewallIngressRulesToApply = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Ingress);
 +        if (!_firewallMgr.applyFirewallRules(firewallIngressRulesToApply, false, caller)) {
 +            s_logger.warn("Failed to reapply Ingress firewall rule(s) as a part of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply port forwarding rules
 +        if (!_rulesMgr.applyPortForwardingRulesForNetwork(networkId, false, caller)) {
 +            s_logger.warn("Failed to reapply port forwarding rule(s) as a part of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply static nat rules
 +        if (!_rulesMgr.applyStaticNatRulesForNetwork(networkId, false, caller)) {
 +            s_logger.warn("Failed to reapply static nat rule(s) as a part of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply public load balancer rules
 +        if (!_lbMgr.applyLoadBalancersForNetwork(networkId, Scheme.Public)) {
 +            s_logger.warn("Failed to reapply Public load balancer rules as a part of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply internal load balancer rules
 +        if (!_lbMgr.applyLoadBalancersForNetwork(networkId, Scheme.Internal)) {
 +            s_logger.warn("Failed to reapply internal load balancer rules as a part of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        // apply vpn rules
 +        final List<? extends RemoteAccessVpn> vpnsToReapply = _vpnMgr.listRemoteAccessVpns(networkId);
 +        if (vpnsToReapply != null) {
 +            for (final RemoteAccessVpn vpn : vpnsToReapply) {
 +                // Start remote access vpn per ip
 +                if (_vpnMgr.startRemoteAccessVpn(vpn.getServerAddressId(), false) == null) {
 +                    s_logger.warn("Failed to reapply vpn rules as a part of network id=" + networkId + " restart");
 +                    success = false;
 +                }
 +            }
 +        }
 +
 +        //apply network ACLs
 +        if (!_networkACLMgr.applyACLToNetwork(networkId)) {
 +            s_logger.warn("Failed to reapply network ACLs as a part of  of network id=" + networkId + " restart");
 +            success = false;
 +        }
 +
 +        return success;
 +    }
 +
 +    protected boolean prepareElement(final NetworkElement element, final Network network, final NicProfile profile, final VirtualMachineProfile vmProfile, final DeployDestination dest,
 +            final ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
 +        element.prepare(network, profile, vmProfile, dest, context);
 +        if (vmProfile.getType() == Type.User && element.getProvider() != null) {
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)
 +                    && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, element.getProvider()) && element instanceof DhcpServiceProvider) {
 +                final DhcpServiceProvider sp = (DhcpServiceProvider)element;
 +                if (isDhcpAccrossMultipleSubnetsSupported(sp)) {
 +                    if (!sp.configDhcpSupportForSubnet(network, profile, vmProfile, dest, context)) {
 +                        return false;
 +                    }
 +                }
 +                if(!sp.addDhcpEntry(network, profile, vmProfile, dest, context)) {
 +                    return false;
 +                }
 +            }
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dns)
 +                    && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, element.getProvider()) && element instanceof DnsServiceProvider) {
 +                final DnsServiceProvider sp = (DnsServiceProvider)element;
 +                if (profile.getIPv6Address() == null) {
 +                    if (!sp.configDnsSupportForSubnet(network, profile, vmProfile, dest, context)) {
 +                        return false;
 +                    }
 +                }
 +                if(!sp.addDnsEntry(network, profile, vmProfile, dest, context)) {
 +                    return false;
 +                }
 +            }
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData)
 +                    && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.UserData, element.getProvider()) && element instanceof UserDataServiceProvider) {
 +                final UserDataServiceProvider sp = (UserDataServiceProvider)element;
 +                if(!sp.addPasswordAndUserdata(network, profile, vmProfile, dest, context)){
 +                    return false;
 +                }
 +            }
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean canUpdateInSequence(Network network, boolean forced){
 +        List<Provider> providers = getNetworkProviders(network.getId());
 +
 +        //check if the there are no service provider other than virtualrouter.
 +        for(Provider provider : providers) {
 +            if (provider != Provider.VirtualRouter)
 +                throw new UnsupportedOperationException("Cannot update the network resources in sequence when providers other than virtualrouter are used");
 +        }
 +        //check if routers are in correct state before proceeding with the update
 +        List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER);
 +        for (DomainRouterVO router : routers){
 +            if (router.getRedundantState() == VirtualRouter.RedundantState.UNKNOWN) {
 +                if (!forced) {
 +                    throw new CloudRuntimeException("Domain router: "+router.getInstanceName()+" is in unknown state, Cannot update network. set parameter forced to true for forcing an update");
 +                }
 +            }
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public List<String> getServicesNotSupportedInNewOffering(Network network,long newNetworkOfferingId){
 +        NetworkOffering offering =_networkOfferingDao.findById(newNetworkOfferingId);
 +        List<String> services=_ntwkOfferingSrvcDao.listServicesForNetworkOffering(offering.getId());
 +        List<NetworkServiceMapVO> serviceMap= _ntwkSrvcDao.getServicesInNetwork(network.getId());
 +        List<String> servicesNotInNewOffering=new ArrayList<>();
 +        for(NetworkServiceMapVO serviceVO :serviceMap){
 +            boolean inlist=false;
 +            for(String service: services){
 +                if(serviceVO.getService().equalsIgnoreCase(service)){
 +                    inlist=true;
 +                    break;
 +                }
 +            }
 +            if(!inlist){
 +                //ignore Gateway service as this has no effect on the
 +                //behaviour of network.
 +                if(!serviceVO.getService().equalsIgnoreCase(Service.Gateway.getName()))
 +                    servicesNotInNewOffering.add(serviceVO.getService());
 +            }
 +        }
 +        return servicesNotInNewOffering;
 +    }
 +
 +    @Override
 +    public void cleanupConfigForServicesInNetwork(List<String> services, final Network network){
 +        long networkId=network.getId();
 +        Account caller=_accountDao.findById(Account.ACCOUNT_ID_SYSTEM);
 +        long userId=User.UID_SYSTEM;
 +        //remove all PF/Static Nat rules for the network
 +        s_logger.info("Services:"+services+" are no longer supported in network:"+network.getUuid()+
 +                " after applying new network offering:"+network.getNetworkOfferingId()+" removing the related configuration");
 +        if(services.contains(Service.StaticNat.getName())|| services.contains(Service.PortForwarding.getName())) {
 +            try {
 +                if (_rulesMgr.revokeAllPFStaticNatRulesForNetwork(networkId, userId, caller)) {
 +                    s_logger.debug("Successfully cleaned up portForwarding/staticNat rules for network id=" + networkId);
 +                } else {
 +                    s_logger.warn("Failed to release portForwarding/StaticNat rules as a part of network id=" + networkId + " cleanup");
 +                }
 +                if(services.contains(Service.StaticNat.getName())){
 +                    //removing static nat configured on ips.
 +                    //optimizing the db operations using transaction.
 +                    Transaction.execute(new TransactionCallbackNoReturn() {
 +                        @Override
 +                        public void doInTransactionWithoutResult(TransactionStatus status) {
 +                            List<IPAddressVO> ips = _ipAddressDao.listStaticNatPublicIps(network.getId());
 +                            for (IPAddressVO ip : ips) {
 +                                ip.setOneToOneNat(false);
 +                                ip.setAssociatedWithVmId(null);
 +                                ip.setVmIp(null);
 +                                _ipAddressDao.update(ip.getId(),ip);
 +                            }
 +                        }
 +                    });
 +                }
 +            } catch (ResourceUnavailableException ex) {
 +                s_logger.warn("Failed to release portForwarding/StaticNat rules as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex);
 +            }
 +        }
 +        if(services.contains(Service.SourceNat.getName())){
 +            Transaction.execute(new TransactionCallbackNoReturn() {
 +                @Override
 +                public void doInTransactionWithoutResult(TransactionStatus status) {
 +                    List<IPAddressVO> ips = _ipAddressDao.listByAssociatedNetwork(network.getId(),true);
 +                    //removing static nat configured on ips.
 +                    for (IPAddressVO ip : ips) {
 +                        ip.setSourceNat(false);
 +                        _ipAddressDao.update(ip.getId(),ip);
 +                    }
 +                }
 +            });
 +        }
 +        if(services.contains(Service.Lb.getName())){
 +            //remove all LB rules for the network
 +            if (_lbMgr.removeAllLoadBalanacersForNetwork(networkId, caller, userId)) {
 +                s_logger.debug("Successfully cleaned up load balancing rules for network id=" + networkId);
 +            } else {
 +                s_logger.warn("Failed to cleanup LB rules as a part of network id=" + networkId + " cleanup");
 +            }
 +        }
 +
 +        if(services.contains(Service.Firewall.getName())){
 +            //revoke all firewall rules for the network
 +            try {
 +                if (_firewallMgr.revokeAllFirewallRulesForNetwork(networkId, userId, caller)) {
 +                    s_logger.debug("Successfully cleaned up firewallRules rules for network id=" + networkId);
 +                } else {
 +                    s_logger.warn("Failed to cleanup Firewall rules as a part of network id=" + networkId + " cleanup");
 +                }
 +            } catch (ResourceUnavailableException ex) {
 +                s_logger.warn("Failed to cleanup Firewall rules as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex);
 +            }
 +        }
 +
 +        //do not remove vpn service for vpc networks.
 +        if(services.contains(Service.Vpn.getName()) && network.getVpcId()==null){
 +            RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByAccountAndNetwork(network.getAccountId(),networkId);
 +            try {
 +                _vpnMgr.destroyRemoteAccessVpnForIp(vpn.getServerAddressId(), caller, true);
 +            } catch (ResourceUnavailableException ex) {
 +                s_logger.warn("Failed to cleanup remote access vpn resources of network:"+network.getUuid() + " due to Exception: ", ex);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void configureUpdateInSequence(Network network) {
 +        List<Provider> providers = getNetworkProviders(network.getId());
 +        for (NetworkElement element : networkElements) {
 +            if (providers.contains(element.getProvider())) {
 +                if (element instanceof RedundantResource) {
 +                    ((RedundantResource) element).configureResource(network);
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public int getResourceCount(Network network){
 +        List<Provider> providers = getNetworkProviders(network.getId());
 +        int resourceCount=0;
 +        for (NetworkElement element : networkElements) {
 +            if (providers.contains(element.getProvider())) {
 +                //currently only one element implements the redundant resource interface
 +                if (element instanceof RedundantResource) {
 +                    resourceCount= ((RedundantResource) element).getResourceCount(network);
 +                    break;
 +                    }
 +                }
 +            }
 +        return resourceCount;
 +        }
 +
 +    @Override
 +    public void configureExtraDhcpOptions(Network network, long nicId, Map<Integer, String> extraDhcpOptions) {
 +        if(_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)) {
 +            if (_networkModel.getNetworkServiceCapabilities(network.getId(), Service.Dhcp).containsKey(Capability.ExtraDhcpOptions)) {
 +                DhcpServiceProvider sp = getDhcpServiceProvider(network);
 +                sp.setExtraDhcpOptions(network, nicId, extraDhcpOptions);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void configureExtraDhcpOptions(Network network, long nicId) {
 +        Map<Integer, String> extraDhcpOptions = getExtraDhcpOptions(nicId);
 +        configureExtraDhcpOptions(network, nicId, extraDhcpOptions);
 +    }
 +
 +    @Override
 +    public void finalizeUpdateInSequence(Network network, boolean success) {
 +        List<Provider> providers = getNetworkProviders(network.getId());
 +        for (NetworkElement element : networkElements) {
 +            if (providers.contains(element.getProvider())) {
 +                //currently only one element implements the redundant resource interface
 +                if (element instanceof RedundantResource) {
 +                    ((RedundantResource) element).finalize(network,success);
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +
 +    @DB
 +    protected void updateNic(final NicVO nic, final long networkId, final int count) {
 +        Transaction.execute(new TransactionCallbackNoReturn() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus status) {
 +                _nicDao.update(nic.getId(), nic);
 +
 +                if (nic.getVmType() == VirtualMachine.Type.User) {
 +                    s_logger.debug("Changing active number of nics for network id=" + networkId + " on " + count);
 +                    _networksDao.changeActiveNicsBy(networkId, count);
 +                }
 +
 +                if (nic.getVmType() == VirtualMachine.Type.User
 +                        || nic.getVmType() == VirtualMachine.Type.DomainRouter && _networksDao.findById(networkId).getTrafficType() == TrafficType.Guest) {
 +                    _networksDao.setCheckForGc(networkId);
 +                }
 +            }
 +        });
 +    }
 +
 +    @Override
 +    public void prepare(final VirtualMachineProfile vmProfile, final DeployDestination dest, final ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException,
 +    ResourceUnavailableException {
 +        final List<NicVO> nics = _nicDao.listByVmId(vmProfile.getId());
 +
 +        // we have to implement default nics first - to ensure that default network elements start up first in multiple
 +        //nics case
 +        // (need for setting DNS on Dhcp to domR's Ip4 address)
 +        Collections.sort(nics, new Comparator<NicVO>() {
 +
 +            @Override
 +            public int compare(final NicVO nic1, final NicVO nic2) {
 +                final boolean isDefault1 = nic1.isDefaultNic();
 +                final boolean isDefault2 = nic2.isDefaultNic();
 +
 +                return isDefault1 ^ isDefault2 ? isDefault1 ^ true ? 1 : -1 : 0;
 +            }
 +        });
 +
 +        for (final NicVO nic : nics) {
 +            final Pair<NetworkGuru, NetworkVO> implemented = implementNetwork(nic.getNetworkId(), dest, context, vmProfile.getVirtualMachine().getType() == Type.DomainRouter);
 +            if (implemented == null || implemented.first() == null) {
 +                s_logger.warn("Failed to implement network id=" + nic.getNetworkId() + " as a part of preparing nic id=" + nic.getId());
 +                throw new CloudRuntimeException("Failed to implement network id=" + nic.getNetworkId() + " as a part preparing nic id=" + nic.getId());
 +            }
 +
 +            final NetworkVO network = implemented.second();
 +            final NicProfile profile = prepareNic(vmProfile, dest, context, nic.getId(), network);
 +            vmProfile.addNic(profile);
 +        }
 +    }
 +
 +    @Override
 +    public NicProfile prepareNic(final VirtualMachineProfile vmProfile, final DeployDestination dest, final ReservationContext context, final long nicId, final Network network)
 +            throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException,
 +            ResourceUnavailableException {
 +
 +        final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vmProfile.getId());
 +        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +        final NicVO nic = _nicDao.findById(nicId);
 +
 +        NicProfile profile = null;
 +        if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) {
 +            nic.setState(Nic.State.Reserving);
 +            nic.setReservationId(context.getReservationId());
 +            _nicDao.update(nic.getId(), nic);
 +            URI broadcastUri = nic.getBroadcastUri();
 +            if (broadcastUri == null) {
 +                broadcastUri = network.getBroadcastUri();
 +            }
 +
 +            final URI isolationUri = nic.getIsolationUri();
 +
 +            profile = new NicProfile(nic, network, broadcastUri, isolationUri,
 +
 +                    networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network));
 +            guru.reserve(profile, network, vmProfile, dest, context);
 +            nic.setIPv4Address(profile.getIPv4Address());
 +            nic.setAddressFormat(profile.getFormat());
 +            nic.setIPv6Address(profile.getIPv6Address());
 +            nic.setIPv6Cidr(profile.getIPv6Cidr());
 +            nic.setIPv6Gateway(profile.getIPv6Gateway());
 +            nic.setMacAddress(profile.getMacAddress());
 +            nic.setIsolationUri(profile.getIsolationUri());
 +            nic.setBroadcastUri(profile.getBroadCastUri());
 +            nic.setReserver(guru.getName());
 +            nic.setState(Nic.State.Reserved);
 +            nic.setIPv4Netmask(profile.getIPv4Netmask());
 +            nic.setIPv4Gateway(profile.getIPv4Gateway());
 +
 +            if (profile.getReservationStrategy() != null) {
 +                nic.setReservationStrategy(profile.getReservationStrategy());
 +            }
 +
 +            updateNic(nic, network.getId(), 1);
 +        } else {
 +            profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
 +                    _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network));
 +            guru.updateNicProfile(profile, network);
 +            nic.setState(Nic.State.Reserved);
 +            updateNic(nic, network.getId(), 1);
 +        }
 +
 +        final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +        for (final NetworkElement element : networkElements) {
 +            if (providersToImplement.contains(element.getProvider())) {
 +                if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
 +                    throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
 +                            + network.getPhysicalNetworkId());
 +                }
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Asking " + element.getName() + " to prepare for " + nic);
 +                }
 +                if (!prepareElement(element, network, profile, vmProfile, dest, context)) {
 +                    throw new InsufficientAddressCapacityException("unable to configure the dhcp service, due to insufficiant address capacity", Network.class, network.getId());
 +                }
 +            }
 +        }
 +
 +        profile.setSecurityGroupEnabled(_networkModel.isSecurityGroupSupportedInNetwork(network));
 +        guru.updateNicProfile(profile, network);
 +
 +        configureExtraDhcpOptions(network, nicId);
 +        return profile;
 +    }
 +
 +    @Override
 +    public Map<Integer, String> getExtraDhcpOptions(long nicId) {
 +        List<NicExtraDhcpOptionVO> nicExtraDhcpOptionVOList = _nicExtraDhcpOptionDao.listByNicId(nicId);
 +        return nicExtraDhcpOptionVOList
 +                .stream()
 +                .collect(Collectors.toMap(NicExtraDhcpOptionVO::getCode, NicExtraDhcpOptionVO::getValue));
 +    }
 +
 +
 +    @Override
 +    public void prepareNicForMigration(final VirtualMachineProfile vm, final DeployDestination dest) {
 +        if(vm.getType().equals(VirtualMachine.Type.DomainRouter) && (vm.getHypervisorType().equals(HypervisorType.KVM) || vm.getHypervisorType().equals(HypervisorType.VMware))) {
 +            //Include nics hot plugged and not stored in DB
 +            prepareAllNicsForMigration(vm, dest);
 +            return;
 +        }
 +        final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
 +        final ReservationContext context = new ReservationContextImpl(UUID.randomUUID().toString(), null, null);
 +        for (final NicVO nic : nics) {
 +            final NetworkVO network = _networksDao.findById(nic.getNetworkId());
 +            final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
 +
 +            final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +            final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
 +                    _networkModel.getNetworkTag(vm.getHypervisorType(), network));
 +            if (guru instanceof NetworkMigrationResponder) {
 +                if (!((NetworkMigrationResponder)guru).prepareMigration(profile, network, vm, dest, context)) {
 +                    s_logger.error("NetworkGuru " + guru + " prepareForMigration failed."); // XXX: Transaction error
 +                }
 +            }
 +            final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +            for (final NetworkElement element : networkElements) {
 +                if (providersToImplement.contains(element.getProvider())) {
 +                    if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
 +                        throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
 +                                + network.getPhysicalNetworkId());
 +                    }
 +                    if (element instanceof NetworkMigrationResponder) {
 +                        if (!((NetworkMigrationResponder)element).prepareMigration(profile, network, vm, dest, context)) {
 +                            s_logger.error("NetworkElement " + element + " prepareForMigration failed."); // XXX: Transaction error
 +                        }
 +                    }
 +                }
 +            }
 +            guru.updateNicProfile(profile, network);
 +            vm.addNic(profile);
 +        }
 +    }
 +
 +    /*
 +    Prepare All Nics for migration including the nics dynamically created and not stored in DB
 +    This is a temporary workaround work KVM migration
 +    Once clean fix is added by stored dynamically nics is DB, this workaround won't be needed
 +     */
 +    @Override
 +    public void prepareAllNicsForMigration(final VirtualMachineProfile vm, final DeployDestination dest) {
 +        final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
 +        final ReservationContext context = new ReservationContextImpl(UUID.randomUUID().toString(), null, null);
 +        Long guestNetworkId = null;
 +        for (final NicVO nic : nics) {
 +            final NetworkVO network = _networksDao.findById(nic.getNetworkId());
 +            if(network.getTrafficType().equals(TrafficType.Guest) && network.getGuestType().equals(GuestType.Isolated)){
 +                guestNetworkId = network.getId();
 +            }
 +            final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
 +
 +            final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +            final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate,
 +                    _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network));
 +            if(guru instanceof NetworkMigrationResponder){
 +                if(!((NetworkMigrationResponder) guru).prepareMigration(profile, network, vm, dest, context)){
 +                    s_logger.error("NetworkGuru "+guru+" prepareForMigration failed."); // XXX: Transaction error
 +                }
 +            }
 +            final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +            for (final NetworkElement element : networkElements) {
 +                if (providersToImplement.contains(element.getProvider())) {
 +                    if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
 +                        throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId());
 +                    }
 +                    if(element instanceof NetworkMigrationResponder){
 +                        if(!((NetworkMigrationResponder) element).prepareMigration(profile, network, vm, dest, context)){
 +                            s_logger.error("NetworkElement "+element+" prepareForMigration failed."); // XXX: Transaction error
 +                        }
 +                    }
 +                }
 +            }
 +            guru.updateNicProfile(profile, network);
 +            vm.addNic(profile);
 +        }
 +
 +        final List<String> addedURIs = new ArrayList<String>();
 +        if(guestNetworkId != null){
 +            final List<IPAddressVO> publicIps = _ipAddressDao.listByAssociatedNetwork(guestNetworkId, null);
 +            for (final IPAddressVO userIp : publicIps){
 +                final PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
 +                final URI broadcastUri = BroadcastDomainType.Vlan.toUri(publicIp.getVlanTag());
 +                final long ntwkId = publicIp.getNetworkId();
 +                final Nic nic = _nicDao.findByNetworkIdInstanceIdAndBroadcastUri(ntwkId, vm.getId(),
 +                        broadcastUri.toString());
 +                if(nic == null && !addedURIs.contains(broadcastUri.toString())){
 +                    //Nic details are not available in DB
 +                    //Create nic profile for migration
 +                    s_logger.debug("Creating nic profile for migration. BroadcastUri: "+broadcastUri.toString()+" NetworkId: "+ntwkId+" Vm: "+vm.getId());
 +                    final NetworkVO network = _networksDao.findById(ntwkId);
 +                    _networkModel.getNetworkRate(network.getId(), vm.getId());
 +                    final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +                    final NicProfile profile = new NicProfile();
 +                    profile.setDeviceId(255); //dummyId
 +                    profile.setIPv4Address(userIp.getAddress().toString());
 +                    profile.setIPv4Netmask(publicIp.getNetmask());
 +                    profile.setIPv4Gateway(publicIp.getGateway());
 +                    profile.setMacAddress(publicIp.getMacAddress());
 +                    profile.setBroadcastType(network.getBroadcastDomainType());
 +                    profile.setTrafficType(network.getTrafficType());
 +                    profile.setBroadcastUri(broadcastUri);
 +                    profile.setIsolationUri(Networks.IsolationType.Vlan.toUri(publicIp.getVlanTag()));
 +                    profile.setSecurityGroupEnabled(_networkModel.isSecurityGroupSupportedInNetwork(network));
 +                    profile.setName(_networkModel.getNetworkTag(vm.getHypervisorType(), network));
 +                    profile.setNetworId(network.getId());
 +
 +                    guru.updateNicProfile(profile, network);
 +                    vm.addNic(profile);
 +                    addedURIs.add(broadcastUri.toString());
 +                }
 +            }
 +        }
 +    }
 +
 +    private NicProfile findNicProfileById(final VirtualMachineProfile vm, final long id) {
 +        for (final NicProfile nic : vm.getNics()) {
 +            if (nic.getId() == id) {
 +                return nic;
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public void commitNicForMigration(final VirtualMachineProfile src, final VirtualMachineProfile dst) {
 +        for (final NicProfile nicSrc : src.getNics()) {
 +            final NetworkVO network = _networksDao.findById(nicSrc.getNetworkId());
 +            final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +            final NicProfile nicDst = findNicProfileById(dst, nicSrc.getId());
 +            final ReservationContext src_context = new ReservationContextImpl(nicSrc.getReservationId(), null, null);
 +            final ReservationContext dst_context = new ReservationContextImpl(nicDst.getReservationId(), null, null);
 +
 +            if (guru instanceof NetworkMigrationResponder) {
 +                ((NetworkMigrationResponder)guru).commitMigration(nicSrc, network, src, src_context, dst_context);
 +            }
 +            final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +            for (final NetworkElement element : networkElements) {
 +                if (providersToImplement.contains(element.getProvider())) {
 +                    if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
 +                        throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
 +                                + network.getPhysicalNetworkId());
 +                    }
 +                    if (element instanceof NetworkMigrationResponder) {
 +                        ((NetworkMigrationResponder)element).commitMigration(nicSrc, network, src, src_context, dst_context);
 +                    }
 +                }
 +            }
 +            // update the reservation id
 +            final NicVO nicVo = _nicDao.findById(nicDst.getId());
 +            nicVo.setReservationId(nicDst.getReservationId());
 +            _nicDao.persist(nicVo);
 +        }
 +    }
 +
 +    @Override
 +    public void rollbackNicForMigration(final VirtualMachineProfile src, final VirtualMachineProfile dst) {
 +        for (final NicProfile nicDst : dst.getNics()) {
 +            final NetworkVO network = _networksDao.findById(nicDst.getNetworkId());
 +            final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +            final NicProfile nicSrc = findNicProfileById(src, nicDst.getId());
 +            final ReservationContext src_context = new ReservationContextImpl(nicSrc.getReservationId(), null, null);
 +            final ReservationContext dst_context = new ReservationContextImpl(nicDst.getReservationId(), null, null);
 +
 +            if (guru instanceof NetworkMigrationResponder) {
 +                ((NetworkMigrationResponder)guru).rollbackMigration(nicDst, network, dst, src_context, dst_context);
 +            }
 +            final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +            for (final NetworkElement element : networkElements) {
 +                if (providersToImplement.contains(element.getProvider())) {
 +                    if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
 +                        throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
 +                                + network.getPhysicalNetworkId());
 +                    }
 +                    if (element instanceof NetworkMigrationResponder) {
 +                        ((NetworkMigrationResponder)element).rollbackMigration(nicDst, network, dst, src_context, dst_context);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public void release(final VirtualMachineProfile vmProfile, final boolean forced) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final List<NicVO> nics = _nicDao.listByVmId(vmProfile.getId());
 +        for (final NicVO nic : nics) {
 +            releaseNic(vmProfile, nic.getId());
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public void releaseNic(final VirtualMachineProfile vmProfile, final Nic nic) throws ConcurrentOperationException, ResourceUnavailableException {
 +        releaseNic(vmProfile, nic.getId());
 +    }
 +
 +    @DB
 +    protected void releaseNic(final VirtualMachineProfile vmProfile, final long nicId) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final Pair<Network, NicProfile> networkToRelease = Transaction.execute(new TransactionCallback<Pair<Network, NicProfile>>() {
 +            @Override
 +            public Pair<Network, NicProfile> doInTransaction(final TransactionStatus status) {
 +                final NicVO nic = _nicDao.lockRow(nicId, true);
 +                if (nic == null) {
 +                    throw new ConcurrentOperationException("Unable to acquire lock on nic " + nic);
 +                }
 +
 +                final Nic.State originalState = nic.getState();
 +                final NetworkVO network = _networksDao.findById(nic.getNetworkId());
 +
 +                if (originalState == Nic.State.Reserved || originalState == Nic.State.Reserving) {
 +                    if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) {
 +                        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +                        nic.setState(Nic.State.Releasing);
 +                        _nicDao.update(nic.getId(), nic);
 +                        final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, _networkModel
 +                                .isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network));
 +                        if (guru.release(profile, vmProfile, nic.getReservationId())) {
 +                            applyProfileToNicForRelease(nic, profile);
 +                            nic.setState(Nic.State.Allocated);
 +                            if (originalState == Nic.State.Reserved) {
 +                                updateNic(nic, network.getId(), -1);
 +                            } else {
 +                                _nicDao.update(nic.getId(), nic);
 +                            }
 +                        }
 +                        // Perform release on network elements
 +                        return new Pair<Network, NicProfile>(network, profile);
 +                    } else {
 +                        nic.setState(Nic.State.Allocated);
 +                        updateNic(nic, network.getId(), -1);
 +                    }
 +                }
 +
 +                return null;
 +            }
 +        });
 +
 +        // cleanup the entry in vm_network_map
 +        if(vmProfile.getType().equals(VirtualMachine.Type.User)) {
 +            final NicVO nic = _nicDao.findById(nicId);
 +            if(nic != null) {
 +                final NetworkVO vmNetwork = _networksDao.findById(nic.getNetworkId());
 +                final VMNetworkMapVO vno = _vmNetworkMapDao.findByVmAndNetworkId(vmProfile.getVirtualMachine().getId(), vmNetwork.getId());
 +                if(vno != null) {
 +                    _vmNetworkMapDao.remove(vno.getId());
 +                }
 +            }
 +        }
 +
 +        if (networkToRelease != null) {
 +            final Network network = networkToRelease.first();
 +            final NicProfile profile = networkToRelease.second();
 +            final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +            for (final NetworkElement element : networkElements) {
 +                if (providersToImplement.contains(element.getProvider())) {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Asking " + element.getName() + " to release " + profile);
 +                    }
 +                    //NOTE: Context appear to never be used in release method
 +                    //implementations. Consider removing it from interface Element
 +                    element.release(network, profile, vmProfile, null);
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public void cleanupNics(final VirtualMachineProfile vm) {
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Cleaning network for vm: " + vm.getId());
 +        }
 +
 +        final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
 +        for (final NicVO nic : nics) {
 +            removeNic(vm, nic);
 +        }
 +    }
 +
 +    @Override
 +    public void removeNic(final VirtualMachineProfile vm, final Nic nic) {
 +        removeNic(vm, _nicDao.findById(nic.getId()));
 +    }
 +
 +    protected void removeNic(final VirtualMachineProfile vm, final NicVO nic) {
 +
 +        if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start && nic.getState() != Nic.State.Allocated) {
 +            // Nics with reservation strategy 'Start' should go through release phase in the Nic life cycle.
 +            // Ensure that release is performed before Nic is to be removed to avoid resource leaks.
 +            try {
 +                releaseNic(vm, nic.getId());
 +            } catch (final Exception ex) {
 +                s_logger.warn("Failed to release nic: " + nic.toString() + " as part of remove operation due to", ex);
 +            }
 +        }
 +
 +        nic.setState(Nic.State.Deallocating);
 +        _nicDao.update(nic.getId(), nic);
 +        final NetworkVO network = _networksDao.findById(nic.getNetworkId());
 +        final NicProfile profile = new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(
 +                vm.getHypervisorType(), network));
 +
 +        /*
 +         * We need to release the nics with a Create ReservationStrategy here
 +         * because the nic is now being removed.
 +         */
 +        if (nic.getReservationStrategy() == Nic.ReservationStrategy.Create) {
 +            final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +            for (final NetworkElement element : networkElements) {
 +                if (providersToImplement.contains(element.getProvider())) {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Asking " + element.getName() + " to release " + nic);
 +                    }
 +                    try {
 +                        element.release(network, profile, vm, null);
 +                    } catch (final ConcurrentOperationException ex) {
 +                        s_logger.warn("release failed during the nic " + nic.toString() + " removeNic due to ", ex);
 +                    } catch (final ResourceUnavailableException ex) {
 +                        s_logger.warn("release failed during the nic " + nic.toString() + " removeNic due to ", ex);
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (vm.getType() == Type.User
 +                && network.getTrafficType() == TrafficType.Guest
 +                && network.getGuestType() == GuestType.Shared
 +                && isLastNicInSubnet(nic)) {
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)) {
 +                // remove the dhcpservice ip if this is the last nic in subnet.
 +                final DhcpServiceProvider dhcpServiceProvider = getDhcpServiceProvider(network);
 +                if (dhcpServiceProvider != null
 +                        && isDhcpAccrossMultipleSubnetsSupported(dhcpServiceProvider)) {
 +                    removeDhcpServiceInSubnet(nic);
 +                }
 +            }
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dns)){
 +                final DnsServiceProvider dnsServiceProvider = getDnsServiceProvider(network);
 +                if (dnsServiceProvider != null) {
 +                    try {
 +                        if(!dnsServiceProvider.removeDnsSupportForSubnet(network)) {
 +                            s_logger.warn("Failed to remove the ip alias on the dns server");
 +                        }
 +                    } catch (final ResourceUnavailableException e) {
 +                        //failed to remove the dnsconfig.
 +                        s_logger.info("Unable to delete the ip alias due to unable to contact the dns server.");
 +                    }
 +                }
 +            }
 +        }
 +
 +        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +        guru.deallocate(network, profile, vm);
 +        _nicDao.remove(nic.getId());
 +
 +        s_logger.debug("Removed nic id=" + nic.getId());
 +        //remove the secondary ip addresses corresponding to to this nic
 +        if (!removeVmSecondaryIpsOfNic(nic.getId())) {
 +            s_logger.debug("Removing nic " + nic.getId() + " secondary ip addreses failed");
 +        }
 +    }
 +
 +    public boolean isDhcpAccrossMultipleSubnetsSupported(final DhcpServiceProvider dhcpServiceProvider) {
 +
 +        final Map<Network.Capability, String> capabilities = dhcpServiceProvider.getCapabilities().get(Network.Service.Dhcp);
 +        final String supportsMultipleSubnets = capabilities.get(Network.Capability.DhcpAccrossMultipleSubnets);
 +        if (supportsMultipleSubnets != null && Boolean.valueOf(supportsMultipleSubnets)) {
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    private boolean isLastNicInSubnet(final NicVO nic) {
 +        if (_nicDao.listByNetworkIdTypeAndGatewayAndBroadcastUri(nic.getNetworkId(), VirtualMachine.Type.User, nic.getIPv4Gateway(), nic.getBroadcastUri()).size() > 1) {
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    @DB
 +    @Override
 +    public void removeDhcpServiceInSubnet(final Nic nic) {
 +        final Network network = _networksDao.findById(nic.getNetworkId());
 +        final DhcpServiceProvider dhcpServiceProvider = getDhcpServiceProvider(network);
 +        try {
 +            final NicIpAliasVO ipAlias = _nicIpAliasDao.findByGatewayAndNetworkIdAndState(nic.getIPv4Gateway(), network.getId(), NicIpAlias.State.active);
 +            if (ipAlias != null) {
 +                ipAlias.setState(NicIpAlias.State.revoked);
 +                Transaction.execute(new TransactionCallbackNoReturn() {
 +                    @Override
 +                    public void doInTransactionWithoutResult(final TransactionStatus status) {
 +                        _nicIpAliasDao.update(ipAlias.getId(), ipAlias);
 +                        final IPAddressVO aliasIpaddressVo = _publicIpAddressDao.findByIpAndSourceNetworkId(ipAlias.getNetworkId(), ipAlias.getIp4Address());
 +                        _publicIpAddressDao.unassignIpAddress(aliasIpaddressVo.getId());
 +                    }
 +                });
 +                if (!dhcpServiceProvider.removeDhcpSupportForSubnet(network)) {
 +                    s_logger.warn("Failed to remove the ip alias on the router, marking it as removed in db and freed the allocated ip " + ipAlias.getIp4Address());
 +                }
 +            }
 +        } catch (final ResourceUnavailableException e) {
 +            //failed to remove the dhcpconfig on the router.
 +            s_logger.info("Unable to delete the ip alias due to unable to contact the virtualrouter.");
 +        }
 +
 +    }
 +
 +    @Override
 +    public void expungeNics(final VirtualMachineProfile vm) {
 +        final List<NicVO> nics = _nicDao.listByVmIdIncludingRemoved(vm.getId());
 +        for (final NicVO nic : nics) {
 +            _nicDao.expunge(nic.getId());
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public Network createGuestNetwork(final long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId,
 +                                      boolean bypassVlanOverlapCheck, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk,
 +                                      final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr,
 +                                      final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
 +
 +        final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
 +        // this method supports only guest network creation
 +        if (ntwkOff.getTrafficType() != TrafficType.Guest) {
 +            s_logger.warn("Only guest networks can be created using this method");
 +            return null;
 +        }
 +
 +        final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, aclType);
 +        //check resource limits
 +        if (updateResourceCount) {
 +            _resourceLimitMgr.checkResourceLimit(owner, ResourceType.network, isDisplayNetworkEnabled);
 +        }
 +
 +        // Validate network offering
 +        if (ntwkOff.getState() != NetworkOffering.State.Enabled) {
 +            // see NetworkOfferingVO
 +            final InvalidParameterValueException ex = new InvalidParameterValueException("Can't use specified network offering id as its stat is not " + NetworkOffering.State.Enabled);
 +            ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId");
 +            throw ex;
 +        }
 +
 +        // Validate physical network
 +        if (pNtwk.getState() != PhysicalNetwork.State.Enabled) {
 +            // see PhysicalNetworkVO.java
 +            final InvalidParameterValueException ex = new InvalidParameterValueException("Specified physical network id is" + " in incorrect state:" + pNtwk.getState());
 +            ex.addProxyObject(pNtwk.getUuid(), "physicalNetworkId");
 +            throw ex;
 +        }
 +
 +        boolean ipv6 = false;
 +
 +        if (StringUtils.isNotBlank(ip6Gateway) && StringUtils.isNotBlank(ip6Cidr)) {
 +            ipv6 = true;
 +        }
 +        // Validate zone
 +        final DataCenterVO zone = _dcDao.findById(zoneId);
 +        if (zone.getNetworkType() == NetworkType.Basic) {
 +            // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true
 +            if (aclType == null || aclType != ACLType.Domain) {
 +                throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone");
 +            }
 +
 +            // Only one guest network is supported in Basic zone
 +            final List<NetworkVO> guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest);
 +            if (!guestNetworks.isEmpty()) {
 +                throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic);
 +            }
 +
 +            // if zone is basic, only Shared network offerings w/o source nat service are allowed
 +            if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) {
 +                throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled "
 +                        + Service.SourceNat.getName() + " service are allowed");
 +            }
 +
 +            if (domainId == null || domainId != Domain.ROOT_DOMAIN) {
 +                throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain");
 +            }
 +
 +            if (subdomainAccess == null) {
 +                subdomainAccess = true;
 +            } else if (!subdomainAccess) {
 +                throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone");
 +            }
 +
 +            if (vlanId == null) {
 +                vlanId = Vlan.UNTAGGED;
 +            } else {
 +                if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) {
 +                    throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic);
 +                }
 +            }
 +
 +        } else if (zone.getNetworkType() == NetworkType.Advanced) {
 +            if (zone.isSecurityGroupEnabled()) {
 +                if (isolatedPvlan != null) {
 +                    throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!");
 +                }
 +                // Only Account specific Isolated network with sourceNat service disabled are allowed in security group
 +                // enabled zone
 +                if (ntwkOff.getGuestType() != GuestType.Shared) {
 +                    throw new InvalidParameterValueException("Only shared guest network can be created in security group enabled zone");
 +                }
 +                if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) {
 +                    throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone");
 +                }
 +            }
 +
 +            //don't allow eip/elb networks in Advance zone
 +            if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) {
 +                throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic);
 +            }
 +        }
 +
 +        if (ipv6 && NetUtils.getIp6CidrSize(ip6Cidr) != 64) {
 +            throw new InvalidParameterValueException("IPv6 subnet should be exactly 64-bits in size");
 +        }
 +
 +        //TODO(VXLAN): Support VNI specified
 +        // VlanId can be specified only when network offering supports it
 +        final boolean vlanSpecified = vlanId != null;
 +        if (vlanSpecified != ntwkOff.isSpecifyVlan()) {
 +            if (vlanSpecified) {
 +                throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false");
 +            } else {
 +                throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true");
 +            }
 +        }
 +
 +        if (vlanSpecified) {
 +            URI uri = BroadcastDomainType.fromString(vlanId);
 +            //don't allow to specify vlan tag used by physical network for dynamic vlan allocation
 +            if (!(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) {
 +                throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone "
 +                        + zone.getName());
 +            }
 +            if (! UuidUtils.validateUUID(vlanId)){
 +                // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone
 +                if (ntwkOff.getGuestType() == GuestType.Isolated || !hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff)) {
 +                    if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) {
 +                        throw new InvalidParameterValueException("Network with vlan " + vlanId + " already exists or overlaps with other network vlans in zone " + zoneId);
 +                    } else {
 +                        final List<DataCenterVnetVO> dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri));
 +                        //for the network that is created as part of private gateway,
 +                        //the vnet is not coming from the data center vnet table, so the list can be empty
 +                        if (!dcVnets.isEmpty()) {
 +                            final DataCenterVnetVO dcVnet = dcVnets.get(0);
 +                            // Fail network creation if specified vlan is dedicated to a different account
 +                            if (dcVnet.getAccountGuestVlanMapId() != null) {
 +                                final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId();
 +                                final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId);
 +                                if (map.getAccountId() != owner.getAccountId()) {
 +                                    throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account");
 +                                }
 +                                // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool
 +                            } else {
 +                                final List<AccountGuestVlanMapVO> maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId());
 +                                if (maps != null && !maps.isEmpty()) {
 +                                    final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId());
 +                                    final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId());
 +                                    if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) {
 +                                        throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner "
 +                                                + owner.getAccountName());
 +                                    }
 +                                }
 +                            }
 +                        }
 +                    }
 +                } else {
 +                    // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or
 +                    // shared network with same Vlan ID in the zone
 +                    if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0 ) {
 +                        throw new InvalidParameterValueException("There is an existing isolated/shared network that overlaps with vlan id:" + vlanId + " in zone " + zoneId);
 +                    }
 +                }
 +            }
 +
 +        }
 +
 +        // If networkDomain is not specified, take it from the global configuration
 +        if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) {
 +            final Map<Network.Capability, String> dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId),
 +                    Service.Dns);
 +            final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification);
 +            if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) {
 +                if (networkDomain != null) {
 +                    // TBD: NetworkOfferingId and zoneId. Send uuids instead.
 +                    throw new InvalidParameterValueException("Domain name change is not supported by network offering id=" + networkOfferingId + " in zone id=" + zoneId);
 +                }
 +            } else {
 +                if (networkDomain == null) {
 +                    // 1) Get networkDomain from the corresponding account/domain/zone
 +                    if (aclType == ACLType.Domain) {
 +                        networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId);
 +                    } else if (aclType == ACLType.Account) {
 +                        networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId);
 +                    }
 +
 +                    // 2) If null, generate networkDomain using domain suffix from the global config variables
 +                    if (networkDomain == null) {
 +                        networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId);
 +                    }
 +
 +                } else {
 +                    // validate network domain
 +                    if (!NetUtils.verifyDomainName(networkDomain)) {
 +                        throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain "
 +                                + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
 +                                + "and the hyphen ('-'); can't start or end with \"-\"");
 +                    }
 +                }
 +            }
 +        }
 +
 +        // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x
 +        // limitation, remove after we introduce support for multiple ip ranges
 +        // with different Cidrs for the same Shared network
 +        final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced
 +                && ntwkOff.getTrafficType() == TrafficType.Guest
 +                && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated
 +                && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)));
 +        if (cidr == null && ip6Cidr == null && cidrRequired) {
 +            throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of" + " type " + Network.GuestType.Shared
 +                    + " and network of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled");
 +        }
 +
 +        checkL2OfferingServices(ntwkOff);
 +
 +        // No cidr can be specified in Basic zone
 +        if (zone.getNetworkType() == NetworkType.Basic && cidr != null) {
 +            throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic);
 +        }
 +
 +        // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4
 +        if (cidr != null && ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) {
 +            if (!NetUtils.validateGuestCidr(cidr)) {
 +                throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC 1918 or 6598 compliant");
 +            }
 +        }
 +
 +        final String networkDomainFinal = networkDomain;
 +        final String vlanIdFinal = vlanId;
 +        final Boolean subdomainAccessFinal = subdomainAccess;
 +        final Network network = Transaction.execute(new TransactionCallback<Network>() {
 +            @Override
 +            public Network doInTransaction(final TransactionStatus status) {
 +                Long physicalNetworkId = null;
 +                if (pNtwk != null) {
 +                    physicalNetworkId = pNtwk.getId();
 +                }
 +                final DataCenterDeployment plan = new DataCenterDeployment(zoneId, null, null, null, null, physicalNetworkId);
 +                final NetworkVO userNetwork = new NetworkVO();
 +                userNetwork.setNetworkDomain(networkDomainFinal);
 +
 +                if (cidr != null && gateway != null) {
 +                    userNetwork.setCidr(cidr);
 +                    userNetwork.setGateway(gateway);
 +                }
 +
 +                if (StringUtils.isNotBlank(ip6Gateway) && StringUtils.isNotBlank(ip6Cidr)) {
 +                    userNetwork.setIp6Cidr(ip6Cidr);
 +                    userNetwork.setIp6Gateway(ip6Gateway);
 +                }
 +
 +                if (externalId != null) {
 +                    userNetwork.setExternalId(externalId);
 +                }
 +
 +                if (vlanIdFinal != null) {
 +                    if (isolatedPvlan == null) {
 +                        URI uri = null;
 +                        if (UuidUtils.validateUUID(vlanIdFinal)){
 +                            //Logical router's UUID provided as VLAN_ID
 +                            userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field
 +                        } else {
 +                            uri = BroadcastDomainType.fromString(vlanIdFinal);
 +                        }
 +                        userNetwork.setBroadcastUri(uri);
 +                        if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) {
 +                            userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan);
 +                        } else {
 +                            userNetwork.setBroadcastDomainType(BroadcastDomainType.Native);
 +                        }
 +                    } else {
 +                        if (vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) {
 +                            throw new InvalidParameterValueException("Cannot support pvlan with untagged primary vlan!");
 +                        }
 +                        userNetwork.setBroadcastUri(NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan));
 +                        userNetwork.setBroadcastDomainType(BroadcastDomainType.Pvlan);
 +                    }
 +                }
 +
 +                final List<? extends Network> networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId,
 +                        isDisplayNetworkEnabled);
 +
 +                Network network = null;
 +                if (networks == null || networks.isEmpty()) {
 +                    throw new CloudRuntimeException("Fail to create a network");
 +                } else {
 +                    if (networks.size() > 0 && networks.get(0).getGuestType() == Network.GuestType.Isolated && networks.get(0).getTrafficType() == TrafficType.Guest) {
 +                        Network defaultGuestNetwork = networks.get(0);
 +                        for (final Network nw : networks) {
 +                            if (nw.getCidr() != null && nw.getCidr().equals(zone.getGuestNetworkCidr())) {
 +                                defaultGuestNetwork = nw;
 +                            }
 +                        }
 +                        network = defaultGuestNetwork;
 +                    } else {
 +                        // For shared network
 +                        network = networks.get(0);
 +                    }
 +                }
 +
 +                if (updateResourceCount) {
 +                    _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network, isDisplayNetworkEnabled);
 +                }
 +
 +                return network;
 +            }
 +        });
 +
 +        CallContext.current().setEventDetails("Network Id: " + network.getId());
 +        CallContext.current().putContextParameter(Network.class, network.getUuid());
 +        return network;
 +    }
 +
 +  /**
 +   * Checks bypass VLAN id/range overlap check during network creation for guest networks
 +   * @param bypassVlanOverlapCheck bypass VLAN id/range overlap check
 +   * @param ntwkOff network offering
 +   */
 +  private boolean hasGuestBypassVlanOverlapCheck(final boolean bypassVlanOverlapCheck, final NetworkOfferingVO ntwkOff) {
 +    return bypassVlanOverlapCheck && ntwkOff.getGuestType() != GuestType.Isolated;
 +  }
 +
 +  /**
 +     * Checks for L2 network offering services. Only 2 cases allowed:
 +     * - No services
 +     * - User Data service only, provided by ConfigDrive
 +     * @param ntwkOff network offering
 +     */
 +    protected void checkL2OfferingServices(NetworkOfferingVO ntwkOff) {
 +        if (ntwkOff.getGuestType() == GuestType.L2 && !_networkModel.listNetworkOfferingServices(ntwkOff.getId()).isEmpty() &&
 +                (!_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.UserData) ||
 +                        (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.UserData) &&
 +                                _networkModel.listNetworkOfferingServices(ntwkOff.getId()).size() > 1))) {
 +            throw new InvalidParameterValueException("For L2 networks, only UserData service is allowed");
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public boolean shutdownNetwork(final long networkId, final ReservationContext context, final boolean cleanupElements) {
 +        NetworkVO network = _networksDao.findById(networkId);
 +        if (network.getState() == Network.State.Allocated) {
 +            s_logger.debug("Network is already shutdown: " + network);
 +            return true;
 +        }
 +
 +        if (network.getState() != Network.State.Implemented && network.getState() != Network.State.Shutdown) {
 +            s_logger.debug("Network is not implemented: " + network);
 +            return false;
 +        }
 +
 +        try {
 +            //do global lock for the network
 +            network = _networksDao.acquireInLockTable(networkId, NetworkLockTimeout.value());
 +            if (network == null) {
 +                s_logger.warn("Unable to acquire lock for the network " + network + " as a part of network shutdown");
 +                return false;
 +            }
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Lock is acquired for network " + network + " as a part of network shutdown");
 +            }
 +
 +            if (network.getState() == Network.State.Allocated) {
 +                s_logger.debug("Network is already shutdown: " + network);
 +                return true;
 +            }
 +
 +            if (network.getState() != Network.State.Implemented && network.getState() != Network.State.Shutdown) {
 +                s_logger.debug("Network is not implemented: " + network);
 +                return false;
 +            }
 +
 +            if (isSharedNetworkWithServices(network)) {
 +                network.setState(Network.State.Shutdown);
 +                _networksDao.update(network.getId(), network);
 +            } else {
 +                try {
 +                    stateTransitTo(network, Event.DestroyNetwork);
 +                } catch (final NoTransitionException e) {
 +                    network.setState(Network.State.Shutdown);
 +                    _networksDao.update(network.getId(), network);
 +                }
 +            }
 +
 +            final boolean success = shutdownNetworkElementsAndResources(context, cleanupElements, network);
 +
 +            final NetworkVO networkFinal = network;
 +            final boolean result = Transaction.execute(new TransactionCallback<Boolean>() {
 +                @Override
 +                public Boolean doInTransaction(final TransactionStatus status) {
 +                    boolean result = false;
 +
 +                    if (success) {
 +                        if (s_logger.isDebugEnabled()) {
 +                            s_logger.debug("Network id=" + networkId + " is shutdown successfully, cleaning up corresponding resources now.");
 +                        }
 +                        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, networkFinal.getGuruName());
 +                        final NetworkProfile profile = convertNetworkToNetworkProfile(networkFinal.getId());
 +                        guru.shutdown(profile, _networkOfferingDao.findById(networkFinal.getNetworkOfferingId()));
 +
 +                        applyProfileToNetwork(networkFinal, profile);
 +                        final DataCenterVO zone = _dcDao.findById(networkFinal.getDataCenterId());
 +                        if (isSharedNetworkOfferingWithServices(networkFinal.getNetworkOfferingId()) && zone.getNetworkType() == NetworkType.Advanced) {
 +                            networkFinal.setState(Network.State.Setup);
 +                        } else {
 +                            try {
 +                                stateTransitTo(networkFinal, Event.OperationSucceeded);
 +                            } catch (final NoTransitionException e) {
 +                                networkFinal.setState(Network.State.Allocated);
 +                                networkFinal.setRestartRequired(false);
 +                            }
 +                        }
 +                        _networksDao.update(networkFinal.getId(), networkFinal);
 +                        _networksDao.clearCheckForGc(networkId);
 +                        result = true;
 +                    } else {
 +                        try {
 +                            stateTransitTo(networkFinal, Event.OperationFailed);
 +                        } catch (final NoTransitionException e) {
 +                            networkFinal.setState(Network.State.Implemented);
 +                            _networksDao.update(networkFinal.getId(), networkFinal);
 +                        }
 +                        result = false;
 +                    }
 +
 +                    return result;
 +                }
 +            });
 +
 +            return result;
 +        } finally {
 +            if (network != null) {
 +                _networksDao.releaseFromLockTable(network.getId());
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Lock is released for network " + network + " as a part of network shutdown");
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean shutdownNetworkElementsAndResources(final ReservationContext context, final boolean cleanupElements, final Network network) {
 +
 +        // get providers to shutdown
 +        final List<Provider> providersToShutdown = getNetworkProviders(network.getId());
 +
 +        // 1) Cleanup all the rules for the network. If it fails, just log the failure and proceed with shutting down
 +        // the elements
 +        boolean cleanupResult = true;
 +        boolean cleanupNeeded = false;
 +        try {
 +            for (final Provider provider: providersToShutdown) {
 +                if (provider.cleanupNeededOnShutdown()) {
 +                    cleanupNeeded = true;
 +                    break;
 +                }
 +            }
 +            if (cleanupNeeded) {
 +                cleanupResult = shutdownNetworkResources(network.getId(), context.getAccount(), context.getCaller().getId());
 +            }
 +        } catch (final Exception ex) {
 +            s_logger.warn("shutdownNetworkRules failed during the network " + network + " shutdown due to ", ex);
 +        } finally {
 +            // just warn the administrator that the network elements failed to shutdown
 +            if (!cleanupResult) {
 +                s_logger.warn("Failed to cleanup network id=" + network.getId() + " resources as a part of shutdownNetwork");
 +            }
 +        }
 +
 +        // 2) Shutdown all the network elements
 +        boolean success = true;
 +        for (final NetworkElement element : networkElements) {
 +            if (providersToShutdown.contains(element.getProvider())) {
 +                try {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Sending network shutdown to " + element.getName());
 +                    }
 +                    if (!element.shutdown(network, context, cleanupElements)) {
 +                        s_logger.warn("Unable to complete shutdown of the network elements due to element: " + element.getName());
 +                        success = false;
 +                    }
 +                } catch (final ResourceUnavailableException e) {
 +                    s_logger.warn("Unable to complete shutdown of the network elements due to element: " + element.getName(), e);
 +                    success = false;
 +                } catch (final ConcurrentOperationException e) {
 +                    s_logger.warn("Unable to complete shutdown of the network elements due to element: " + element.getName(), e);
 +                    success = false;
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to complete shutdown of the network elements due to element: " + element.getName(), e);
 +                    success = false;
 +                }
 +            }
 +        }
 +        return success;
 +    }
 +
 +    @Override
 +    @DB
 +    public boolean destroyNetwork(final long networkId, final ReservationContext context, final boolean forced) {
 +        final Account callerAccount = context.getAccount();
 +
 +        NetworkVO network = _networksDao.findById(networkId);
 +        if (network == null) {
 +            s_logger.debug("Unable to find network with id: " + networkId);
 +            return false;
 +        }
 +        // Make sure that there are no user vms in the network that are not Expunged/Error
 +        final List<UserVmVO> userVms = _userVmDao.listByNetworkIdAndStates(networkId);
 +
 +        for (final UserVmVO vm : userVms) {
 +            if (!(vm.getState() == VirtualMachine.State.Expunging && vm.getRemoved() != null)) {
 +                s_logger.warn("Can't delete the network, not all user vms are expunged. Vm " + vm + " is in " + vm.getState() + " state");
 +                return false;
 +            }
 +        }
 +
 +        // Don't allow to delete network via api call when it has vms assigned to it
 +        final int nicCount = getActiveNicsInNetwork(networkId);
 +        if (nicCount > 0) {
 +            s_logger.debug("The network id=" + networkId + " has active Nics, but shouldn't.");
 +            // at this point we have already determined that there are no active user vms in network
 +            // if the op_networks table shows active nics, it's a bug in releasing nics updating op_networks
 +            _networksDao.changeActiveNicsBy(networkId, -1 * nicCount);
 +        }
 +
 +        //In Basic zone, make sure that there are no non-removed console proxies and SSVMs using the network
 +        final DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
 +        if (zone.getNetworkType() == NetworkType.Basic) {
 +            final List<VMInstanceVO> systemVms = _vmDao.listNonRemovedVmsByTypeAndNetwork(network.getId(), Type.ConsoleProxy, Type.SecondaryStorageVm);
 +            if (systemVms != null && !systemVms.isEmpty()) {
 +                s_logger.warn("Can't delete the network, not all consoleProxy/secondaryStorage vms are expunged");
 +                return false;
 +            }
 +        }
 +
 +        // Shutdown network first
 +        shutdownNetwork(networkId, context, false);
 +
 +        // get updated state for the network
 +        network = _networksDao.findById(networkId);
 +        if (network.getState() != Network.State.Allocated && network.getState() != Network.State.Setup && !forced) {
 +            s_logger.debug("Network is not not in the correct state to be destroyed: " + network.getState());
 +            return false;
 +        }
 +
 +        boolean success = true;
 +        if (!cleanupNetworkResources(networkId, callerAccount, context.getCaller().getId())) {
 +            s_logger.warn("Unable to delete network id=" + networkId + ": failed to cleanup network resources");
 +            return false;
 +        }
 +
 +        // get providers to destroy
 +        final List<Provider> providersToDestroy = getNetworkProviders(network.getId());
 +        for (final NetworkElement element : networkElements) {
 +            if (providersToDestroy.contains(element.getProvider())) {
 +                try {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Sending destroy to " + element);
 +                    }
 +
 +                    if (!element.destroy(network, context)) {
 +                        success = false;
 +                        s_logger.warn("Unable to complete destroy of the network: failed to destroy network element " + element.getName());
 +                    }
 +                } catch (final ResourceUnavailableException e) {
 +                    s_logger.warn("Unable to complete destroy of the network due to element: " + element.getName(), e);
 +                    success = false;
 +                } catch (final ConcurrentOperationException e) {
 +                    s_logger.warn("Unable to complete destroy of the network due to element: " + element.getName(), e);
 +                    success = false;
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to complete destroy of the network due to element: " + element.getName(), e);
 +                    success = false;
 +                }
 +            }
 +        }
 +
 +        if (success) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Network id=" + networkId + " is destroyed successfully, cleaning up corresponding resources now.");
 +            }
 +
 +            final NetworkVO networkFinal = network;
 +            try {
 +                Transaction.execute(new TransactionCallbackNoReturn() {
 +                    @Override
 +                    public void doInTransactionWithoutResult(final TransactionStatus status) {
 +                        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, networkFinal.getGuruName());
 +
 +                        if (!guru.trash(networkFinal, _networkOfferingDao.findById(networkFinal.getNetworkOfferingId()))) {
 +                            throw new CloudRuntimeException("Failed to trash network.");
 +                        }
 +
 +                        if (!deleteVlansInNetwork(networkFinal.getId(), context.getCaller().getId(), callerAccount)) {
 +                            s_logger.warn("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges");
 +                            throw new CloudRuntimeException("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges");
 +                        } else {
 +                            // commit transaction only when ips and vlans for the network are released successfully
 +                            try {
 +                                stateTransitTo(networkFinal, Event.DestroyNetwork);
 +                            } catch (final NoTransitionException e) {
 +                                s_logger.debug(e.getMessage());
 +                            }
 +                            if (_networksDao.remove(networkFinal.getId())) {
 +                                final NetworkDomainVO networkDomain = _networkDomainDao.getDomainNetworkMapByNetworkId(networkFinal.getId());
 +                                if (networkDomain != null) {
 +                                    _networkDomainDao.remove(networkDomain.getId());
 +                                }
 +
 +                                final NetworkAccountVO networkAccount = _networkAccountDao.getAccountNetworkMapByNetworkId(networkFinal.getId());
 +                                if (networkAccount != null) {
 +                                    _networkAccountDao.remove(networkAccount.getId());
 +                                }
 +                            }
 +
 +                            final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId());
 +                            final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, networkFinal.getAclType());
 +                            if (updateResourceCount) {
 +                                _resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network, networkFinal.getDisplayNetwork());
 +                            }
 +                        }
 +                    }
 +                });
 +                if (_networksDao.findById(network.getId()) == null) {
 +                    // remove its related ACL permission
 +                    final Pair<Class<?>, Long> networkMsg = new Pair<Class<?>, Long>(Network.class, networkFinal.getId());
 +                    _messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, networkMsg);
 +                }
 +                return true;
 +            } catch (final CloudRuntimeException e) {
 +                s_logger.error("Failed to delete network", e);
 +                return false;
 +            }
 +        }
 +
 +        return success;
 +    }
 +
 +    @Override
 +    public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACLType aclType) {
 +        //Update resource count only for Isolated account specific non-system networks
 +        final boolean updateResourceCount = ntwkOff.getGuestType() == GuestType.Isolated && !ntwkOff.isSystemOnly() && aclType == ACLType.Account;
 +        return updateResourceCount;
 +    }
 +
 +    protected boolean deleteVlansInNetwork(final long networkId, final long userId, final Account callerAccount) {
 +
 +        //cleanup Public vlans
 +        final List<VlanVO> publicVlans = _vlanDao.listVlansByNetworkId(networkId);
 +        boolean result = true;
 +        for (final VlanVO vlan : publicVlans) {
 +            if (!_configMgr.deleteVlanAndPublicIpRange(userId, vlan.getId(), callerAccount)) {
 +                s_logger.warn("Failed to delete vlan " + vlan.getId() + ");");
 +                result = false;
 +            }
 +        }
 +
 +        //cleanup private vlans
 +        final int privateIpAllocCount = _privateIpDao.countAllocatedByNetworkId(networkId);
 +        if (privateIpAllocCount > 0) {
 +            s_logger.warn("Can't delete Private ip range for network " + networkId + " as it has allocated ip addresses");
 +            result = false;
 +        } else {
 +            _privateIpDao.deleteByNetworkId(networkId);
 +            s_logger.debug("Deleted ip range for private network id=" + networkId);
 +        }
 +        return result;
 +    }
 +
 +    public class NetworkGarbageCollector extends ManagedContextRunnable {
 +        @Override
 +        protected void runInContext() {
 +            final GlobalLock gcLock = GlobalLock.getInternLock("Network.GC.Lock");
 +            try {
 +                if (gcLock.lock(3)) {
 +                    try {
 +                        reallyRun();
 +                    } finally {
 +                        gcLock.unlock();
 +                    }
 +                }
 +            } finally {
 +                gcLock.releaseRef();
 +            }
 +        }
 +
 +        public void reallyRun() {
 +            try {
 +                final List<Long> shutdownList = new ArrayList<Long>();
 +                final long currentTime = System.currentTimeMillis() / 1000;
 +                final HashMap<Long, Long> stillFree = new HashMap<Long, Long>();
 +
 +                final List<Long> networkIds = _networksDao.findNetworksToGarbageCollect();
 +                final int netGcWait = NumbersUtil.parseInt(_configDao.getValue(NetworkGcWait.key()), 60);
 +                s_logger.info("NetworkGarbageCollector uses '" + netGcWait + "' seconds for GC interval.");
 +
 +                for (final Long networkId : networkIds) {
 +
 +                    if (!_networkModel.isNetworkReadyForGc(networkId)) {
 +                        continue;
 +                    }
 +
 +                    final Long time = _lastNetworkIdsToFree.remove(networkId);
 +                    if (time == null) {
 +                        if (s_logger.isDebugEnabled()) {
 +                            s_logger.debug("We found network " + networkId + " to be free for the first time.  Adding it to the list: " + currentTime);
 +                        }
 +                        stillFree.put(networkId, currentTime);
 +                    } else if (time > currentTime - netGcWait) {
 +                        if (s_logger.isDebugEnabled()) {
 +                            s_logger.debug("Network " + networkId + " is still free but it's not time to shutdown yet: " + time);
 +                        }
 +                        stillFree.put(networkId, time);
 +                    } else {
 +                        shutdownList.add(networkId);
 +                    }
 +                }
 +
 +                _lastNetworkIdsToFree = stillFree;
 +
 +                final CallContext cctx = CallContext.current();
 +
 +                for (final Long networkId : shutdownList) {
 +
 +                    // If network is removed, unset gc flag for it
 +                    if (_networksDao.findById(networkId) == null) {
 +                        s_logger.debug("Network id=" + networkId + " is removed, so clearing up corresponding gc check");
 +                        _networksDao.clearCheckForGc(networkId);
 +                    } else {
 +                        try {
 +
 +                            final User caller = cctx.getCallingUser();
 +                            final Account owner = cctx.getCallingAccount();
 +
 +                            final ReservationContext context = new ReservationContextImpl(null, null, caller, owner);
 +
 +                            shutdownNetwork(networkId, context, false);
 +                        } catch (final Exception e) {
 +                            s_logger.warn("Unable to shutdown network: " + networkId);
 +                        }
 +                    }
 +                }
 +            } catch (final Exception e) {
 +                s_logger.warn("Caught exception while running network gc: ", e);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean startNetwork(final long networkId, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException,
 +    InsufficientCapacityException {
 +
 +        // Check if network exists
 +        final NetworkVO network = _networksDao.findById(networkId);
 +        if (network == null) {
 +            final InvalidParameterValueException ex = new InvalidParameterValueException("Network with specified id doesn't exist");
 +            ex.addProxyObject(String.valueOf(networkId), "networkId");
 +            throw ex;
 +        }
 +
 +        // implement the network
 +        s_logger.debug("Starting network " + network + "...");
 +        final Pair<NetworkGuru, NetworkVO> implementedNetwork = implementNetwork(networkId, dest, context);
 +        if (implementedNetwork== null || implementedNetwork.first() == null) {
 +            s_logger.warn("Failed to start the network " + network);
 +            return false;
 +        } else {
 +            return true;
 +        }
 +    }
 +
 +    @Override
 +    public boolean restartNetwork(final Long networkId, final Account callerAccount, final User callerUser, final boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException,
 +    InsufficientCapacityException {
 +
 +        final NetworkVO network = _networksDao.findById(networkId);
 +
 +        s_logger.debug("Restarting network " + networkId + "...");
 +
 +        final ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount);
 +        final NetworkOffering offering = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId());
 +        final DeployDestination dest = new DeployDestination(_dcDao.findById(network.getDataCenterId()), null, null, null);
 +
 +        if (cleanup) {
 +            if (!rollingRestartRouters(network, offering, dest, context)) {
 +                setRestartRequired(network, true);
 +                return false;
 +            }
 +            return true;
 +        }
 +
 +        s_logger.debug("Implementing the network " + network + " elements and resources as a part of network restart without cleanup");
 +        try {
 +            implementNetworkElementsAndResources(dest, context, network, offering);
 +            setRestartRequired(network, true);
 +            return true;
 +        } catch (final Exception ex) {
 +            s_logger.warn("Failed to implement network " + network + " elements and resources as a part of network restart due to ", ex);
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public void destroyExpendableRouters(final List<? extends VirtualRouter> routers, final ReservationContext context) throws ResourceUnavailableException {
 +        final List<VirtualRouter> remainingRouters = new ArrayList<>();
 +        for (final VirtualRouter router : routers) {
 +            if (router.getState() == VirtualMachine.State.Stopped ||
 +                    router.getState() == VirtualMachine.State.Error ||
 +                    router.getState() == VirtualMachine.State.Shutdowned ||
 +                    router.getState() == VirtualMachine.State.Unknown) {
 +                s_logger.debug("Destroying old router " + router);
 +                _routerService.destroyRouter(router.getId(), context.getAccount(), context.getCaller().getId());
 +            } else {
 +                remainingRouters.add(router);
 +            }
 +        }
 +
 +        if (remainingRouters.size() < 2) {
 +            return;
 +        }
 +
 +        VirtualRouter backupRouter = null;
 +        for (final VirtualRouter router : remainingRouters) {
 +            if (router.getRedundantState() == VirtualRouter.RedundantState.BACKUP) {
 +                backupRouter = router;
 +            }
 +        }
 +        if (backupRouter == null) {
 +            backupRouter = routers.get(routers.size() - 1);
 +        }
 +        if (backupRouter != null) {
 +            _routerService.destroyRouter(backupRouter.getId(), context.getAccount(), context.getCaller().getId());
 +        }
 +    }
 +
 +    @Override
 +    public boolean areRoutersRunning(final List<? extends VirtualRouter> routers) {
 +        for (final VirtualRouter router : routers) {
 +            if (router.getState() != VirtualMachine.State.Running) {
 +                s_logger.debug("Found new router " + router.getInstanceName() + " to be in non-Running state: " + router.getState() + ". Please try restarting network again.");
 +                return false;
 +            }
 +        }
 +        return true;
 +    }
 +
 +    /**
++     * Cleanup entry on VR file specified by type
++     */
++    @Override
++    public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile) {
++
++        final List<Provider> networkProviders = getNetworkProviders(network.getId());
++        for (final NetworkElement element : networkElements) {
++            if (networkProviders.contains(element.getProvider())) {
++                if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
++                    throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
++                            + network.getPhysicalNetworkId());
++                }
++                if (vmProfile.getType() == Type.User && element.getProvider() != null) {
++                    if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)
++                            && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, element.getProvider()) && element instanceof DhcpServiceProvider) {
++                        final DhcpServiceProvider sp = (DhcpServiceProvider) element;
++                        try {
++                            sp.removeDhcpEntry(network, nicProfile, vmProfile);
++                        } catch (ResourceUnavailableException e) {
++                            s_logger.error("Failed to remove dhcp-dns entry due to: ", e);
++                        }
++                    }
++                }
++            }
++        }
++    }
++
++    /**
 +     * rollingRestartRouters performs restart of routers of a network by first
 +     * deploying a new VR and then destroying old VRs in rolling fashion. For
 +     * non-redundant network, it will re-program the new router as final step
 +     * otherwise deploys a backup router for the network.
 +     * @param network network to be restarted
 +     * @param offering network offering
 +     * @param dest deployment destination
 +     * @param context reservation context
 +     * @return returns true when the rolling restart operation succeeds
 +     * @throws ResourceUnavailableException
 +     * @throws ConcurrentOperationException
 +     * @throws InsufficientCapacityException
 +     */
 +    private boolean rollingRestartRouters(final NetworkVO network, final NetworkOffering offering, final DeployDestination dest, final ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException {
 +        if (!NetworkOrchestrationService.RollingRestartEnabled.value()) {
 +            if (shutdownNetworkElementsAndResources(context, true, network)) {
 +                implementNetworkElementsAndResources(dest, context, network, offering);
 +                return true;
 +            }
 +            s_logger.debug("Failed to shutdown the network elements and resources as a part of network restart: " + network.getState());
 +            return false;
 +        }
 +        s_logger.debug("Performing rolling restart of routers of network " + network);
 +        destroyExpendableRouters(_routerDao.findByNetwork(network.getId()), context);
 +
 +        final List<Provider> providersToImplement = getNetworkProviders(network.getId());
 +        final List<DomainRouterVO> oldRouters = _routerDao.findByNetwork(network.getId());
 +
 +        // Deploy a new router
 +        if (oldRouters.size() > 0) {
 +            network.setRollingRestart(true);
 +        }
 +        implementNetworkElements(dest, context, network, offering, providersToImplement);
 +        if (oldRouters.size() > 0) {
 +            network.setRollingRestart(false);
 +        }
 +
 +        // For redundant network wait for 3*advert_int+skew_seconds for VRRP to kick in
 +        if (network.isRedundant() || (oldRouters.size() == 1 && oldRouters.get(0).getIsRedundantRouter())) {
 +            try {
 +                Thread.sleep(NetworkOrchestrationService.RVRHandoverTime);
 +            } catch (final InterruptedException ignored) {}
 +        }
 +
 +        // Destroy old routers
 +        for (final DomainRouterVO oldRouter : oldRouters) {
 +            _routerService.stopRouter(oldRouter.getId(), true);
 +            _routerService.destroyRouter(oldRouter.getId(), context.getAccount(), context.getCaller().getId());
 +        }
 +
 +        if (network.isRedundant()) {
 +            // Add a new backup router for redundant network
 +            implementNetworkElements(dest, context, network, offering, providersToImplement);
 +        } else {
 +            // Re-apply rules for non-redundant network
 +            implementNetworkElementsAndResources(dest, context, network, offering);
 +        }
 +
 +        return areRoutersRunning(_routerDao.findByNetwork(network.getId()));
 +    }
 +
 +    private void setRestartRequired(final NetworkVO network, final boolean restartRequired) {
 +        s_logger.debug("Marking network " + network + " with restartRequired=" + restartRequired);
 +        network.setRestartRequired(restartRequired);
 +        _networksDao.update(network.getId(), network);
 +    }
 +
 +    protected int getActiveNicsInNetwork(final long networkId) {
 +        return _networksDao.getActiveNicsIn(networkId);
 +    }
 +
 +    @Override
 +    public NetworkProfile convertNetworkToNetworkProfile(final long networkId) {
 +        final NetworkVO network = _networksDao.findById(networkId);
 +        final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +        final NetworkProfile profile = new NetworkProfile(network);
 +        guru.updateNetworkProfile(profile);
 +
 +        return profile;
 +    }
 +
 +    @Override
 +    public UserDataServiceProvider getPasswordResetProvider(final Network network) {
 +        final String passwordProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData);
 +
 +        if (passwordProvider == null) {
 +            s_logger.debug("Network " + network + " doesn't support service " + Service.UserData.getName());
 +            return null;
 +        }
 +
 +        return (UserDataServiceProvider)_networkModel.getElementImplementingProvider(passwordProvider);
 +    }
 +
 +    @Override
 +    public UserDataServiceProvider getSSHKeyResetProvider(final Network network) {
 +        final String SSHKeyProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData);
 +
 +        if (SSHKeyProvider == null) {
 +            s_logger.debug("Network " + network + " doesn't support service " + Service.UserData.getName());
 +            return null;
 +        }
 +
 +        return (UserDataServiceProvider)_networkModel.getElementImplementingProvider(SSHKeyProvider);
 +    }
 +
 +    @Override
 +    public DhcpServiceProvider getDhcpServiceProvider(final Network network) {
 +        final String DhcpProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.Dhcp);
 +
 +        if (DhcpProvider == null) {
 +            s_logger.debug("Network " + network + " doesn't support service " + Service.Dhcp.getName());
 +            return null;
 +        }
 +
 +        final NetworkElement element = _networkModel.getElementImplementingProvider(DhcpProvider);
 +        if ( element instanceof DhcpServiceProvider ) {
 +            return (DhcpServiceProvider)element;
 +        } else {
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    public DnsServiceProvider getDnsServiceProvider(final Network network) {
 +        final String dnsProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.Dns);
 +
 +        if (dnsProvider == null) {
 +            s_logger.debug("Network " + network + " doesn't support service " + Service.Dhcp.getName());
 +            return null;
 +        }
 +
 +        return  (DnsServiceProvider) _networkModel.getElementImplementingProvider(dnsProvider);
 +    }
 +
 +    protected boolean isSharedNetworkWithServices(final Network network) {
 +        assert network != null;
 +        final DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
 +        if (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced
 +                && isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) {
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    protected boolean isSharedNetworkOfferingWithServices(final long networkOfferingId) {
 +        final NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId);
 +        if (networkOffering.getGuestType() == Network.GuestType.Shared
 +                && (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat)
 +                        || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat)
 +                        || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall)
 +                        || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || _networkModel.areServicesSupportedByNetworkOffering(
 +                                networkOfferingId, Service.Lb))) {
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public List<? extends Nic> listVmNics(final long vmId, final Long nicId, final Long networkId, String keyword) {
 +        List<NicVO> result = null;
 +
 +        if (keyword == null || keyword.isEmpty()) {
 +            if (nicId == null && networkId == null) {
 +                result = _nicDao.listByVmId(vmId);
 +            } else {
 +                result = _nicDao.listByVmIdAndNicIdAndNtwkId(vmId, nicId, networkId);
 +            }
 +        } else {
 +            result = _nicDao.listByVmIdAndKeyword(vmId, keyword);
 +        }
 +
 +        for (final NicVO nic : result) {
 +            if (_networkModel.isProviderForNetwork(Provider.NiciraNvp, nic.getNetworkId())) {
 +                //For NSX Based networks, add nsxlogicalswitch, nsxlogicalswitchport to each result
 +                s_logger.info("Listing NSX logical switch and logical switch por for each nic");
 +                final NetworkVO network = _networksDao.findById(nic.getNetworkId());
 +                final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +                final NetworkGuruAdditionalFunctions guruFunctions = (NetworkGuruAdditionalFunctions) guru;
 +
 +                final Map<String, ? extends Object> nsxParams = guruFunctions.listAdditionalNicParams(nic.getUuid());
 +                if (nsxParams != null){
 +                    final String lswitchUuuid = nsxParams.containsKey(NetworkGuruAdditionalFunctions.NSX_LSWITCH_UUID)
 +                            ? (String) nsxParams.get(NetworkGuruAdditionalFunctions.NSX_LSWITCH_UUID) : null;
 +                    final String lswitchPortUuuid = nsxParams.containsKey(NetworkGuruAdditionalFunctions.NSX_LSWITCHPORT_UUID)
 +                            ? (String) nsxParams.get(NetworkGuruAdditionalFunctions.NSX_LSWITCHPORT_UUID) : null;
 +                    nic.setNsxLogicalSwitchUuid(lswitchUuuid);
 +                    nic.setNsxLogicalSwitchPortUuid(lswitchPortUuuid);
 +                }
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    @DB
 +    @Override
 +    public boolean reallocate(final VirtualMachineProfile vm, final DataCenterDeployment dest) throws InsufficientCapacityException, ConcurrentOperationException {
 +        final VMInstanceVO vmInstance = _vmDao.findById(vm.getId());
 +        final DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterId());
 +        if (dc.getNetworkType() == NetworkType.Basic) {
 +            final List<NicVO> nics = _nicDao.listByVmId(vmInstance.getId());
 +            final NetworkVO network = _networksDao.findById(nics.get(0).getNetworkId());
 +            final LinkedHashMap<Network, List<? extends NicProfile>> profiles = new LinkedHashMap<Network, List<? extends NicProfile>>();
 +            profiles.put(network, new ArrayList<NicProfile>());
 +
 +            Transaction.execute(new TransactionCallbackWithExceptionNoReturn<InsufficientCapacityException>() {
 +                @Override
 +                public void doInTransactionWithoutResult(final TransactionStatus status) throws InsufficientCapacityException {
 +                    cleanupNics(vm);
 +                    allocate(vm, profiles, null);
 +                }
 +            });
 +        }
 +        return true;
 +    }
 +
 +    private boolean cleanupNetworkResources(final long networkId, final Account caller, final long callerUserId) {
 +        boolean success = true;
 +        final Network network = _networksDao.findById(networkId);
 +
 +        //remove all PF/Static Nat rules for the network
 +        try {
 +            if (_rulesMgr.revokeAllPFStaticNatRulesForNetwork(networkId, callerUserId, caller)) {
 +                s_logger.debug("Successfully cleaned up portForwarding/staticNat rules for network id=" + networkId);
 +            } else {
 +                success = false;
 +                s_logger.warn("Failed to release portForwarding/StaticNat rules as a part of network id=" + networkId + " cleanup");
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            success = false;
 +            // shouldn't even come here as network is being cleaned up after all network elements are shutdown
 +            s_logger.warn("Failed to release portForwarding/StaticNat rules as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex);
 +        }
 +
 +        //remove all LB rules for the network
 +        if (_lbMgr.removeAllLoadBalanacersForNetwork(networkId, caller, callerUserId)) {
 +            s_logger.debug("Successfully cleaned up load balancing rules for network id=" + networkId);
 +        } else {
 +            // shouldn't even come here as network is being cleaned up after all network elements are shutdown
 +            success = false;
 +            s_logger.warn("Failed to cleanup LB rules as a part of network id=" + networkId + " cleanup");
 +        }
 +
 +        //revoke all firewall rules for the network
 +        try {
 +            if (_firewallMgr.revokeAllFirewallRulesForNetwork(networkId, callerUserId, caller)) {
 +                s_logger.debug("Successfully cleaned up firewallRules rules for network id=" + networkId);
 +            } else {
 +                success = false;
 +                s_logger.warn("Failed to cleanup Firewall rules as a part of network id=" + networkId + " cleanup");
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            success = false;
 +            // shouldn't even come here as network is being cleaned up after all network elements are shutdown
 +            s_logger.warn("Failed to cleanup Firewall rules as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex);
 +        }
 +
 +        //revoke all network ACLs for network
 +        try {
 +            if (_networkACLMgr.revokeACLItemsForNetwork(networkId)) {
 +                s_logger.debug("Successfully cleaned up NetworkACLs for network id=" + networkId);
 +            } else {
 +                success = false;
 +                s_logger.warn("Failed to cleanup NetworkACLs as a part of network id=" + networkId + " cleanup");
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            success = false;
 +            s_logger.warn("Failed to cleanup Network ACLs as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex);
 +        }
 +
 +        //release all ip addresses
 +        final List<IPAddressVO> ipsToRelease = _ipAddressDao.listByAssociatedNetwork(networkId, null);
 +        for (final IPAddressVO ipToRelease : ipsToRelease) {
 +            if (ipToRelease.getVpcId() == null) {
 +                if (!ipToRelease.isPortable()) {
 +                    final IPAddressVO ip = _ipAddrMgr.markIpAsUnavailable(ipToRelease.getId());
 +                    assert ip != null : "Unable to mark the ip address id=" + ipToRelease.getId() + " as unavailable.";
 +                } else {
 +                    // portable IP address are associated with owner, until explicitly requested to be disassociated
 +                    // so as part of network clean up just break IP association with guest network
 +                    ipToRelease.setAssociatedWithNetworkId(null);
 +                    _ipAddressDao.update(ipToRelease.getId(), ipToRelease);
 +                    s_logger.debug("Portable IP address " + ipToRelease + " is no longer associated with any network");
 +                }
 +            } else {
 +                _vpcMgr.unassignIPFromVpcNetwork(ipToRelease.getId(), network.getId());
 +            }
 +        }
 +
 +        try {
 +            if (!_ipAddrMgr.applyIpAssociations(network, true)) {
 +                s_logger.warn("Unable to apply ip address associations for " + network);
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException e) {
 +            throw new CloudRuntimeException("We should never get to here because we used true when applyIpAssociations", e);
 +        }
 +
 +        return success;
 +    }
 +
 +    private boolean shutdownNetworkResources(final long networkId, final Account caller, final long callerUserId) {
 +        // This method cleans up network rules on the backend w/o touching them in the DB
 +        boolean success = true;
 +        final Network network = _networksDao.findById(networkId);
 +
 +        // Mark all PF rules as revoked and apply them on the backend (not in the DB)
 +        final List<PortForwardingRuleVO> pfRules = _portForwardingRulesDao.listByNetwork(networkId);
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Releasing " + pfRules.size() + " port forwarding rules for network id=" + networkId + " as a part of shutdownNetworkRules");
 +        }
 +
 +        for (final PortForwardingRuleVO pfRule : pfRules) {
 +            s_logger.trace("Marking pf rule " + pfRule + " with Revoke state");
 +            pfRule.setState(FirewallRule.State.Revoke);
 +        }
 +
 +        try {
 +            if (!_firewallMgr.applyRules(pfRules, true, false)) {
 +                s_logger.warn("Failed to cleanup pf rules as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup pf rules as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        // Mark all static rules as revoked and apply them on the backend (not in the DB)
 +        final List<FirewallRuleVO> firewallStaticNatRules = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.StaticNat);
 +        final List<StaticNatRule> staticNatRules = new ArrayList<StaticNatRule>();
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Releasing " + firewallStaticNatRules.size() + " static nat rules for network id=" + networkId + " as a part of shutdownNetworkRules");
 +        }
 +
 +        for (final FirewallRuleVO firewallStaticNatRule : firewallStaticNatRules) {
 +            s_logger.trace("Marking static nat rule " + firewallStaticNatRule + " with Revoke state");
 +            final IpAddress ip = _ipAddressDao.findById(firewallStaticNatRule.getSourceIpAddressId());
 +            final FirewallRuleVO ruleVO = _firewallDao.findById(firewallStaticNatRule.getId());
 +
 +            if (ip == null || !ip.isOneToOneNat() || ip.getAssociatedWithVmId() == null) {
 +                throw new InvalidParameterValueException("Source ip address of the rule id=" + firewallStaticNatRule.getId() + " is not static nat enabled");
 +            }
 +
 +            //String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), firewallStaticNatRule.getNetworkId());
 +            ruleVO.setState(FirewallRule.State.Revoke);
 +            staticNatRules.add(new StaticNatRuleImpl(ruleVO, ip.getVmIp()));
 +        }
 +
 +        try {
 +            if (!_firewallMgr.applyRules(staticNatRules, true, false)) {
 +                s_logger.warn("Failed to cleanup static nat rules as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup static nat rules as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        try {
 +            if (!_lbMgr.revokeLoadBalancersForNetwork(networkId, Scheme.Public)) {
 +                s_logger.warn("Failed to cleanup public lb rules as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup public lb rules as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        try {
 +            if (!_lbMgr.revokeLoadBalancersForNetwork(networkId, Scheme.Internal)) {
 +                s_logger.warn("Failed to cleanup internal lb rules as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup public lb rules as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        // revoke all firewall rules for the network w/o applying them on the DB
 +        final List<FirewallRuleVO> firewallRules = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Ingress);
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Releasing " + firewallRules.size() + " firewall ingress rules for network id=" + networkId + " as a part of shutdownNetworkRules");
 +        }
 +
 +        for (final FirewallRuleVO firewallRule : firewallRules) {
 +            s_logger.trace("Marking firewall ingress rule " + firewallRule + " with Revoke state");
 +            firewallRule.setState(FirewallRule.State.Revoke);
 +        }
 +
 +        try {
 +            if (!_firewallMgr.applyRules(firewallRules, true, false)) {
 +                s_logger.warn("Failed to cleanup firewall ingress rules as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup firewall ingress rules as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        final List<FirewallRuleVO> firewallEgressRules = _firewallDao.listByNetworkPurposeTrafficType(networkId, Purpose.Firewall, FirewallRule.TrafficType.Egress);
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Releasing " + firewallEgressRules.size() + " firewall egress rules for network id=" + networkId + " as a part of shutdownNetworkRules");
 +        }
 +
 +        try {
 +            // delete default egress rule
 +            final DataCenter zone = _dcDao.findById(network.getDataCenterId());
 +            if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall)
 +                    && (network.getGuestType() == Network.GuestType.Isolated || network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) {
 +                // add default egress rule to accept the traffic
 +                _firewallMgr.applyDefaultEgressFirewallRule(network.getId(), _networkModel.getNetworkEgressDefaultPolicy(networkId), false);
 +            }
 +
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup firewall default egress rule as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        for (final FirewallRuleVO firewallRule : firewallEgressRules) {
 +            s_logger.trace("Marking firewall egress rule " + firewallRule + " with Revoke state");
 +            firewallRule.setState(FirewallRule.State.Revoke);
 +        }
 +
 +        try {
 +            if (!_firewallMgr.applyRules(firewallEgressRules, true, false)) {
 +                s_logger.warn("Failed to cleanup firewall egress rules as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException ex) {
 +            s_logger.warn("Failed to cleanup firewall egress rules as a part of shutdownNetworkRules due to ", ex);
 +            success = false;
 +        }
 +
 +        if (network.getVpcId() != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Releasing Network ACL Items for network id=" + networkId + " as a part of shutdownNetworkRules");
 +            }
 +
 +            try {
 +                //revoke all Network ACLs for the network w/o applying them in the DB
 +                if (!_networkACLMgr.revokeACLItemsForNetwork(networkId)) {
 +                    s_logger.warn("Failed to cleanup network ACLs as a part of shutdownNetworkRules");
 +                    success = false;
 +                }
 +            } catch (final ResourceUnavailableException ex) {
 +                s_logger.warn("Failed to cleanup network ACLs as a part of shutdownNetworkRules due to ", ex);
 +                success = false;
 +            }
 +
 +        }
 +
 +        //release all static nats for the network
 +        if (!_rulesMgr.applyStaticNatForNetwork(networkId, false, caller, true)) {
 +            s_logger.warn("Failed to disable static nats as part of shutdownNetworkRules for network id " + networkId);
 +            success = false;
 +        }
 +
 +        // Get all ip addresses, mark as releasing and release them on the backend
 +        final List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(networkId, null);
 +        final List<PublicIp> publicIpsToRelease = new ArrayList<PublicIp>();
 +        if (userIps != null && !userIps.isEmpty()) {
 +            for (final IPAddressVO userIp : userIps) {
 +                userIp.setState(IpAddress.State.Releasing);
 +                final PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
 +                publicIpsToRelease.add(publicIp);
 +            }
 +        }
 +
 +        try {
 +            if (!_ipAddrMgr.applyIpAssociations(network, true, true, publicIpsToRelease)) {
 +                s_logger.warn("Unable to apply ip address associations for " + network + " as a part of shutdownNetworkRules");
 +                success = false;
 +            }
 +        } catch (final ResourceUnavailableException e) {
 +            throw new CloudRuntimeException("We should never get to here because we used true when applyIpAssociations", e);
 +        }
 +
 +        return success;
 +    }
 +
 +    @Override
 +    public boolean processAnswers(final long agentId, final long seq, final Answer[] answers) {
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean processCommands(final long agentId, final long seq, final Command[] commands) {
 +        return false;
 +    }
 +
 +    @Override
 +    public AgentControlAnswer processControlCommand(final long agentId, final AgentControlCommand cmd) {
 +        return null;
 +    }
 +
 +    public void processHostAdded(long hostId) {
 +    }
 +
 +    @Override
 +    public void processConnect(final Host host, final StartupCommand cmd, final boolean forRebalance) throws ConnectionException {
 +        if (!(cmd instanceof StartupRoutingCommand)) {
 +            return;
 +        }
 +        final long hostId = host.getId();
 +        final StartupRoutingCommand startup = (StartupRoutingCommand)cmd;
 +
 +        final String dataCenter = startup.getDataCenter();
 +
 +        long dcId = -1;
 +        DataCenterVO dc = _dcDao.findByName(dataCenter);
 +        if (dc == null) {
 +            try {
 +                dcId = Long.parseLong(dataCenter);
 +                dc = _dcDao.findById(dcId);
 +            } catch (final NumberFormatException e) {
 +            }
 +        }
 +        if (dc == null) {
 +            throw new IllegalArgumentException("Host " + startup.getPrivateIpAddress() + " sent incorrect data center: " + dataCenter);
 +        }
 +        dcId = dc.getId();
 +        final HypervisorType hypervisorType = startup.getHypervisorType();
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Host's hypervisorType is: " + hypervisorType);
 +        }
 +
 +        final List<PhysicalNetworkSetupInfo> networkInfoList = new ArrayList<PhysicalNetworkSetupInfo>();
 +
 +        // list all physicalnetworks in the zone & for each get the network names
 +        final List<PhysicalNetworkVO> physicalNtwkList = _physicalNetworkDao.listByZone(dcId);
 +        for (final PhysicalNetworkVO pNtwk : physicalNtwkList) {
 +            final String publicName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Public, hypervisorType);
 +            final String privateName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Management, hypervisorType);
 +            final String guestName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Guest, hypervisorType);
 +            final String storageName = _pNTrafficTypeDao.getNetworkTag(pNtwk.getId(), TrafficType.Storage, hypervisorType);
 +            // String controlName = _pNTrafficTypeDao._networkModel.getNetworkTag(pNtwk.getId(), TrafficType.Control, hypervisorType);
 +            final PhysicalNetworkSetupInfo info = new PhysicalNetworkSetupInfo();
 +            info.setPhysicalNetworkId(pNtwk.getId());
 +            info.setGuestNetworkName(guestName);
 +            info.setPrivateNetworkName(privateName);
 +            info.setPublicNetworkName(publicName);
 +            info.setStorageNetworkName(storageName);
 +            final PhysicalNetworkTrafficTypeVO mgmtTraffic = _pNTrafficTypeDao.findBy(pNtwk.getId(), TrafficType.Management);
 +            if (mgmtTraffic != null) {
 +                final String vlan = mgmtTraffic.getVlan();
 +                info.setMgmtVlan(vlan);
 +            }
 +            networkInfoList.add(info);
 +        }
 +
 +        // send the names to the agent
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Sending CheckNetworkCommand to check the Network is setup correctly on Agent");
 +        }
 +        final CheckNetworkCommand nwCmd = new CheckNetworkCommand(networkInfoList);
 +
 +        final CheckNetworkAnswer answer = (CheckNetworkAnswer)_agentMgr.easySend(hostId, nwCmd);
 +
 +        if (answer == null) {
 +            s_logger.warn("Unable to get an answer to the CheckNetworkCommand from agent:" + host.getId());
 +            throw new ConnectionException(true, "Unable to get an answer to the CheckNetworkCommand from agent: " + host.getId());
 +        }
 +
 +        if (!answer.getResult()) {
 +            s_logger.warn("Unable to setup agent " + hostId + " due to " + answer.getDetails() );
 +            final String msg = "Incorrect Network setup on agent, Reinitialize agent after network names are setup, details : " + answer.getDetails();
 +            _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, dcId, host.getPodId(), msg, msg);
 +            throw new ConnectionException(true, msg);
 +        } else {
 +            if (answer.needReconnect()) {
 +                throw new ConnectionException(false, "Reinitialize agent after network setup.");
 +            }
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Network setup is correct on Agent");
 +            }
 +            return;
 +        }
 +    }
 +
 +    @Override
 +    public boolean processDisconnect(final long agentId, final Status state) {
 +        return false;
 +    }
 +
 +    @Override
 +    public void processHostAboutToBeRemoved(long hostId) {
 +    }
 +
 +    @Override
 +    public void processHostRemoved(long hostId, long clusterId) {
 +    }
 +
 +    @Override
 +    public boolean isRecurring() {
 +        return false;
 +    }
 +
 +    @Override
 +    public int getTimeout() {
 +        return 0;
 +    }
 +
 +    @Override
 +    public boolean processTimeout(final long agentId, final long seq) {
 +        return false;
 +    }
 +
 +    @Override
 +    public Map<String, String> finalizeServicesAndProvidersForNetwork(final NetworkOffering offering, final Long physicalNetworkId) {
 +        final Map<String, String> svcProviders = new HashMap<String, String>();
 +        final Map<String, List<String>> providerSvcs = new HashMap<String, List<String>>();
 +        final List<NetworkOfferingServiceMapVO> servicesMap = _ntwkOfferingSrvcDao.listByNetworkOfferingId(offering.getId());
 +
 +        final boolean checkPhysicalNetwork = physicalNetworkId != null ? true : false;
 +
 +        for (final NetworkOfferingServiceMapVO serviceMap : servicesMap) {
 +            if (svcProviders.containsKey(serviceMap.getService())) {
 +                // FIXME - right now we pick up the first provider from the list, need to add more logic based on
 +                // provider load, etc
 +                continue;
 +            }
 +
 +            final String service = serviceMap.getService();
 +            String provider = serviceMap.getProvider();
 +
 +            if (provider == null) {
 +                provider = _networkModel.getDefaultUniqueProviderForService(service).getName();
 +            }
 +
 +            // check that provider is supported
 +            if (checkPhysicalNetwork) {
 +                if (!_pNSPDao.isServiceProviderEnabled(physicalNetworkId, provider, service)) {
 +                    throw new UnsupportedServiceException("Provider " + provider + " is either not enabled or doesn't " + "support service " + service + " in physical network id="
 +                            + physicalNetworkId);
 +                }
 +            }
 +
 +            svcProviders.put(service, provider);
 +            List<String> l = providerSvcs.get(provider);
 +            if (l == null) {
 +                providerSvcs.put(provider, l = new ArrayList<String>());
 +            }
 +            l.add(service);
 +        }
 +
 +        return svcProviders;
 +    }
 +
 +    private List<Provider> getNetworkProviders(final long networkId) {
 +        final List<String> providerNames = _ntwkSrvcDao.getDistinctProviders(networkId);
 +        final List<Provider> providers = new ArrayList<Provider>();
 +        for (final String providerName : providerNames) {
 +            providers.add(Network.Provider.getProvider(providerName));
 +        }
 +
 +        return providers;
 +    }
 +
 +    @Override
 +    public boolean setupDns(final Network network, final Provider provider) {
 +        final boolean dnsProvided = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, provider);
 +        final boolean dhcpProvided = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, provider);
 +
 +        final boolean setupDns = dnsProvided || dhcpProvided;
 +        return setupDns;
 +    }
 +
 +    protected NicProfile getNicProfileForVm(final Network network, final NicProfile requested, final VirtualMachine vm) {
 +        NicProfile nic = null;
 +        if (requested != null && requested.getBroadCastUri() != null) {
 +            final String broadcastUri = requested.getBroadCastUri().toString();
 +            final String ipAddress = requested.getIPv4Address();
 +            final NicVO nicVO = _nicDao.findByNetworkIdInstanceIdAndBroadcastUri(network.getId(), vm.getId(), broadcastUri);
 +            if (nicVO != null) {
 +                if (ipAddress == null || nicVO.getIPv4Address().equals(ipAddress)) {
 +                    nic = _networkModel.getNicProfile(vm, network.getId(), broadcastUri);
 +                }
 +            }
 +        } else {
 +            final NicVO nicVO = _nicDao.findByNtwkIdAndInstanceId(network.getId(), vm.getId());
 +            if (nicVO != null) {
 +                nic = _networkModel.getNicProfile(vm, network.getId(), null);
 +            }
 +        }
 +        return nic;
 +    }
 +
 +    @Override
 +    public NicProfile createNicForVm(final Network network, final NicProfile requested, final ReservationContext context, final VirtualMachineProfile vmProfile, final boolean prepare)
 +            throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException,
 +            ResourceUnavailableException {
 +
 +        final VirtualMachine vm = vmProfile.getVirtualMachine();
 +        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);
 +
 +        NicProfile nic = getNicProfileForVm(network, requested, vm);
 +
 +        //1) allocate nic (if needed) Always allocate if it is a user vm
 +        if (nic == null || vmProfile.getType() == VirtualMachine.Type.User) {
 +            final int deviceId = _nicDao.getFreeDeviceId(vm.getId());
 +
 +            nic = allocateNic(requested, network, false, deviceId, vmProfile).first();
 +
 +            if (nic == null) {
 +                throw new CloudRuntimeException("Failed to allocate nic for vm " + vm + " in network " + network);
 +            }
 +
 +            //Update vm_network_map table
 +            if(vmProfile.getType() == VirtualMachine.Type.User) {
 +                final VMNetworkMapVO vno = new VMNetworkMapVO(vm.getId(), network.getId());
 +                _vmNetworkMapDao.persist(vno);
 +            }
 +            s_logger.debug("Nic is allocated successfully for vm " + vm + " in network " + network);
 +        }
 +
 +        //2) prepare nic
 +        if (prepare) {
 +            final Pair<NetworkGuru, NetworkVO> implemented = implementNetwork(nic.getNetworkId(), dest, context, vmProfile.getVirtualMachine().getType() == Type.DomainRouter);
 +            if (implemented == null || implemented.first() == null) {
 +                s_logger.warn("Failed to implement network id=" + nic.getNetworkId() + " as a part of preparing nic id=" + nic.getId());
 +                throw new CloudRuntimeException("Failed to implement network id=" + nic.getNetworkId() + " as a part preparing nic id=" + nic.getId());
 +            }
 +            nic = prepareNic(vmProfile, dest, context, nic.getId(), implemented.second());
 +            s_logger.debug("Nic is prepared successfully for vm " + vm + " in network " + network);
 +        }
 +
 +        return nic;
 +    }
 +
 +    @Override
 +    public List<NicProfile> getNicProfiles(final VirtualMachine vm) {
 +        final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
 +        final List<NicProfile> profiles = new ArrayList<NicProfile>();
 +
 +        if (nics != null) {
 +            for (final Nic nic : nics) {
 +                final NetworkVO network = _networksDao.findById(nic.getNetworkId());
 +                final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
 +
 +                final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
 +                final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate,
 +                        _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network));
 +                guru.updateNicProfile(profile, network);
 +                profiles.add(profile);
 +            }
 +        }
 +        return profiles;
 +    }
 +
 +    @Override
 +    public Map<String, String> getSystemVMAccessDetails(final VirtualMachine vm) {
 +        final Map<String, String> accessDetails = new HashMap<>();
 +        accessDetails.put(NetworkElementCommand.ROUTER_NAME, vm.getInstanceName());
 +        String privateIpAddress = null;
 +        for (final NicProfile profile : getNicProfiles(vm)) {
 +            if (profile == null) {
 +                continue;
 +            }
 +            final Network network = _networksDao.findById(profile.getNetworkId());
 +            if (network == null) {
 +                continue;
 +            }
 +            final String address = profile.getIPv4Address();
 +            if (network.getTrafficType() == Networks.TrafficType.Control) {
 +                accessDetails.put(NetworkElementCommand.ROUTER_IP, address);
 +            }
 +            if (network.getTrafficType() == Networks.TrafficType.Guest) {
 +                accessDetails.put(NetworkElementCommand.ROUTER_GUEST_IP, address);
 +            }
 +            if (network.getTrafficType() == Networks.TrafficType.Management) {
 +                privateIpAddress = address;
 +            }
 +            if (network.getTrafficType() != null && !Strings.isNullOrEmpty(address)) {
 +                accessDetails.put(network.getTrafficType().name(), address);
 +            }
 +        }
 +        if (privateIpAddress != null && Strings.isNullOrEmpty(accessDetails.get(NetworkElementCommand.ROUTER_IP))) {
 +            accessDetails.put(NetworkElementCommand.ROUTER_IP,  privateIpAddress);
 +        }
 +        return accessDetails;
 +    }
 +
 +    protected boolean stateTransitTo(final NetworkVO network, final Network.Event e) throws NoTransitionException {
 +        return _stateMachine.transitTo(network, e, null, _networksDao);
 +    }
 +
 +    private void setStateMachine() {
 +        _stateMachine = Network.State.getStateMachine();
 +    }
 +
 +    private Map<Service, Set<Provider>> getServiceProvidersMap(final long networkId) {
 +        final Map<Service, Set<Provider>> map = new HashMap<Service, Set<Provider>>();
 +        final List<NetworkServiceMapVO> nsms = _ntwkSrvcDao.getServicesInNetwork(networkId);
 +        for (final 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;
 +    }
 +
 +    @Override
 +    public List<Provider> getProvidersForServiceInNetwork(final Network network, final Service service) {
 +        final Map<Service, Set<Provider>> service2ProviderMap = getServiceProvidersMap(network.getId());
 +        if (service2ProviderMap.get(service) != null) {
 +            final List<Provider> providers = new ArrayList<Provider>(service2ProviderMap.get(service));
 +            return providers;
 +        }
 +        return null;
 +    }
 +
 +    protected List<NetworkElement> getElementForServiceInNetwork(final Network network, final Service service) {
 +        final List<NetworkElement> elements = new ArrayList<NetworkElement>();
 +        final List<Provider> providers = getProvidersForServiceInNetwork(network, service);
 +        //Only support one provider now
 +        if (providers == null) {
 +            s_logger.error("Cannot find " + service.getName() + " provider for network " + network.getId());
 +            return null;
 +        }
 +        if (providers.size() != 1 && service != Service.Lb) {
 +            //support more than one LB providers only
 +            s_logger.error("Found " + providers.size() + " " + service.getName() + " providers for network!" + network.getId());
 +            return null;
 +        }
 +
 +        for (final Provider provider : providers) {
 +            final NetworkElement element = _networkModel.getElementImplementingProvider(provider.getName());
 +            s_logger.info("Let " + element.getName() + " handle " + service.getName() + " in network " + network.getId());
 +            elements.add(element);
 +        }
 +        return elements;
 +    }
 +
 +    @Override
 +    public StaticNatServiceProvider getStaticNatProviderForNetwork(final Network network) {
 +        //only one provider per Static nat service is supoprted
 +        final NetworkElement element = getElementForServiceInNetwork(network, Service.StaticNat).get(0);
 +        assert element instanceof StaticNatServiceProvider;
 +        return (StaticNatServiceProvider)element;
 +    }
 +
 +    @Override
 +    public LoadBalancingServiceProvider getLoadBalancingProviderForNetwork(final Network network, final Scheme lbScheme) {
 +        final List<NetworkElement> lbElements = getElementForServiceInNetwork(network, Service.Lb);
 +        NetworkElement lbElement = null;
 +        if (lbElements.size() > 1) {
 +            String providerName = null;
 +            //get network offering details
 +            final NetworkOffering off = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
 +            if (lbScheme == Scheme.Public) {
 +                providerName = _ntwkOffDetailsDao.getDetail(off.getId(), NetworkOffering.Detail.PublicLbProvider);
 +            } else {
 +                providerName = _ntwkOffDetailsDao.getDetail(off.getId(), NetworkOffering.Detail.InternalLbProvider);
 +            }
 +            if (providerName == null) {
 +                throw new InvalidParameterValueException("Can't find Lb provider supporting scheme " + lbScheme.toString() + " in network " + network);
 +            }
 +            lbElement = _networkModel.getElementImplementingProvider(providerName);
 +        } else if (lbElements.size() == 1) {
 +            lbElement = lbElements.get(0);
 +        }
 +
 +        assert lbElement != null;
 +        assert lbElement instanceof LoadBalancingServiceProvider;
 +        return (LoadBalancingServiceProvider)lbElement;
 +    }
 +
 +    @Override
 +    public boolean isNetworkInlineMode(final Network network) {
 +        final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +        return offering.isInline();
 +    }
 +
 +    @Override
 +    public boolean isSecondaryIpSetForNic(final long nicId) {
 +        final NicVO nic = _nicDao.findById(nicId);
 +        return nic.getSecondaryIp();
 +    }
 +
 +    private boolean removeVmSecondaryIpsOfNic(final long nicId) {
 +        Transaction.execute(new TransactionCallbackNoReturn() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus status) {
 +                final List<NicSecondaryIpVO> ipList = _nicSecondaryIpDao.listByNicId(nicId);
 +                if (ipList != null) {
 +                    for (final NicSecondaryIpVO ip : ipList) {
 +                        _nicSecondaryIpDao.remove(ip.getId());
 +                    }
 +                    s_logger.debug("Revoving nic secondary ip entry ...");
 +                }
 +            }
 +        });
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public NicVO savePlaceholderNic(final Network network, final String ip4Address, final String ip6Address, final Type vmType) {
 +        final NicVO nic = new NicVO(null, null, network.getId(), null);
 +        nic.setIPv4Address(ip4Address);
 +        nic.setIPv6Address(ip6Address);
 +        nic.setReservationStrategy(ReservationStrategy.PlaceHolder);
 +        nic.setState(Nic.State.Reserved);
 +        nic.setVmType(vmType);
 +        return _nicDao.persist(nic);
 +    }
 +
 +    @Override
 +    public String getConfigComponentName() {
 +        return NetworkOrchestrationService.class.getSimpleName();
 +    }
 +
 +    public static final ConfigKey<Integer> NetworkGcWait = new ConfigKey<Integer>(Integer.class, "network.gc.wait", "Advanced", "600",
 +            "Time (in seconds) to wait before shutting down a network that's not in used", false, Scope.Global, null);
 +    public static final ConfigKey<Integer> NetworkGcInterval = new ConfigKey<Integer>(Integer.class, "network.gc.interval", "Advanced", "600",
 +            "Seconds to wait before checking for networks to shutdown", true, Scope.Global, null);
 +
 +    @Override
 +    public ConfigKey<?>[] getConfigKeys() {
 +        return new ConfigKey<?>[] {NetworkGcWait, NetworkGcInterval, NetworkLockTimeout,
 +                GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion,
 +                PromiscuousMode, MacAddressChanges, ForgedTransmits, RollingRestartEnabled};
 +    }
 +}
diff --cc plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java
index b92e827,0000000..93b660a
mode 100644,000000..100644
--- a/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java
+++ b/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java
@@@ -1,788 -1,0 +1,793 @@@
 +//
 +// 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.element;
 +
 +import java.util.ArrayList;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +
 +import javax.annotation.Nullable;
 +import javax.inject.Inject;
 +import javax.naming.ConfigurationException;
 +
 +import net.nuage.vsp.acs.client.api.model.VspAclRule;
 +import net.nuage.vsp.acs.client.api.model.VspDhcpDomainOption;
 +import net.nuage.vsp.acs.client.api.model.VspNetwork;
 +import net.nuage.vsp.acs.client.api.model.VspStaticNat;
 +
 +import org.apache.commons.collections.CollectionUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.log4j.Logger;
 +
 +import com.google.common.base.Function;
 +import com.google.common.base.Preconditions;
 +import com.google.common.collect.ImmutableMap;
 +import com.google.common.collect.ImmutableSet;
 +import com.google.common.collect.Lists;
 +import com.google.common.collect.Multimap;
 +import com.google.common.collect.Sets;
 +
 +import org.apache.cloudstack.api.InternalIdentity;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.network.ExternalNetworkDeviceManager;
 +import org.apache.cloudstack.resourcedetail.VpcDetailVO;
 +import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
 +
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.StartupCommand;
 +import com.cloud.agent.api.StartupVspCommand;
 +import com.cloud.agent.api.element.ApplyAclRuleVspCommand;
 +import com.cloud.agent.api.element.ApplyStaticNatVspCommand;
 +import com.cloud.agent.api.element.ExtraDhcpOptionsVspCommand;
 +import com.cloud.agent.api.element.ImplementVspCommand;
 +import com.cloud.agent.api.element.ShutDownVpcVspCommand;
 +import com.cloud.agent.api.element.ShutDownVspCommand;
 +import com.cloud.dc.Vlan;
 +import com.cloud.dc.VlanVO;
 +import com.cloud.dc.dao.VlanDao;
 +import com.cloud.dc.dao.VlanDetailsDao;
 +import com.cloud.deploy.DeployDestination;
 +import com.cloud.domain.Domain;
 +import com.cloud.domain.dao.DomainDao;
 +import com.cloud.exception.ConcurrentOperationException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.ResourceUnavailableException;
 +import com.cloud.exception.UnsupportedServiceException;
 +import com.cloud.host.Host;
 +import com.cloud.host.HostVO;
 +import com.cloud.network.Network;
 +import com.cloud.network.Network.Capability;
 +import com.cloud.network.Network.Provider;
 +import com.cloud.network.Network.Service;
 +import com.cloud.network.NetworkMigrationManager;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.Networks;
 +import com.cloud.network.PhysicalNetworkServiceProvider;
 +import com.cloud.network.PublicIpAddress;
 +import com.cloud.network.dao.FirewallRulesCidrsDao;
 +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.NetworkServiceMapDao;
 +import com.cloud.network.dao.PhysicalNetworkDao;
 +import com.cloud.network.dao.PhysicalNetworkVO;
 +import com.cloud.network.manager.NuageVspManager;
 +import com.cloud.network.manager.NuageVspManagerImpl;
 +import com.cloud.network.rules.FirewallRule;
 +import com.cloud.network.rules.FirewallRule.FirewallRuleType;
 +import com.cloud.network.rules.FirewallRuleVO;
 +import com.cloud.network.rules.StaticNat;
 +import com.cloud.network.vpc.NetworkACLItem;
 +import com.cloud.network.vpc.NetworkACLItemDao;
 +import com.cloud.network.vpc.NetworkACLItemVO;
 +import com.cloud.network.vpc.PrivateGateway;
 +import com.cloud.network.vpc.StaticRouteProfile;
 +import com.cloud.network.vpc.Vpc;
 +import com.cloud.network.vpc.VpcOfferingServiceMapVO;
 +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offerings.NetworkOfferingVO;
 +import com.cloud.offerings.dao.NetworkOfferingDao;
 +import com.cloud.resource.ResourceManager;
 +import com.cloud.resource.ResourceStateAdapter;
 +import com.cloud.resource.ServerResource;
 +import com.cloud.resource.UnableDeleteHostException;
 +import com.cloud.server.ResourceTag;
 +import com.cloud.tags.dao.ResourceTagDao;
 +import com.cloud.util.NuageVspEntityBuilder;
 +import com.cloud.util.NuageVspUtil;
 +import com.cloud.utils.Pair;
 +import com.cloud.utils.component.AdapterBase;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.vm.DomainRouterVO;
 +import com.cloud.vm.NicProfile;
 +import com.cloud.vm.NicVO;
 +import com.cloud.vm.ReservationContext;
 +import com.cloud.vm.VirtualMachineProfile;
 +import com.cloud.vm.dao.DomainRouterDao;
 +import com.cloud.vm.dao.NicDao;
 +
 +public class NuageVspElement extends AdapterBase implements ConnectivityProvider, IpDeployer, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider,
 +        DhcpServiceProvider, ResourceStateAdapter, VpcProvider, NetworkACLServiceProvider {
 +
 +    private static final Logger s_logger = Logger.getLogger(NuageVspElement.class);
 +
 +    private static final Map<Service, Map<Capability, String>> capabilities = setCapabilities();
 +
 +    private static final Set<Service> REQUIRED_SERVICES = ImmutableSet.of(
 +            Service.Connectivity,
 +            Service.Dhcp
 +    );
 +    private static final Set<Service> NUAGE_ONLY_SERVICES = ImmutableSet.of(
 +            Service.SourceNat,
 +            Service.StaticNat,
 +            Service.Gateway
 +    );
 +    private static final Set<Service> UNSUPPORTED_SERVICES = ImmutableSet.of(
 +            Service.Vpn,
 +            Service.Dns,
 +            Service.PortForwarding,
 +            Service.SecurityGroup
 +    );
 +    private static final Set<Pair<Service, Service>> ANY_REQUIRED_SERVICES = ImmutableSet.of(
 +            new Pair<>(Service.SourceNat, Service.StaticNat)
 +    );
 +
 +
 +    public static final ExternalNetworkDeviceManager.NetworkDevice NuageVspDevice = new ExternalNetworkDeviceManager.NetworkDevice("NuageVsp", Provider.NuageVsp.getName());
 +
 +    @Inject
 +    ResourceManager _resourceMgr;
 +    @Inject
 +    NetworkModel _networkModel;
 +    @Inject
 +    NetworkServiceMapDao _ntwkSrvcDao;
 +    @Inject
 +    NetworkDao _networkDao;
 +    @Inject
 +    DomainDao _domainDao;
 +    @Inject
 +    IPAddressDao _ipAddressDao;
 +    @Inject
 +    VlanDao _vlanDao;
 +    @Inject
 +    VlanDetailsDao _vlanDetailsDao;
 +    @Inject
 +    NicDao _nicDao;
 +    @Inject
 +    VpcOfferingServiceMapDao _vpcOfferingSrvcDao;
 +    @Inject
 +    AgentManager _agentMgr;
 +    @Inject
 +    NetworkOfferingDao _ntwkOfferingDao;
 +    @Inject
 +    ConfigurationDao _configDao;
 +    @Inject
 +    NuageVspManager _nuageVspManager;
 +    @Inject
 +    FirewallRulesDao _firewallRulesDao;
 +    @Inject
 +    FirewallRulesCidrsDao _firewallRulesCidrsDao;
 +    @Inject
 +    PhysicalNetworkDao _physicalNetworkDao;
 +    @Inject
 +    NetworkACLItemDao _networkACLItemDao;
 +    @Inject
 +    NuageVspEntityBuilder _nuageVspEntityBuilder;
 +    @Inject
 +    VpcDetailsDao _vpcDetailsDao;
 +    @Inject
 +    DomainRouterDao _routerDao;
 +    @Inject
 +    ResourceTagDao _resourceTagDao;
 +
 +    @Override
 +    public boolean applyIps(Network network, List<? extends PublicIpAddress> ipAddress, Set<Service> service) throws ResourceUnavailableException {
 +        return false;
 +    }
 +
 +    @Override
 +    public Map<Service, Map<Capability, String>> getCapabilities() {
 +        return capabilities;
 +    }
 +
 +    private static Map<Service, Map<Capability, String>> setCapabilities() {
 +        return ImmutableMap.<Service, Map<Capability, String>>builder()
 +            .put(Service.Connectivity, ImmutableMap.of(
 +                    Capability.NoVlan, "",
 +                    Capability.PublicAccess, ""
 +            ))
 +            .put(Service.Gateway, ImmutableMap.<Capability, String>of())
 +            .put(Service.SourceNat, ImmutableMap.of(
 +                    Capability.SupportedSourceNatTypes, "perzone",
 +                    Capability.RedundantRouter, "false"
 +            ))
 +            .put(Service.StaticNat, ImmutableMap.<Capability, String>of())
 +            .put(Service.SecurityGroup, ImmutableMap.<Capability, String>of())
 +            .put(Service.Firewall, ImmutableMap.of(
 +                    Capability.TrafficStatistics, "per public ip",
 +                    Capability.SupportedProtocols, "tcp,udp,icmp",
 +                    Capability.SupportedEgressProtocols, "tcp,udp,icmp, all",
 +                    Capability.SupportedTrafficDirection, "ingress, egress",
 +                    Capability.MultipleIps, "true"
 +            ))
 +            .put(Service.Dhcp, ImmutableMap.of(
 +                    Capability.DhcpAccrossMultipleSubnets, "true",
 +                    Capability.ExtraDhcpOptions, "true"
 +            ))
 +            .put(Service.NetworkACL, ImmutableMap.of(
 +                    Capability.SupportedProtocols, "tcp,udp,icmp"
 +            ))
 +            .build();
 +    }
 +
 +    @Override
 +    public Provider getProvider() {
 +        return Provider.NuageVsp;
 +    }
 +
 +    @Override
 +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
 +        super.configure(name, params);
 +        _resourceMgr.registerResourceStateAdapter(name, this);
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
 +            ResourceUnavailableException, InsufficientCapacityException {
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Entering NuageElement implement function for network " + network.getDisplayText() + " (state " + network.getState() + ")");
 +        }
 +
 +        if (network.getVpcId() != null) {
 +            return applyACLRulesForVpc(network, offering);
 +        }
 +
 +        if (!canHandle(network, offering, Service.Connectivity)) {
 +            return false;
 +        }
 +
 +        if (network.getBroadcastUri() == null) {
 +            s_logger.error("Nic has no broadcast Uri with the virtual router IP");
 +            return false;
 +        }
 +
 +        _nuageVspManager.updateBroadcastUri(network);
 +        network = _networkDao.findById(network.getId());
 +        VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network);
 +        List<VspAclRule> ingressFirewallRules = getFirewallRulesToApply(network, FirewallRule.TrafficType.Ingress);
 +        List<VspAclRule> egressFirewallRules = getFirewallRulesToApply(network, FirewallRule.TrafficType.Egress);
 +
 +        List<IPAddressVO> ips = _ipAddressDao.listStaticNatPublicIps(network.getId());
 +        List<String> floatingIpUuids = new ArrayList<String>();
 +        for (IPAddressVO ip : ips) {
 +            floatingIpUuids.add(ip.getUuid());
 +        }
 +        VspDhcpDomainOption vspDhcpOptions = _nuageVspEntityBuilder.buildNetworkDhcpOption(network, offering);
 +        HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId());
 +        ImplementVspCommand cmd = new ImplementVspCommand(vspNetwork, ingressFirewallRules, egressFirewallRules, floatingIpUuids, vspDhcpOptions);
 +        send(cmd, network);
 +
 +        return true;
 +    }
 +
 +    private void send(Command cmd, Network network)
 +            throws ResourceUnavailableException {
 +        send(cmd, network.getPhysicalNetworkId(), Network.class, network);
 +    }
 +
 +    private void send(Command cmd, Vpc vpc)
 +            throws ResourceUnavailableException {
 +        send(cmd, getPhysicalNetworkId(vpc.getZoneId()), Vpc.class, vpc);
 +    }
 +
 +
 +    private <R extends InternalIdentity> void send(Command cmd, long physicalNetworkId, Class<R> resourceClass,
 +            R resource)
 +            throws ResourceUnavailableException {
 +        HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(physicalNetworkId);
 +        Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd);
 +        if (isFailure(answer)) {
 +            s_logger.error(cmd.getClass().getName() + " for " + resourceClass.getName() + " " + resource.getId() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname"));
 +            if (hasFailureDetails(answer)) {
 +                throw new ResourceUnavailableException(answer.getDetails(), resourceClass, resource.getId());
 +            }
 +        }
 +    }
 +
 +    private boolean hasFailureDetails(Answer answer) {
 +        return (null != answer) && (null != answer.getDetails());
 +    }
 +
 +    private boolean isFailure(Answer answer) {
 +        return answer == null || !answer.getResult();
 +    }
 +
 +    private boolean applyACLRulesForVpc(Network network, NetworkOffering offering) throws ResourceUnavailableException {
 +        List<NetworkACLItemVO> rules = _networkACLItemDao.listByACL(network.getNetworkACLId());
 +        if (_networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.NetworkACL)) {
 +            applyACLRules(network, rules, true, false);
 +        }
 +        return true;
 +    }
 +
 +    private List<VspAclRule> getFirewallRulesToApply(final Network network, FirewallRule.TrafficType trafficType) {
 +        List<FirewallRuleVO> firewallRulesToApply = _firewallRulesDao.listByNetworkPurposeTrafficType(network.getId(), FirewallRule.Purpose.Firewall, trafficType);
 +        List<VspAclRule> vspAclRulesToApply = Lists.newArrayListWithExpectedSize(firewallRulesToApply.size());
 +
 +        for (FirewallRuleVO rule : firewallRulesToApply) {
 +            rule.setSourceCidrList(_firewallRulesCidrsDao.getSourceCidrs(rule.getId()));
 +            VspAclRule vspAclRule = _nuageVspEntityBuilder.buildVspAclRule(rule, network);
 +            vspAclRulesToApply.add(vspAclRule);
 +        }
 +        return vspAclRulesToApply;
 +    }
 +
 +    @Override
 +    public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
 +            ResourceUnavailableException, InsufficientCapacityException {
 +        if (!canHandle(network, Service.Connectivity)) {
 +            return false;
 +        }
 +
 +        if (network.getBroadcastUri() == null) {
 +            s_logger.error("Nic has no broadcast Uri with the virtual router IP");
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
 +        if (!canHandle(network, Service.Connectivity)) {
 +            return false;
 +        }
 +
 +        if (network.getBroadcastUri() == null) {
 +            s_logger.error("Nic has no broadcast Uri with the virtual router IP");
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
 +        if (!canHandle(network, Service.Connectivity)) {
 +            return false;
 +        }
 +        if (cleanup && isDnsSupportedByVR(network)) {
 +            // The network is restarted, possibly the domain name is changed, update the dhcpOptions as soon as possible
 +            NetworkOfferingVO networkOfferingVO = _ntwkOfferingDao.findById(network.getNetworkOfferingId());
 +            VspDhcpDomainOption vspDhcpOptions = _nuageVspEntityBuilder.buildNetworkDhcpOption(network, networkOfferingVO);
 +            VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network);
 +
 +            ShutDownVspCommand cmd = new ShutDownVspCommand(vspNetwork, vspDhcpOptions);
 +            send(cmd, network);
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean isReady(PhysicalNetworkServiceProvider provider) {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean canEnableIndividualServices() {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
 +        return canHandle(network, Service.Connectivity);
 +    }
 +
 +    @Override
 +    public boolean verifyServicesCombination(Set<Service> services) {
 +        Preconditions.checkNotNull(services);
 +        final Sets.SetView<Service> missingServices = Sets.difference(REQUIRED_SERVICES, services);
 +        final Sets.SetView<Service> unsupportedServices = Sets.intersection(UNSUPPORTED_SERVICES, services);
 +        final Sets.SetView<Service> wantedServices = Sets.intersection(NUAGE_ONLY_SERVICES, new HashSet<>());
 +
 +        if (!missingServices.isEmpty()) {
 +            throw new UnsupportedServiceException("Provider " + Provider.NuageVsp + " requires services: " + missingServices);
 +        }
 +
 +        if (!unsupportedServices.isEmpty()) {
 +            // NuageVsp doesn't implement any of these services.
 +            // So if these services are requested, we can't handle it.
 +            s_logger.debug("Unable to support services combination. The services " + unsupportedServices + " are not supported by Nuage VSP.");
 +            return false;
 +        }
 +
 +        if (!wantedServices.isEmpty()) {
 +            throw new UnsupportedServiceException("Provider " + Provider.NuageVsp + " does not support services to be implemented by another provider: " + wantedServices);
 +        }
 +
 +        return true;
 +    }
 +
 +    protected boolean canHandle(Network network, Service service) {
 +        NetworkOffering networkOffering = _ntwkOfferingDao.findById(network.getNetworkOfferingId());
 +        return canHandle(network, networkOffering, service);
 +    }
 +
 +    protected boolean canHandle(Network network, NetworkOffering networkOffering, Service service) {
 +        if (network.getBroadcastDomainType() != Networks.BroadcastDomainType.Vsp) {
 +            return false;
 +        }
 +
 +        if (!_networkModel.isProviderForNetwork(getProvider(), network.getId())) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("NuageVsp is not a provider for network " + network.getDisplayText());
 +            }
 +            return false;
 +        }
 +
 +        if (service != null) {
 +            if (!_ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), service, getProvider())) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("NuageVsp can't provide the " + service.getName() + " service on network " + network.getDisplayText());
 +                }
 +                return false;
 +            }
 +        }
 +
 +        if (service != Service.Connectivity
 +                && !_ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), Service.Connectivity, getProvider())) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("NuageVsp can't handle networks which use a network offering without NuageVsp as Connectivity provider");
 +            }
 +            return false;
 +        }
 +
 +        if (service != Service.SourceNat
 +                && networkOffering.getGuestType() == Network.GuestType.Isolated
 +                && !_ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), Service.SourceNat, getProvider())) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("NuageVsp can't handle networks which use a network offering without NuageVsp as SourceNat provider");
 +            }
 +            return false;
 +        }
 +
 +        if (networkOffering.isSpecifyVlan()) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("NuageVsp doesn't support VLAN values for networks");
 +            }
 +            return false;
 +        }
 +
 +        if (network.getVpcId() != null && !networkOffering.isPersistent()) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("NuageVsp can't handle VPC tiers which use a network offering which are not persistent");
 +            }
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException,
 +            InsufficientCapacityException, ResourceUnavailableException {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean configDhcpSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context)
 +            throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +        return true;
 +    }
 +
 +    private boolean isDnsSupportedByVR(Network network) {
 +        return (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dns) &&
 +                ( _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns,  Provider.VirtualRouter) ||
 +                  _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns,  Provider.VPCVirtualRouter)));
 +    }
 +
 +    @Override
 +    public boolean removeDhcpSupportForSubnet(Network network) throws ResourceUnavailableException {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
 +        if (network.isRollingRestart()) {
 +            return true;
 +        }
 +
 +        VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network);
 +        HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId());
 +        NicVO nic = _nicDao.findById(nicId);
 +
 +        ExtraDhcpOptionsVspCommand extraDhcpOptionsVspCommand = new ExtraDhcpOptionsVspCommand(vspNetwork, nic.getUuid(), dhcpOptions);
 +        Answer answer = _agentMgr.easySend(nuageVspHost.getId(), extraDhcpOptionsVspCommand);
 +        if (isFailure(answer)) {
 +            s_logger.error("[setExtraDhcpOptions] setting extra DHCP options for nic " + nic.getUuid() + " failed.");
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
++    public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) {
++        return false;
++    }
++
++    @Override
 +    public boolean applyStaticNats(Network config, List<? extends StaticNat> rules) throws ResourceUnavailableException {
 +        List<VspStaticNat> vspStaticNatDetails = new ArrayList<VspStaticNat>();
 +        for (StaticNat staticNat : rules) {
 +            IPAddressVO sourceNatIp = _ipAddressDao.findById(staticNat.getSourceIpAddressId());
 +            VlanVO sourceNatVlan = _vlanDao.findById(sourceNatIp.getVlanId());
 +            checkVlanUnderlayCompatibility(sourceNatVlan);
 +
 +            if (!staticNat.isForRevoke()) {
 +                final List<FirewallRuleVO> firewallRules = _firewallRulesDao.listByIpAndNotRevoked(staticNat.getSourceIpAddressId());
 +                for (FirewallRuleVO firewallRule : firewallRules) {
 +                    _nuageVspEntityBuilder.buildVspAclRule(firewallRule, config, sourceNatIp);
 +                }
 +            }
 +
 +            NicVO nicVO = _nicDao.findByIp4AddressAndNetworkId(staticNat.getDestIpAddress(), staticNat.getNetworkId());
 +            VspStaticNat vspStaticNat = _nuageVspEntityBuilder.buildVspStaticNat(staticNat.isForRevoke(), sourceNatIp, sourceNatVlan, nicVO);
 +            vspStaticNatDetails.add(vspStaticNat);
 +
 +
 +        }
 +
 +        VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(config);
 +        ApplyStaticNatVspCommand cmd = new ApplyStaticNatVspCommand(vspNetwork, vspStaticNatDetails);
 +        send(cmd,
 +             config);
 +
 +        return true;
 +    }
 +
 +    private void checkVlanUnderlayCompatibility(VlanVO newVlan) throws ResourceUnavailableException {
 +        List<VlanVO> vlans = _vlanDao.listByZone(newVlan.getDataCenterId());
 +        if (CollectionUtils.isNotEmpty(vlans)) {
 +            boolean newVlanUnderlay = NuageVspUtil.isUnderlayEnabledForVlan(_vlanDetailsDao, newVlan);
 +            final String newCidr = NetUtils.getCidrFromGatewayAndNetmask(newVlan.getVlanGateway(), newVlan.getVlanNetmask());
 +
 +            for (VlanVO vlan : vlans) {
 +                if (vlan.getId() == newVlan.getId()) continue;
 +
 +                final String existingCidr = NetUtils.getCidrFromGatewayAndNetmask(vlan.getVlanGateway(), vlan.getVlanNetmask());
 +
 +                NetUtils.SupersetOrSubset supersetOrSubset = NetUtils.isNetowrkASubsetOrSupersetOfNetworkB(newCidr, existingCidr);
 +                if (supersetOrSubset == NetUtils.SupersetOrSubset.sameSubnet) {
 +                    boolean vlanUnderlay = NuageVspUtil.isUnderlayEnabledForVlan(_vlanDetailsDao, vlan);
 +                    if (newVlanUnderlay != vlanUnderlay) {
 +                        throw new ResourceUnavailableException("Mixed values for the underlay flag for IP ranges in the same subnet is not supported", Vlan.class, newVlan.getId());
 +                    }
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public IpDeployer getIpDeployer(Network network) {
 +        return this;
 +    }
 +
 +    @Override
 +    public boolean applyFWRules(Network network, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
 +        if (rules == null || rules.isEmpty()) {
 +            return true;
 +        }
 +
 +        if (rules.size() == 1 && rules.iterator().next().getType().equals(FirewallRuleType.System)) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Default ACL added by CS as system is ignored for network " + network.getName() + " with rule " + rules);
 +            }
 +            return true;
 +        }
 +
 +        s_logger.info("Applying " + rules.size() + " Firewall Rules for network " + network.getName());
 +        return applyACLRules(network, rules, false, false);
 +    }
 +
 +    protected boolean applyACLRules(final Network network, List<? extends InternalIdentity> rules, boolean isNetworkAcl, boolean networkReset)
 +            throws ResourceUnavailableException {
 +        VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network);
 +        List<VspAclRule> vspAclRules = Lists.transform(rules, new Function<InternalIdentity, VspAclRule>() {
 +            @Nullable
 +            @Override
 +            public VspAclRule apply(@Nullable InternalIdentity input) {
 +                if (input instanceof FirewallRule) {
 +                    return _nuageVspEntityBuilder.buildVspAclRule((FirewallRule) input, network);
 +                }
 +                return _nuageVspEntityBuilder.buildVspAclRule((NetworkACLItem) input);
 +            }
 +        });
 +
 +        VspAclRule.ACLType vspAclType = isNetworkAcl ? VspAclRule.ACLType.NetworkACL : VspAclRule.ACLType.Firewall;
 +        ApplyAclRuleVspCommand cmd = new ApplyAclRuleVspCommand(vspAclType, vspNetwork, vspAclRules, networkReset);
 +        send(cmd,
 +             network);
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean applyNetworkACLs(Network config, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
 +        if (rules == null || rules.isEmpty()) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("No rules to apply. So, delete all the existing ACL in VSP from Subnet with uuid " + config.getUuid());
 +            }
 +        } else {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("New rules has to applied. So, delete all the existing ACL in VSP from Subnet with uuid " + config.getUuid());
 +            }
 +        }
 +        if (rules != null) {
 +            s_logger.info("Applying " + rules.size() + " Network ACLs for network " + config.getName());
 +            applyACLRules(config, rules, true, rules.isEmpty());
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
 +        List<VpcOfferingServiceMapVO> vpcOfferingServices = _vpcOfferingSrvcDao.listByVpcOffId(vpc.getVpcOfferingId());
 +        Multimap<Service, Provider> supportedVpcServices = NuageVspManagerImpl.SUPPORTED_NUAGE_VSP_VPC_SERVICE_MAP;
 +        for (VpcOfferingServiceMapVO vpcOfferingService : vpcOfferingServices) {
 +            Network.Service service = Network.Service.getService(vpcOfferingService.getService());
 +            if (!supportedVpcServices.containsKey(service)) {
 +                s_logger.warn(String.format("NuageVsp doesn't support service %s for VPCs", service.getName()));
 +                return false;
 +            }
 +
 +            Network.Provider provider = Network.Provider.getProvider(vpcOfferingService.getProvider());
 +            if (!supportedVpcServices.containsEntry(service, provider)) {
 +                s_logger.warn(String.format("NuageVsp doesn't support provider %s for service %s for VPCs", provider.getName(), service.getName()));
 +                return false;
 +            }
 +        }
 +
 +        String globalDomainTemplate = _nuageVspManager.NuageVspVpcDomainTemplateName.value();
 +        if (StringUtils.isNotBlank(globalDomainTemplate) && !_nuageVspManager.checkIfDomainTemplateExist(vpc.getDomainId(),globalDomainTemplate,vpc.getZoneId(),null)) {
 +            s_logger.warn("The global pre configured domain template does not exist on the VSD.");
 +            throw new CloudRuntimeException("The global pre configured domain template does not exist on the VSD.");
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean shutdownVpc(Vpc vpc, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
 +        if (vpc.getState().equals(Vpc.State.Inactive)) {
 +            List<DomainRouterVO> routers = _routerDao.listByVpcId(vpc.getId());
 +            if (CollectionUtils.isEmpty(routers)) {
 +                routers = _routerDao.listIncludingRemovedByVpcId(vpc.getId());
 +            }
 +
 +            List<String> domainRouterUuids = Lists.transform(routers, new Function<DomainRouterVO, String>() {
 +                @Nullable
 +                @Override
 +                public String apply(@Nullable DomainRouterVO input) {
 +                    return input != null ? input.getUuid() : null;
 +                }
 +            });
 +
 +            Domain vpcDomain = _domainDao.findById(vpc.getDomainId());
 +
 +            String preConfiguredDomainTemplateName;
 +            VpcDetailVO domainTemplateNameDetail = _vpcDetailsDao.findDetail(vpc.getId(), NuageVspManager.nuageDomainTemplateDetailName);
 +            if (domainTemplateNameDetail != null) {
 +                preConfiguredDomainTemplateName = domainTemplateNameDetail.getValue();
 +            } else {
 +                preConfiguredDomainTemplateName = _configDao.getValue(NuageVspManager.NuageVspVpcDomainTemplateName.key());
 +            }
 +
 +            Map<String, String> vpcDetails = _vpcDetailsDao.listDetailsKeyPairs(vpc.getId(), false);
 +
 +            cleanUpVpcCaching(vpc.getId());
 +            //related to migration caching
 +            List<? extends ResourceTag> vpcResourceDetails = _resourceTagDao.listByResourceUuid(vpc.getUuid());
 +            if (vpcResourceDetails != null) {
 +                vpcResourceDetails.stream()
 +                                  .filter(item -> item.getKey().equals(NetworkMigrationManager.MIGRATION))
 +                                  .findFirst()
 +                                  .map(ResourceTag::getResourceId)
 +                                  .ifPresent(this::cleanUpVpcCaching);
 +            }
 +
 +            ShutDownVpcVspCommand cmd = new ShutDownVpcVspCommand(vpcDomain.getUuid(), vpc.getUuid(), preConfiguredDomainTemplateName, domainRouterUuids, vpcDetails);
 +            send(cmd, vpc);
 +        }
 +        return true;
 +    }
 +
 +    private void cleanUpVpcCaching(long vpcId) {
 +        _vpcDetailsDao.removeDetail(vpcId, NuageVspManager.NETWORK_METADATA_VSD_DOMAIN_ID);
 +        _vpcDetailsDao.removeDetail(vpcId, NuageVspManager.NETWORK_METADATA_VSD_ZONE_ID);
 +    }
 +
 +    private Long getPhysicalNetworkId(Long zoneId) {
 +        Long guestPhysicalNetworkId = 0L;
 +        List<PhysicalNetworkVO> physicalNetworkList = _physicalNetworkDao.listByZone(zoneId);
 +        for (PhysicalNetworkVO phyNtwk : physicalNetworkList) {
 +            if (phyNtwk.getIsolationMethods().contains("VSP")) {
 +                guestPhysicalNetworkId = phyNtwk.getId();
 +                break;
 +            }
 +        }
 +        return guestPhysicalNetworkId;
 +    }
 +
 +    @Override
 +    public boolean createPrivateGateway(PrivateGateway gateway) throws ConcurrentOperationException, ResourceUnavailableException {
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean deletePrivateGateway(PrivateGateway privateGateway) throws ConcurrentOperationException, ResourceUnavailableException {
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean applyStaticRoutes(Vpc vpc, List<StaticRouteProfile> routes) throws ResourceUnavailableException {
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
 +        return false;
 +    }
 +
 +    @Override
 +    public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
 +        return null;
 +    }
 +
 +    @Override
 +    public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) {
 +        if (!(startup[0] instanceof StartupVspCommand)) {
 +            return null;
 +        }
 +        host.setType(Host.Type.L2Networking);
 +        return host;
 +    }
 +
 +    @Override
 +    public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
 +        if (!(host.getType() == Host.Type.L2Networking)) {
 +            return null;
 +        }
 +        return new DeleteHostAnswer(true);
 +    }
 +}
diff --cc server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
index 862ccfe,0000000..a6a4e0a
mode 100644,000000..100644
--- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
@@@ -1,1315 -1,0 +1,1343 @@@
 +// 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.element;
 +
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +
 +import javax.inject.Inject;
 +
 +import org.apache.commons.collections.CollectionUtils;
 +import org.apache.log4j.Logger;
 +import org.cloud.network.router.deployment.RouterDeploymentDefinition;
 +import org.cloud.network.router.deployment.RouterDeploymentDefinitionBuilder;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.beans.factory.annotation.Qualifier;
 +
 +import com.google.gson.Gson;
 +
 +import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd;
 +import org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd;
 +import org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElementCmd;
 +import org.apache.cloudstack.api.command.admin.router.ListOvsElementsCmd;
 +import org.apache.cloudstack.api.command.admin.router.ListVirtualRouterElementsCmd;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.network.topology.NetworkTopology;
 +import org.apache.cloudstack.network.topology.NetworkTopologyContext;
 +
 +import com.cloud.agent.api.to.LoadBalancerTO;
 +import com.cloud.configuration.ConfigurationManager;
 +import com.cloud.dc.DataCenter;
 +import com.cloud.dc.DataCenter.NetworkType;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.deploy.DeployDestination;
 +import com.cloud.exception.ConcurrentOperationException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.ResourceUnavailableException;
 +import com.cloud.host.dao.HostDao;
 +import com.cloud.hypervisor.Hypervisor.HypervisorType;
 +import com.cloud.network.Network;
 +import com.cloud.network.Network.Capability;
 +import com.cloud.network.Network.Provider;
 +import com.cloud.network.Network.Service;
 +import com.cloud.network.NetworkMigrationResponder;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.Networks;
 +import com.cloud.network.Networks.TrafficType;
 +import com.cloud.network.OvsProvider;
 +import com.cloud.network.PhysicalNetworkServiceProvider;
 +import com.cloud.network.PublicIpAddress;
 +import com.cloud.network.RemoteAccessVpn;
 +import com.cloud.network.VirtualRouterProvider;
 +import com.cloud.network.VirtualRouterProvider.Type;
 +import com.cloud.network.VpnUser;
 +import com.cloud.network.as.AutoScaleCounter;
 +import com.cloud.network.as.AutoScaleCounter.AutoScaleCounterType;
 +import com.cloud.network.dao.IPAddressDao;
 +import com.cloud.network.dao.LoadBalancerDao;
 +import com.cloud.network.dao.NetworkDao;
 +import com.cloud.network.dao.NetworkDetailVO;
 +import com.cloud.network.dao.NetworkDetailsDao;
 +import com.cloud.network.dao.OvsProviderDao;
 +import com.cloud.network.dao.VirtualRouterProviderDao;
 +import com.cloud.network.lb.LoadBalancingRule;
 +import com.cloud.network.lb.LoadBalancingRulesManager;
 +import com.cloud.network.router.NetworkHelper;
 +import com.cloud.network.router.VirtualRouter;
 +import com.cloud.network.router.VirtualRouter.Role;
 +import com.cloud.network.router.VpcVirtualNetworkApplianceManager;
 +import com.cloud.network.rules.FirewallRule;
 +import com.cloud.network.rules.LbStickinessMethod;
 +import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType;
 +import com.cloud.network.rules.LoadBalancerContainer;
 +import com.cloud.network.rules.PortForwardingRule;
 +import com.cloud.network.rules.RulesManager;
 +import com.cloud.network.rules.StaticNat;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offerings.NetworkOfferingVO;
 +import com.cloud.offerings.dao.NetworkOfferingDao;
 +import com.cloud.user.Account;
 +import com.cloud.user.AccountManager;
 +import com.cloud.utils.component.AdapterBase;
 +import com.cloud.utils.crypt.DBEncryptionUtil;
 +import com.cloud.utils.db.QueryBuilder;
 +import com.cloud.utils.db.SearchCriteria.Op;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.vm.DomainRouterVO;
 +import com.cloud.vm.NicProfile;
 +import com.cloud.vm.ReservationContext;
 +import com.cloud.vm.UserVmManager;
 +import com.cloud.vm.UserVmVO;
 +import com.cloud.vm.VirtualMachine;
 +import com.cloud.vm.VirtualMachine.State;
 +import com.cloud.vm.VirtualMachineProfile;
 +import com.cloud.vm.dao.DomainRouterDao;
 +import com.cloud.vm.dao.UserVmDao;
 +
 +public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider,
 +StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer,
 +NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider {
 +    private static final Logger s_logger = Logger.getLogger(VirtualRouterElement.class);
 +    public static final AutoScaleCounterType AutoScaleCounterCpu = new AutoScaleCounterType("cpu");
 +    public static final AutoScaleCounterType AutoScaleCounterMemory = new AutoScaleCounterType("memory");
 +    protected static final Map<Service, Map<Capability, String>> capabilities = setCapabilities();
 +
 +    @Inject
 +    NetworkDao _networksDao;
 +    @Inject
 +    NetworkModel _networkMdl;
 +    @Inject
 +    LoadBalancingRulesManager _lbMgr;
 +    @Inject
 +    NetworkOfferingDao _networkOfferingDao;
 +
 +    @Inject
 +    VpcVirtualNetworkApplianceManager _routerMgr;
 +    @Inject
 +    ConfigurationManager _configMgr;
 +    @Inject
 +    RulesManager _rulesMgr;
 +    @Inject
 +    UserVmManager _userVmMgr;
 +
 +    @Inject
 +    UserVmDao _userVmDao;
 +    @Inject
 +    DomainRouterDao _routerDao;
 +    @Inject
 +    LoadBalancerDao _lbDao;
 +    @Inject
 +    HostDao _hostDao;
 +    @Inject
 +    AccountManager _accountMgr;
 +    @Inject
 +    ConfigurationDao _configDao;
 +    @Inject
 +    VirtualRouterProviderDao _vrProviderDao;
 +    @Inject
 +    OvsProviderDao _ovsProviderDao;
 +    @Inject
 +    IPAddressDao _ipAddressDao;
 +    @Inject
 +    DataCenterDao _dcDao;
 +    @Inject
 +    NetworkModel _networkModel;
 +
 +    @Inject
 +    NetworkTopologyContext networkTopologyContext;
 +
 +    @Inject
 +    NetworkDetailsDao _networkDetailsDao;
 +
 +    @Inject
 +    protected RouterDeploymentDefinitionBuilder routerDeploymentDefinitionBuilder;
 +
 +    @Autowired
 +    @Qualifier("networkHelper")
 +    protected NetworkHelper _networkHelper;
 +
 +    protected boolean canHandle(final Network network, final Service service) {
 +        final Long physicalNetworkId = _networkMdl.getPhysicalNetworkId(network);
 +        if (physicalNetworkId == null) {
 +            return false;
 +        }
 +
 +        if (network.getVpcId() != null) {
 +            return false;
 +        }
 +
 +        if (!_networkMdl.isProviderEnabledInPhysicalNetwork(physicalNetworkId, Network.Provider.VirtualRouter.getName())) {
 +            return false;
 +        }
 +
 +        if (service == null) {
 +            if (!_networkMdl.isProviderForNetwork(getProvider(), network.getId())) {
 +                s_logger.trace("Element " + getProvider().getName() + " is not a provider for the network " + network);
 +                return false;
 +            }
 +        } else {
 +            if (!_networkMdl.isProviderSupportServiceInNetwork(network.getId(), service, getProvider())) {
 +                s_logger.trace("Element " + getProvider().getName() + " doesn't support service " + service.getName() + " in the network " + network);
 +                return false;
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean implement(final Network network, final NetworkOffering offering, final DeployDestination dest, final ReservationContext context)
 +            throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException {
 +
 +        if (offering.isSystemOnly()) {
 +            return false;
 +        }
 +
 +        final Map<VirtualMachineProfile.Param, Object> params = new HashMap<VirtualMachineProfile.Param, Object>(1);
 +        params.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true);
 +
 +        if (network.isRollingRestart()) {
 +            params.put(VirtualMachineProfile.Param.RollingRestart, true);
 +        }
 +
 +        final RouterDeploymentDefinition routerDeploymentDefinition =
 +                routerDeploymentDefinitionBuilder.create()
 +                .setGuestNetwork(network)
 +                .setDeployDestination(dest)
 +                .setAccountOwner(_accountMgr.getAccount(network.getAccountId()))
 +                .setParams(params)
 +                .build();
 +
 +        final List<DomainRouterVO> routers = routerDeploymentDefinition.deployVirtualRouter();
 +
 +        int expectedRouters = 1;
 +        if (offering.isRedundantRouter() || network.isRollingRestart()) {
 +            expectedRouters = 2;
 +        }
 +        if (routers == null || routers.size() < expectedRouters) {
 +            //we might have a router which is already deployed and running.
 +            //so check the no of routers in network currently.
 +            List<DomainRouterVO> current_routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            if (current_routers.size() < 2) {
 +                updateToFailedState(network);
 +                throw new ResourceUnavailableException("Can't find all necessary running routers!", DataCenter.class, network.getDataCenterId());
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean prepare(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final DeployDestination dest, final ReservationContext context)
 +            throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +        if (vm.getType() != VirtualMachine.Type.User || vm.getHypervisorType() == HypervisorType.BareMetal) {
 +            return false;
 +        }
 +
 +        if (!canHandle(network, null)) {
 +            return false;
 +        }
 +
 +        final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +        if (offering.isSystemOnly()) {
 +            return false;
 +        }
 +        if (!_networkMdl.isProviderEnabledInPhysicalNetwork(_networkMdl.getPhysicalNetworkId(network), getProvider().getName())) {
 +            return false;
 +        }
 +
 +        final RouterDeploymentDefinition routerDeploymentDefinition =
 +                routerDeploymentDefinitionBuilder.create()
 +                .setGuestNetwork(network)
 +                .setDeployDestination(dest)
 +                .setAccountOwner(_accountMgr.getAccount(network.getAccountId()))
 +                .setParams(vm.getParameters())
 +                .build();
 +
 +        final List<DomainRouterVO> routers = routerDeploymentDefinition.deployVirtualRouter();
 +
 +        if (routers == null || routers.size() == 0) {
 +            throw new ResourceUnavailableException("Can't find at least one running router!", DataCenter.class, network.getDataCenterId());
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean applyFWRules(final Network network, final List<? extends FirewallRule> rules) throws ResourceUnavailableException {
 +        boolean result = true;
 +        if (canHandle(network, Service.Firewall)) {
 +            final List<DomainRouterVO> routers = getRouters(network);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need to apply firewall rules on the backend; virtual " + "router doesn't exist in the network " + network.getId());
 +                return true;
 +            }
 +
 +            if (rules != null && rules.size() == 1) {
 +                // for VR no need to add default egress rule to ALLOW traffic
 +                //The default allow rule is added from the router defalut iptables rules iptables-router
 +                if (rules.get(0).getTrafficType() == FirewallRule.TrafficType.Egress && rules.get(0).getType() == FirewallRule.FirewallRuleType.System
 +                        && _networkMdl.getNetworkEgressDefaultPolicy(network.getId())) {
 +                    return true;
 +                }
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.applyFirewallRules(network, rules, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean validateLBRule(final Network network, final LoadBalancingRule rule) {
 +        final List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();
 +        rules.add(rule);
 +        if (canHandle(network, Service.Lb) && canHandleLbRules(rules)) {
 +            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            if (routers == null || routers.isEmpty()) {
 +                return true;
 +            }
 +            return _networkHelper.validateHAProxyLBRule(rule);
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean applyLBRules(final Network network, final List<LoadBalancingRule> rules) throws ResourceUnavailableException {
 +        boolean result = true;
 +        if (canHandle(network, Service.Lb)) {
 +            if (!canHandleLbRules(rules)) {
 +                return false;
 +            }
 +
 +            final List<DomainRouterVO> routers = getRouters(network);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need to apply lb rules on the backend; virtual " + "router doesn't exist in the network " + network.getId());
 +                return true;
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.applyLoadBalancingRules(network, rules, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public String[] applyVpnUsers(final RemoteAccessVpn vpn, final List<? extends VpnUser> users) throws ResourceUnavailableException {
 +        if (vpn.getNetworkId() == null) {
 +            return null;
 +        }
 +
 +        final Network network = _networksDao.findById(vpn.getNetworkId());
 +        if (canHandle(network, Service.Vpn)) {
 +            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need to apply vpn users on the backend; virtual router" + " doesn't exist in the network " + network.getId());
 +                return null;
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            return networkTopology.applyVpnUsers(network, users, routers);
 +        } else {
 +            s_logger.debug("Element " + getName() + " doesn't handle applyVpnUsers command");
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    public boolean startVpn(final RemoteAccessVpn vpn) throws ResourceUnavailableException {
 +        if (vpn.getNetworkId() == null) {
 +            return false;
 +        }
 +
 +        final Network network = _networksDao.findById(vpn.getNetworkId());
 +        if (canHandle(network, Service.Vpn)) {
 +            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need stop vpn on the backend; virtual router doesn't" + " exist in the network " + network.getId());
 +                return true;
 +            }
 +            return _routerMgr.startRemoteAccessVpn(network, vpn, routers);
 +        } else {
 +            s_logger.debug("Element " + getName() + " doesn't handle createVpn command");
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public boolean stopVpn(final RemoteAccessVpn vpn) throws ResourceUnavailableException {
 +        if (vpn.getNetworkId() == null) {
 +            return false;
 +        }
 +
 +        final Network network = _networksDao.findById(vpn.getNetworkId());
 +        if (canHandle(network, Service.Vpn)) {
 +            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need stop vpn on the backend; virtual router doesn't " + "exist in the network " + network.getId());
 +                return true;
 +            }
 +            return _routerMgr.deleteRemoteAccessVpn(network, vpn, routers);
 +        } else {
 +            s_logger.debug("Element " + getName() + " doesn't handle removeVpn command");
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public boolean applyIps(final Network network, final List<? extends PublicIpAddress> ipAddress, final Set<Service> services) throws ResourceUnavailableException {
 +        boolean canHandle = true;
 +        for (final Service service : services) {
 +            if (!canHandle(network, service)) {
 +                canHandle = false;
 +                break;
 +            }
 +        }
 +        boolean result = true;
 +        if (canHandle) {
 +            final List<DomainRouterVO> routers = getRouters(network);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need to associate ip addresses on the backend; virtual " + "router doesn't exist in the network " + network.getId());
 +                return true;
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.associatePublicIP(network, ipAddress, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public Provider getProvider() {
 +        return Provider.VirtualRouter;
 +    }
 +
 +    @Override
 +    public Map<Service, Map<Capability, String>> getCapabilities() {
 +        return capabilities;
 +    }
 +
 +    public static String getHAProxyStickinessCapability() {
 +        LbStickinessMethod method;
 +        final List<LbStickinessMethod> methodList = new ArrayList<LbStickinessMethod>(1);
 +
 +        method = new LbStickinessMethod(StickinessMethodType.LBCookieBased, "This is loadbalancer cookie based stickiness method.");
 +        method.addParam("cookie-name", false, "Cookie name passed in http header by the LB to the client.", false);
 +        method.addParam("mode", false, "Valid values: insert, rewrite, prefix. Default value: insert.  In the insert mode cookie will be created"
 +                + " by the LB. In other modes, cookie will be created by the server and LB modifies it.", false);
 +        method.addParam("nocache", false, "This option is recommended in conjunction with the insert mode when there is a cache between the client"
 +                + " and HAProxy, as it ensures that a cacheable response will be tagged non-cacheable if  a cookie needs "
 +                + "to be inserted. This is important because if all persistence cookies are added on a cacheable home page"
 +                + " for instance, then all customers will then fetch the page from an outer cache and will all share the "
 +                + "same persistence cookie, leading to one server receiving much more traffic than others. See also the " + "insert and postonly options. ", true);
 +        method.addParam("indirect", false, "When this option is specified in insert mode, cookies will only be added when the server was not reached"
 +                + " after a direct access, which means that only when a server is elected after applying a load-balancing algorithm,"
 +                + " or after a redispatch, then the cookie  will be inserted. If the client has all the required information"
 +                + " to connect to the same server next time, no further cookie will be inserted. In all cases, when the "
 +                + "indirect option is used in insert mode, the cookie is always removed from the requests transmitted to "
 +                + "the server. The persistence mechanism then becomes totally transparent from the application point of view.", true);
 +        method.addParam("postonly", false, "This option ensures that cookie insertion will only be performed on responses to POST requests. It is an"
 +                + " alternative to the nocache option, because POST responses are not cacheable, so this ensures that the "
 +                + "persistence cookie will never get cached.Since most sites do not need any sort of persistence before the"
 +                + " first POST which generally is a login request, this is a very efficient method to optimize caching "
 +                + "without risking to find a persistence cookie in the cache. See also the insert and nocache options.", true);
 +        method.addParam("domain", false, "This option allows to specify the domain at which a cookie is inserted. It requires exactly one parameter:"
 +                + " a valid domain name. If the domain begins with a dot, the browser is allowed to use it for any host "
 +                + "ending with that name. It is also possible to specify several domain names by invoking this option multiple"
 +                + " times. Some browsers might have small limits on the number of domains, so be careful when doing that. "
 +                + "For the record, sending 10 domains to MSIE 6 or Firefox 2 works as expected.", false);
 +        methodList.add(method);
 +
 +        method = new LbStickinessMethod(StickinessMethodType.AppCookieBased,
 +                "This is App session based sticky method. Define session stickiness on an existing application cookie. " + "It can be used only for a specific http traffic");
 +        method.addParam("cookie-name", false, "This is the name of the cookie used by the application and which LB will "
 +                + "have to learn for each new session. Default value: Auto geneared based on ip", false);
 +        method.addParam("length", false, "This is the max number of characters that will be memorized and checked in " + "each cookie value. Default value:52", false);
 +        method.addParam("holdtime", false, "This is the time after which the cookie will be removed from memory if unused. The value should be in "
 +                + "the format Example : 20s or 30m  or 4h or 5d . only seconds(s), minutes(m) hours(h) and days(d) are valid,"
 +                + " cannot use th combinations like 20h30m. Default value:3h ", false);
 +        method.addParam(
 +                "request-learn",
 +                false,
 +                "If this option is specified, then haproxy will be able to learn the cookie found in the request in case the server does not specify any in response. This is typically what happens with PHPSESSID cookies, or when haproxy's session expires before the application's session and the correct server is selected. It is recommended to specify this option to improve reliability",
 +                true);
 +        method.addParam(
 +                "prefix",
 +                false,
 +                "When this option is specified, haproxy will match on the cookie prefix (or URL parameter prefix). "
 +                        + "The appsession value is the data following this prefix. Example : appsession ASPSESSIONID len 64 timeout 3h prefix  This will match the cookie ASPSESSIONIDXXXX=XXXXX, the appsession value will be XXXX=XXXXX.",
 +                        true);
 +        method.addParam("mode", false, "This option allows to change the URL parser mode. 2 modes are currently supported : - path-parameters "
 +                + ": The parser looks for the appsession in the path parameters part (each parameter is separated by a semi-colon), "
 +                + "which is convenient for JSESSIONID for example.This is the default mode if the option is not set. - query-string :"
 +                + " In this mode, the parser will look for the appsession in the query string.", false);
 +        methodList.add(method);
 +
 +        method = new LbStickinessMethod(StickinessMethodType.SourceBased, "This is source based Stickiness method, " + "it can be used for any type of protocol.");
 +        method.addParam("tablesize", false, "Size of table to store source ip addresses. example: tablesize=200k or 300m" + " or 400g. Default value:200k", false);
 +        method.addParam("expire", false, "Entry in source ip table will expire after expire duration. units can be s,m,h,d ."
 +                + " example: expire=30m 20s 50h 4d. Default value:3h", false);
 +        methodList.add(method);
 +
 +        final Gson gson = new Gson();
 +        final String capability = gson.toJson(methodList);
 +        return capability;
 +    }
 +
 +    private static Map<Service, Map<Capability, String>> setCapabilities() {
 +        final Map<Service, Map<Capability, String>> capabilities = new HashMap<Service, Map<Capability, String>>();
 +
 +        // Set capabilities for LB service
 +        final Map<Capability, String> lbCapabilities = new HashMap<Capability, String>();
 +        lbCapabilities.put(Capability.SupportedLBAlgorithms, "roundrobin,leastconn,source");
 +        lbCapabilities.put(Capability.SupportedLBIsolation, "dedicated");
 +        lbCapabilities.put(Capability.SupportedProtocols, "tcp, udp, tcp-proxy");
 +        lbCapabilities.put(Capability.SupportedStickinessMethods, getHAProxyStickinessCapability());
 +        lbCapabilities.put(Capability.LbSchemes, LoadBalancerContainer.Scheme.Public.toString());
 +
 +        // specifies that LB rules can support autoscaling and the list of
 +        // counters it supports
 +        AutoScaleCounter counter;
 +        final List<AutoScaleCounter> counterList = new ArrayList<AutoScaleCounter>();
 +        counter = new AutoScaleCounter(AutoScaleCounterCpu);
 +        counterList.add(counter);
 +        counter = new AutoScaleCounter(AutoScaleCounterMemory);
 +        counterList.add(counter);
 +        final Gson gson = new Gson();
 +        final String autoScaleCounterList = gson.toJson(counterList);
 +        lbCapabilities.put(Capability.AutoScaleCounters, autoScaleCounterList);
 +        capabilities.put(Service.Lb, lbCapabilities);
 +
 +        // Set capabilities for Firewall service
 +        final Map<Capability, String> firewallCapabilities = new HashMap<Capability, String>();
 +        firewallCapabilities.put(Capability.TrafficStatistics, "per public ip");
 +        firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp");
 +        firewallCapabilities.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp, all");
 +        firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress, egress");
 +        firewallCapabilities.put(Capability.MultipleIps, "true");
 +        capabilities.put(Service.Firewall, firewallCapabilities);
 +
 +        // Set capabilities for vpn
 +        final Map<Capability, String> vpnCapabilities = new HashMap<Capability, String>();
 +        vpnCapabilities.put(Capability.SupportedVpnProtocols, "pptp,l2tp,ipsec");
 +        vpnCapabilities.put(Capability.VpnTypes, "removeaccessvpn");
 +        capabilities.put(Service.Vpn, vpnCapabilities);
 +
 +        final Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>();
 +        dnsCapabilities.put(Capability.AllowDnsSuffixModification, "true");
 +        capabilities.put(Service.Dns, dnsCapabilities);
 +
 +        capabilities.put(Service.UserData, null);
 +
 +        final Map<Capability, String> dhcpCapabilities = new HashMap<Capability, String>();
 +        dhcpCapabilities.put(Capability.DhcpAccrossMultipleSubnets, "true");
 +        capabilities.put(Service.Dhcp, dhcpCapabilities);
 +
 +        capabilities.put(Service.Gateway, null);
 +
 +        final Map<Capability, String> sourceNatCapabilities = new HashMap<Capability, String>();
 +        sourceNatCapabilities.put(Capability.SupportedSourceNatTypes, "peraccount");
 +        sourceNatCapabilities.put(Capability.RedundantRouter, "true");
 +        capabilities.put(Service.SourceNat, sourceNatCapabilities);
 +
 +        capabilities.put(Service.StaticNat, null);
 +
 +        final Map<Capability, String> portForwardingCapabilities = new HashMap<Capability, String>();
 +        portForwardingCapabilities.put(Capability.SupportedProtocols, NetUtils.TCP_PROTO + "," + NetUtils.UDP_PROTO);
 +        capabilities.put(Service.PortForwarding, portForwardingCapabilities);
 +
 +        return capabilities;
 +    }
 +
 +    @Override
 +    public boolean applyStaticNats(final Network network, final List<? extends StaticNat> rules) throws ResourceUnavailableException {
 +        boolean result = true;
 +        if (canHandle(network, Service.StaticNat)) {
 +            final List<DomainRouterVO> routers = getRouters(network);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need to apply static nat on the backend; virtual " + "router doesn't exist in the network " + network.getId());
 +                return true;
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.applyStaticNats(network, rules, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    public List<DomainRouterVO> getRouters(Network network){
 +        List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +        if (routers !=null && routers.isEmpty()) {
 +            return null;
 +        }
 +        NetworkDetailVO updateInSequence=_networkDetailsDao.findDetail(network.getId(), Network.updatingInSequence);
 +        if(network.isRedundant() && updateInSequence!=null && "true".equalsIgnoreCase(updateInSequence.getValue())){
 +            List<DomainRouterVO> masterRouters=new ArrayList<DomainRouterVO>();
 +            int noOfrouters=routers.size();
 +            while (noOfrouters>0){
 +                DomainRouterVO router = routers.get(0);
 +                if(router.getUpdateState()== VirtualRouter.UpdateState.UPDATE_IN_PROGRESS){
 +                    ArrayList<DomainRouterVO> routerList = new ArrayList<DomainRouterVO>();
 +                    routerList.add(router);
 +                    return routerList;
 +                }
 +                if(router.getUpdateState()== VirtualRouter.UpdateState.UPDATE_COMPLETE) {
 +                    routers.remove(router);
 +                    noOfrouters--;
 +                    continue;
 +                }
 +                if(router.getRedundantState()!=VirtualRouter.RedundantState.BACKUP) {
 +                    masterRouters.add(router);
 +                    routers.remove(router);
 +                }
 +                noOfrouters--;
 +            }
 +            if(routers.size()==0 && masterRouters.size()==0){
 +                return null;
 +            }
 +            if(routers.size()==0 && masterRouters.size()!=0){
 +                routers=masterRouters;
 +            }
 +            routers=routers.subList(0,1);
 +            routers.get(0).setUpdateState(VirtualRouter.UpdateState.UPDATE_IN_PROGRESS);
 +            _routerDao.persist(routers.get(0));
 +        }
 +        return routers;
 +    }
 +
 +    @Override
 +    public boolean shutdown(final Network network, final ReservationContext context, final boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final List<DomainRouterVO> routers = getRouters(network);
 +        if (routers == null || routers.isEmpty()) {
 +            return true;
 +        }
 +        boolean stopResult = true;
 +        boolean destroyResult = true;
 +        for (final DomainRouterVO router : routers) {
 +            stopResult = stopResult && _routerMgr.stop(router, false, context.getCaller(), context.getAccount()) != null;
 +            if (!stopResult) {
 +                s_logger.warn("Failed to stop virtual router element " + router + ", but would try to process clean up anyway.");
 +            }
 +            if (cleanup) {
 +                destroyResult = destroyResult && _routerMgr.destroyRouter(router.getId(), context.getAccount(), context.getCaller().getId()) != null;
 +                if (!destroyResult) {
 +                    s_logger.warn("Failed to clean up virtual router element " + router);
 +                }
 +            }
 +        }
 +        return stopResult & destroyResult;
 +    }
 +
 +    @Override
 +    public boolean destroy(final Network config, final ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(config.getId(), Role.VIRTUAL_ROUTER);
 +        if (routers == null || routers.isEmpty()) {
 +            return true;
 +        }
 +        boolean result = true;
 +        // NOTE that we need to pass caller account to destroyRouter, otherwise
 +        // it will fail permission check there. Context passed in from
 +        // deleteNetwork is the network account,
 +        // not caller account
 +        final Account callerAccount = _accountMgr.getAccount(context.getCaller().getAccountId());
 +        for (final DomainRouterVO router : routers) {
 +            result = result && _routerMgr.destroyRouter(router.getId(), callerAccount, context.getCaller().getId()) != null;
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean savePassword(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException {
 +        if (!canHandle(network, null)) {
 +            return false;
 +        }
 +        final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +        if (routers == null || routers.isEmpty()) {
 +            s_logger.debug("Can't find virtual router element in network " + network.getId());
 +            return true;
 +        }
 +
 +        final VirtualMachineProfile uservm = vm;
 +
 +        final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +        final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +        // If any router is running then send save password command otherwise
 +        // save the password in DB
 +        for (final VirtualRouter router : routers) {
 +            if (router.getState() == State.Running) {
 +                final boolean result = networkTopology.savePasswordToRouter(network, nic, uservm, router);
 +                if (result) {
 +                    // Explicit password reset, while VM hasn't generated a password yet.
 +                    final UserVmVO userVmVO = _userVmDao.findById(vm.getId());
 +                    userVmVO.setUpdateParameters(false);
 +                    _userVmDao.update(userVmVO.getId(), userVmVO);
 +                }
 +                return result;
 +            }
 +        }
 +        final String password = (String) uservm.getParameter(VirtualMachineProfile.Param.VmPassword);
 +        final String password_encrypted = DBEncryptionUtil.encrypt(password);
 +        final UserVmVO userVmVO = _userVmDao.findById(vm.getId());
 +
 +        _userVmDao.loadDetails(userVmVO);
 +        userVmVO.setDetail("password", password_encrypted);
 +        _userVmDao.saveDetails(userVmVO);
 +
 +        userVmVO.setUpdateParameters(true);
 +        _userVmDao.update(userVmVO.getId(), userVmVO);
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean saveSSHKey(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final String sshPublicKey) throws ResourceUnavailableException {
 +        if (!canHandle(network, null)) {
 +            return false;
 +        }
 +        final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +        if (routers == null || routers.isEmpty()) {
 +            s_logger.debug("Can't find virtual router element in network " + network.getId());
 +            return true;
 +        }
 +
 +        final VirtualMachineProfile uservm = vm;
 +
 +        final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +        final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +        boolean result = true;
 +        for (final DomainRouterVO domainRouterVO : routers) {
 +            result = result && networkTopology.saveSSHPublicKeyToRouter(network, nic, uservm, domainRouterVO, sshPublicKey);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException {
 +        if (!canHandle(network, null)) {
 +            return false;
 +        }
 +        final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +        if (routers == null || routers.isEmpty()) {
 +            s_logger.debug("Can't find virtual router element in network " + network.getId());
 +            return true;
 +        }
 +
 +        final VirtualMachineProfile uservm = vm;
 +
 +        final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +        final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +        boolean result = true;
 +        for (final DomainRouterVO domainRouterVO : routers) {
 +            result = result && networkTopology.saveUserDataToRouter(network, nic, uservm, domainRouterVO);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public List<Class<?>> getCommands() {
 +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
 +        cmdList.add(CreateVirtualRouterElementCmd.class);
 +        cmdList.add(ConfigureVirtualRouterElementCmd.class);
 +        cmdList.add(ListVirtualRouterElementsCmd.class);
 +        return cmdList;
 +    }
 +
 +    @Override
 +    public VirtualRouterProvider configure(final ConfigureVirtualRouterElementCmd cmd) {
 +        final VirtualRouterProviderVO element = _vrProviderDao.findById(cmd.getId());
 +        if (element == null || !(element.getType() == Type.VirtualRouter || element.getType() == Type.VPCVirtualRouter)) {
 +            s_logger.debug("Can't find Virtual Router element with network service provider id " + cmd.getId());
 +            return null;
 +        }
 +
 +        element.setEnabled(cmd.getEnabled());
 +        _vrProviderDao.persist(element);
 +
 +        return element;
 +    }
 +
 +    @Override
 +    public OvsProvider configure(final ConfigureOvsElementCmd cmd) {
 +        final OvsProviderVO element = _ovsProviderDao.findById(cmd.getId());
 +        if (element == null) {
 +            s_logger.debug("Can't find Ovs element with network service provider id " + cmd.getId());
 +            return null;
 +        }
 +
 +        element.setEnabled(cmd.getEnabled());
 +        _ovsProviderDao.persist(element);
 +
 +        return element;
 +    }
 +
 +    @Override
 +    public VirtualRouterProvider addElement(final Long nspId, final Type providerType) {
 +        if (!(providerType == Type.VirtualRouter || providerType == Type.VPCVirtualRouter)) {
 +            throw new InvalidParameterValueException("Element " + getName() + " supports only providerTypes: " + Type.VirtualRouter.toString() + " and " + Type.VPCVirtualRouter);
 +        }
 +        VirtualRouterProviderVO element = _vrProviderDao.findByNspIdAndType(nspId, providerType);
 +        if (element != null) {
 +            s_logger.debug("There is already a virtual router element with service provider id " + nspId);
 +            return null;
 +        }
 +        element = new VirtualRouterProviderVO(nspId, providerType);
 +        _vrProviderDao.persist(element);
 +        return element;
 +    }
 +
 +    @Override
 +    public boolean applyPFRules(final Network network, final List<PortForwardingRule> rules) throws ResourceUnavailableException {
 +        boolean result = true;
 +        if (canHandle(network, Service.PortForwarding)) {
 +            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            if (routers == null || routers.isEmpty()) {
 +                s_logger.debug("Virtual router elemnt doesn't need to apply firewall rules on the backend; virtual " + "router doesn't exist in the network " + network.getId());
 +                return true;
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.applyFirewallRules(network, rules, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean isReady(final PhysicalNetworkServiceProvider provider) {
 +        final VirtualRouterProviderVO element = _vrProviderDao.findByNspIdAndType(provider.getId(), getVirtualRouterProvider());
 +        if (element == null) {
 +            return false;
 +        }
 +        return element.isEnabled();
 +    }
 +
 +    @Override
 +    public boolean shutdownProviderInstances(final PhysicalNetworkServiceProvider provider, final ReservationContext context) throws ConcurrentOperationException,
 +    ResourceUnavailableException {
 +        final VirtualRouterProviderVO element = _vrProviderDao.findByNspIdAndType(provider.getId(), getVirtualRouterProvider());
 +        if (element == null) {
 +            return true;
 +        }
 +        // Find domain routers
 +        final long elementId = element.getId();
 +        final List<DomainRouterVO> routers = _routerDao.listByElementId(elementId);
 +        boolean result = true;
 +        for (final DomainRouterVO router : routers) {
 +            result = result && _routerMgr.destroyRouter(router.getId(), context.getAccount(), context.getCaller().getId()) != null;
 +        }
 +        _vrProviderDao.remove(elementId);
 +
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean canEnableIndividualServices() {
 +        return true;
 +    }
 +
 +    public Long getIdByNspId(final Long nspId) {
 +        final VirtualRouterProviderVO vr = _vrProviderDao.findByNspIdAndType(nspId, Type.VirtualRouter);
 +        return vr.getId();
 +    }
 +
 +    @Override
 +    public VirtualRouterProvider getCreatedElement(final long id) {
 +        final VirtualRouterProvider provider = _vrProviderDao.findById(id);
 +        if (!(provider.getType() == Type.VirtualRouter || provider.getType() == Type.VPCVirtualRouter)) {
 +            throw new InvalidParameterValueException("Unable to find provider by id");
 +        }
 +        return provider;
 +    }
 +
 +    @Override
 +    public boolean release(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final ReservationContext context) throws ConcurrentOperationException,
 +    ResourceUnavailableException {
 +        return true;
 +    }
 +
 +
 +    @Override
 +    public boolean configDhcpSupportForSubnet(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final DeployDestination dest,
 +                                              final ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +            return configureDhcpSupport(network, nic, vm, dest, Service.Dhcp);
 +    }
 +
 +    @Override
 +    public boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +        // Ignore if virtual router is already dhcp provider
 +        if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, getProvider())) {
 +            return true;
 +        }
 +        return configureDhcpSupport(network, nic, vm, dest, Service.Dns);
 +    }
 +
 +    protected boolean configureDhcpSupport(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, Service service) throws ResourceUnavailableException {
 +        if (canHandle(network, service)) {
 +            if (vm.getType() != VirtualMachine.Type.User) {
 +                return false;
 +            }
 +
 +            final VirtualMachineProfile uservm = vm;
 +
 +            final List<DomainRouterVO> routers = getRouters(network, dest);
 +
 +            if (routers == null || routers.size() == 0) {
 +                throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            return networkTopology.configDhcpForSubnet(network, nic, uservm, dest, routers);
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean removeDhcpSupportForSubnet(final Network network) throws ResourceUnavailableException {
 +        return removeDhcpSupportForSubnet(network, Service.Dhcp);
 +    }
 +
 +    @Override
 +    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
 +        return false;
 +    }
 +
 +    @Override
++    public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) throws ResourceUnavailableException {
++        boolean result = true;
++        if (canHandle(network, Service.Dhcp)) {
++            if (vmProfile.getType() != VirtualMachine.Type.User) {
++                return false;
++            }
++
++            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER);
++
++            if (CollectionUtils.isEmpty(routers)) {
++                throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
++            }
++
++            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
++            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
++
++            for (final DomainRouterVO domainRouterVO : routers) {
++                if (domainRouterVO.getState() != VirtualMachine.State.Running) {
++                    continue;
++                }
++
++                result = result && networkTopology.removeDhcpEntry(network, nic, vmProfile, domainRouterVO);
++            }
++        }
++        return result;
++    }
++
++    @Override
 +    public boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException {
 +        // Ignore if virtual router is already dhcp provider
 +        if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, getProvider())) {
 +            return true;
 +        } else {
 +            return removeDhcpSupportForSubnet(network, Service.Dns);
 +        }
 +    }
 +
 +    protected boolean removeDhcpSupportForSubnet(Network network, Network.Service service) throws ResourceUnavailableException {
 +        if (canHandle(network, service)) {
 +            final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +
 +            if (CollectionUtils.isEmpty(routers)) {
 +                throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
 +            }
 +            try {
 +                return _routerMgr.removeDhcpSupportForSubnet(network, routers);
 +            } catch (final ResourceUnavailableException e) {
 +                s_logger.info("Router resource unavailable ", e);
 +            }
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean addDhcpEntry(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final DeployDestination dest, final ReservationContext context)
 +            throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +        return applyDhcpEntries(network, nic, vm, dest, Service.Dhcp);
 +    }
 +
 +    @Override
 +    public boolean addDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +        // Ignore if virtual router is already dhcp provider
 +        if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, getProvider())) {
 +            return true;
 +        }
 +
 +        return applyDhcpEntries(network, nic, vm, dest, Service.Dns);
 +    }
 +
 +    protected boolean applyDhcpEntries (final Network network, final NicProfile nic, final VirtualMachineProfile vm, final DeployDestination dest, final Network.Service service) throws ResourceUnavailableException {
 +
 +        boolean result = true;
 +        if (canHandle(network, service)) {
 +            if (vm.getType() != VirtualMachine.Type.User) {
 +                return false;
 +            }
 +
 +            final VirtualMachineProfile uservm = vm;
 +            final List<DomainRouterVO> routers = getRouters(network, dest);
 +
 +            if (routers == null || routers.size() == 0) {
 +                throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.applyDhcpEntry(network, nic, uservm, dest, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean addPasswordAndUserdata(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final DeployDestination dest,
 +            final ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
 +        boolean result = true;
 +        if (canHandle(network, Service.UserData)) {
 +            if (vm.getType() != VirtualMachine.Type.User) {
 +                return false;
 +            }
 +
 +            final VirtualMachineProfile uservm = vm;
 +
 +            final List<DomainRouterVO> routers = getRouters(network, dest);
 +
 +            if (routers == null || routers.size() == 0) {
 +                throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
 +            }
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            for (final DomainRouterVO domainRouterVO : routers) {
 +                result = result && networkTopology.applyUserData(network, nic, uservm, dest, domainRouterVO);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    protected List<DomainRouterVO> getRouters(final Network network, final DeployDestination dest) {
 +        boolean publicNetwork = false;
 +        if (_networkMdl.isProviderSupportServiceInNetwork(network.getId(), Service.SourceNat, getProvider())) {
 +            publicNetwork = true;
 +        }
 +        final boolean isPodBased = (dest.getDataCenter().getNetworkType() == NetworkType.Basic || _networkMdl.isSecurityGroupSupportedInNetwork(network))
 +                && network.getTrafficType() == TrafficType.Guest;
 +
 +        List<DomainRouterVO> routers;
 +
 +        if (publicNetwork) {
 +            routers = getRouters(network);
 +        } else {
 +            if (isPodBased && dest.getPod() != null) {
 +                final Long podId = dest.getPod().getId();
 +                routers = _routerDao.listByNetworkAndPodAndRole(network.getId(), podId, Role.VIRTUAL_ROUTER);
 +            } else {
 +                // With pod == null, it's network restart case, we would add all
 +                // router to it
 +                // Ignore DnsBasicZoneUpdate() parameter here
 +                routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
 +            }
 +        }
 +
 +        // for Basic zone, add all Running routers - we have to send
 +        // Dhcp/vmData/password info to them when
 +        // network.dns.basiczone.updates is set to "all"
 +        // With pod == null, it's network restart case, we already add all
 +        // routers to it
 +        if (isPodBased && dest.getPod() != null && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) {
 +            final Long podId = dest.getPod().getId();
 +            final List<DomainRouterVO> allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running, Role.VIRTUAL_ROUTER);
 +            routers.addAll(allRunningRoutersOutsideThePod);
 +        }
 +        return routers;
 +    }
 +
 +    @Override
 +    public List<? extends VirtualRouterProvider> searchForVirtualRouterElement(final ListVirtualRouterElementsCmd cmd) {
 +        final Long id = cmd.getId();
 +        final Long nspId = cmd.getNspId();
 +        final Boolean enabled = cmd.getEnabled();
 +
 +        final QueryBuilder<VirtualRouterProviderVO> sc = QueryBuilder.create(VirtualRouterProviderVO.class);
 +        if (id != null) {
 +            sc.and(sc.entity().getId(), Op.EQ, id);
 +        }
 +        if (nspId != null) {
 +            sc.and(sc.entity().getNspId(), Op.EQ, nspId);
 +        }
 +        if (enabled != null) {
 +            sc.and(sc.entity().isEnabled(), Op.EQ, enabled);
 +        }
 +
 +        // return only VR and VPC VR
 +        sc.and(sc.entity().getType(), Op.IN, VirtualRouterProvider.Type.VPCVirtualRouter, VirtualRouterProvider.Type.VirtualRouter);
 +
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<? extends OvsProvider> searchForOvsElement(final ListOvsElementsCmd cmd) {
 +        final Long id = cmd.getId();
 +        final Long nspId = cmd.getNspId();
 +        final Boolean enabled = cmd.getEnabled();
 +        final QueryBuilder<OvsProviderVO> sc = QueryBuilder.create(OvsProviderVO.class);
 +
 +        if (id != null) {
 +            sc.and(sc.entity().getId(), Op.EQ, id);
 +        }
 +        if (nspId != null) {
 +            sc.and(sc.entity().getNspId(), Op.EQ, nspId);
 +        }
 +        if (enabled != null) {
 +            sc.and(sc.entity().isEnabled(), Op.EQ, enabled);
 +        }
 +
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public boolean verifyServicesCombination(final Set<Service> services) {
 +        return true;
 +    }
 +
 +    @Override
 +    public IpDeployer getIpDeployer(final Network network) {
 +        return this;
 +    }
 +
 +    protected VirtualRouterProvider.Type getVirtualRouterProvider() {
 +        return VirtualRouterProvider.Type.VirtualRouter;
 +    }
 +
 +    @Override
 +    public List<LoadBalancerTO> updateHealthChecks(final Network network, final List<LoadBalancingRule> lbrules) {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +    @Override
 +    public boolean handlesOnlyRulesInTransitionState() {
 +        return true;
 +    }
 +
 +    private boolean canHandleLbRules(final List<LoadBalancingRule> rules) {
 +        final Map<Capability, String> lbCaps = getCapabilities().get(Service.Lb);
 +        if (!lbCaps.isEmpty()) {
 +            final String schemeCaps = lbCaps.get(Capability.LbSchemes);
 +            if (schemeCaps != null) {
 +                for (final LoadBalancingRule rule : rules) {
 +                    if (!schemeCaps.contains(rule.getScheme().toString())) {
 +                        s_logger.debug("Scheme " + rules.get(0).getScheme() + " is not supported by the provider " + getName());
 +                        return false;
 +                    }
 +                }
 +            }
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean prepareMigration(final NicProfile nic, final Network network, final VirtualMachineProfile vm, final DeployDestination dest, final ReservationContext context) {
 +        if (nic.getBroadcastType() != Networks.BroadcastDomainType.Pvlan) {
 +            return true;
 +        }
 +        if (vm.getType() == VirtualMachine.Type.DomainRouter) {
 +            assert vm instanceof DomainRouterVO;
 +            final DomainRouterVO router = (DomainRouterVO) vm.getVirtualMachine();
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            try {
 +                networkTopology.setupDhcpForPvlan(false, router, router.getHostId(), nic);
 +            } catch (final ResourceUnavailableException e) {
 +                s_logger.warn("Timed Out", e);
 +            }
 +        } else if (vm.getType() == VirtualMachine.Type.User) {
 +            assert vm instanceof UserVmVO;
 +            final UserVmVO userVm = (UserVmVO) vm.getVirtualMachine();
 +            _userVmMgr.setupVmForPvlan(false, userVm.getHostId(), nic);
 +        }
 +        return true;
 +    }
 +
 +    @Override
 +    public void rollbackMigration(final NicProfile nic, final Network network, final VirtualMachineProfile vm, final ReservationContext src, final ReservationContext dst) {
 +        if (nic.getBroadcastType() != Networks.BroadcastDomainType.Pvlan) {
 +            return;
 +        }
 +        if (vm.getType() == VirtualMachine.Type.DomainRouter) {
 +            assert vm instanceof DomainRouterVO;
 +            final DomainRouterVO router = (DomainRouterVO) vm.getVirtualMachine();
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            try {
 +                networkTopology.setupDhcpForPvlan(true, router, router.getHostId(), nic);
 +            } catch (final ResourceUnavailableException e) {
 +                s_logger.warn("Timed Out", e);
 +            }
 +        } else if (vm.getType() == VirtualMachine.Type.User) {
 +            assert vm instanceof UserVmVO;
 +            final UserVmVO userVm = (UserVmVO) vm.getVirtualMachine();
 +            _userVmMgr.setupVmForPvlan(true, userVm.getHostId(), nic);
 +        }
 +    }
 +
 +    @Override
 +    public void commitMigration(final NicProfile nic, final Network network, final VirtualMachineProfile vm, final ReservationContext src, final ReservationContext dst) {
 +        if (nic.getBroadcastType() != Networks.BroadcastDomainType.Pvlan) {
 +            return;
 +        }
 +        if (vm.getType() == VirtualMachine.Type.DomainRouter) {
 +            assert vm instanceof DomainRouterVO;
 +            final DomainRouterVO router = (DomainRouterVO) vm.getVirtualMachine();
 +
 +            final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
 +            final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO);
 +
 +            try {
 +                networkTopology.setupDhcpForPvlan(true, router, router.getHostId(), nic);
 +            } catch (final ResourceUnavailableException e) {
 +                s_logger.warn("Timed Out", e);
 +            }
 +        } else if (vm.getType() == VirtualMachine.Type.User) {
 +            assert vm instanceof UserVmVO;
 +            final UserVmVO userVm = (UserVmVO) vm.getVirtualMachine();
 +            _userVmMgr.setupVmForPvlan(true, userVm.getHostId(), nic);
 +        }
 +    }
 +
 +    @Override
 +    public boolean prepareAggregatedExecution(final Network network, final DeployDestination dest) throws ResourceUnavailableException {
 +        final List<DomainRouterVO> routers = getRouters(network, dest);
 +
 +        if (routers == null || routers.size() == 0) {
 +            throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
 +        }
 +
 +        return _routerMgr.prepareAggregatedExecution(network, routers);
 +    }
 +
 +    @Override
 +    public boolean completeAggregatedExecution(final Network network, final DeployDestination dest) throws ResourceUnavailableException {
 +        final List<DomainRouterVO> routers = getRouters(network, dest);
 +
 +        if (routers == null || routers.size() == 0) {
 +            throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId());
 +        }
 +
 +        NetworkDetailVO networkDetail=_networkDetailsDao.findDetail(network.getId(), Network.updatingInSequence);
 +        boolean updateInSequence= "true".equalsIgnoreCase((networkDetail!=null ? networkDetail.getValue() : null));
 +        if(updateInSequence){
 +            DomainRouterVO router=routers.get(0);
 +            router.setUpdateState(VirtualRouter.UpdateState.UPDATE_COMPLETE);
 +            _routerDao.persist(router);
 +        }
 +        boolean result=false;
 +        try{
 +            result=_routerMgr.completeAggregatedExecution(network, routers);
 +        } finally {
 +            if(!result && updateInSequence) {
 +                //fail the network update. even if one router fails we fail the network update.
 +                updateToFailedState(network);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public boolean cleanupAggregatedExecution(final Network network, final DeployDestination dest) throws ResourceUnavailableException {
 +        // The VR code already cleansup in the Finish routine using finally,
 +        // lets not waste another command
 +        return true;
 +    }
 +
 +    @Override
 +    public void configureResource(Network network) {
 +        NetworkDetailVO networkDetail=_networkDetailsDao.findDetail(network.getId(), Network.updatingInSequence);
 +        if(networkDetail==null || !"true".equalsIgnoreCase(networkDetail.getValue()))
 +            throw new CloudRuntimeException("failed to configure the resource, network update is not in progress.");
 +        List<DomainRouterVO>routers = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER);
 +        for(DomainRouterVO router : routers){
 +            router.setUpdateState(VirtualRouter.UpdateState.UPDATE_NEEDED);
 +            _routerDao.persist(router);
 +        }
 +    }
 +
 +    @Override
 +    public int getResourceCount(Network network) {
 +        return _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER).size();
 +    }
 +
 +    @Override
 +    public void finalize(Network network, boolean success) {
 +        if(!success){
 +            updateToFailedState(network);
 +        }
 +    }
 +
 +    private void updateToFailedState(Network network){
 +        //fail the network update. even if one router fails we fail the network update.
 +        List<DomainRouterVO> routerList = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER);
 +        for (DomainRouterVO router : routerList) {
 +            router.setUpdateState(VirtualRouter.UpdateState.UPDATE_FAILED);
 +            _routerDao.persist(router);
 +        }
 +    }
 +
 +}
diff --cc server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
index 829e07d,0000000..9ea2b98
mode 100644,000000..100644
--- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
+++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
@@@ -1,1103 -1,0 +1,1104 @@@
 +// 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.router;
 +
 +import java.net.URI;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.Comparator;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +import javax.inject.Inject;
 +
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.log4j.Logger;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.beans.factory.annotation.Qualifier;
 +
 +import com.cloud.agent.api.SetupGuestNetworkCommand;
 +import com.cloud.agent.api.routing.CreateIpAliasCommand;
 +import com.cloud.agent.api.routing.DeleteIpAliasCommand;
 +import com.cloud.agent.api.routing.DhcpEntryCommand;
 +import com.cloud.agent.api.routing.DnsMasqConfigCommand;
 +import com.cloud.agent.api.routing.IpAliasTO;
 +import com.cloud.agent.api.routing.IpAssocCommand;
 +import com.cloud.agent.api.routing.IpAssocVpcCommand;
 +import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
 +import com.cloud.agent.api.routing.NetworkElementCommand;
 +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand;
 +import com.cloud.agent.api.routing.SavePasswordCommand;
 +import com.cloud.agent.api.routing.SetFirewallRulesCommand;
 +import com.cloud.agent.api.routing.SetNetworkACLCommand;
 +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand;
 +import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand;
 +import com.cloud.agent.api.routing.SetSourceNatCommand;
 +import com.cloud.agent.api.routing.SetStaticNatRulesCommand;
 +import com.cloud.agent.api.routing.SetStaticRouteCommand;
 +import com.cloud.agent.api.routing.Site2SiteVpnCfgCommand;
 +import com.cloud.agent.api.routing.VmDataCommand;
 +import com.cloud.agent.api.routing.VpnUsersCfgCommand;
 +import com.cloud.agent.api.to.DhcpTO;
 +import com.cloud.agent.api.to.FirewallRuleTO;
 +import com.cloud.agent.api.to.IpAddressTO;
 +import com.cloud.agent.api.to.LoadBalancerTO;
 +import com.cloud.agent.api.to.NetworkACLTO;
 +import com.cloud.agent.api.to.NicTO;
 +import com.cloud.agent.api.to.PortForwardingRuleTO;
 +import com.cloud.agent.api.to.StaticNatRuleTO;
 +import com.cloud.agent.manager.Commands;
 +import com.cloud.configuration.Config;
 +import com.cloud.dc.DataCenter;
 +import com.cloud.dc.DataCenter.NetworkType;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.dc.dao.VlanDao;
 +import com.cloud.network.IpAddress;
 +import com.cloud.network.Network;
 +import com.cloud.network.Network.Provider;
 +import com.cloud.network.Network.Service;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.Networks.BroadcastDomainType;
 +import com.cloud.network.Networks.TrafficType;
 +import com.cloud.network.PublicIpAddress;
 +import com.cloud.network.RemoteAccessVpn;
 +import com.cloud.network.Site2SiteVpnConnection;
 +import com.cloud.network.VpnUser;
 +import com.cloud.network.VpnUserVO;
 +import com.cloud.network.dao.FirewallRulesDao;
 +import com.cloud.network.dao.IPAddressDao;
 +import com.cloud.network.dao.NetworkDao;
 +import com.cloud.network.dao.NetworkVO;
 +import com.cloud.network.dao.IPAddressVO;
 +import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
 +import com.cloud.network.dao.Site2SiteCustomerGatewayVO;
 +import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 +import com.cloud.network.dao.Site2SiteVpnGatewayVO;
 +import com.cloud.network.dao.VpnUserDao;
 +import com.cloud.network.lb.LoadBalancingRule;
 +import com.cloud.network.lb.LoadBalancingRule.LbDestination;
 +import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy;
 +import com.cloud.network.rules.FirewallRule;
 +import com.cloud.network.rules.FirewallRule.Purpose;
 +import com.cloud.network.rules.FirewallRuleVO;
 +import com.cloud.network.rules.PortForwardingRule;
 +import com.cloud.network.rules.StaticNat;
 +import com.cloud.network.rules.StaticNatRule;
 +import com.cloud.network.vpc.NetworkACLItem;
 +import com.cloud.network.vpc.PrivateIpAddress;
 +import com.cloud.network.vpc.StaticRouteProfile;
 +import com.cloud.network.vpc.Vpc;
 +import com.cloud.network.vpc.VpcGateway;
 +import com.cloud.network.vpc.dao.VpcDao;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offerings.NetworkOfferingVO;
 +import com.cloud.offerings.dao.NetworkOfferingDao;
 +import com.cloud.service.dao.ServiceOfferingDao;
 +import com.cloud.user.Account;
 +import com.cloud.uservm.UserVm;
 +import com.cloud.utils.Pair;
 +import com.cloud.utils.StringUtils;
 +import com.cloud.utils.db.EntityManager;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.vm.DomainRouterVO;
 +import com.cloud.vm.Nic;
 +import com.cloud.vm.NicIpAlias;
 +import com.cloud.vm.NicProfile;
 +import com.cloud.vm.NicVO;
 +import com.cloud.vm.UserVmVO;
 +import com.cloud.vm.VirtualMachine;
 +import com.cloud.vm.VirtualMachineManager;
 +import com.cloud.vm.VirtualMachineProfile;
 +import com.cloud.vm.dao.DomainRouterDao;
 +import com.cloud.vm.dao.NicDao;
 +import com.cloud.vm.dao.NicIpAliasDao;
 +import com.cloud.vm.dao.NicIpAliasVO;
 +import com.cloud.vm.dao.UserVmDao;
 +
 +public class CommandSetupHelper {
 +
 +    private static final Logger s_logger = Logger.getLogger(CommandSetupHelper.class);
 +
 +    @Inject
 +    private EntityManager _entityMgr;
 +
 +    @Inject
 +    private NicDao _nicDao;
 +    @Inject
 +    private NetworkDao _networkDao;
 +    @Inject
 +    private DomainRouterDao _routerDao;
 +    @Inject
 +    private NetworkModel _networkModel;
 +    @Inject
 +    private VirtualMachineManager _itMgr;
 +    @Inject
 +    private DataCenterDao _dcDao;
 +    @Inject
 +    private NicIpAliasDao _nicIpAliasDao;
 +    @Inject
 +    private FirewallRulesDao _rulesDao;
 +    @Inject
 +    private NetworkOfferingDao _networkOfferingDao;
 +    @Inject
 +    private ConfigurationDao _configDao;
 +    @Inject
 +    private ServiceOfferingDao _serviceOfferingDao;
 +    @Inject
 +    private UserVmDao _userVmDao;
 +    @Inject
 +    private VpnUserDao _vpnUsersDao;
 +    @Inject
 +    private Site2SiteCustomerGatewayDao _s2sCustomerGatewayDao;
 +    @Inject
 +    private Site2SiteVpnGatewayDao _s2sVpnGatewayDao;
 +    @Inject
 +    private VpcDao _vpcDao;
 +    @Inject
 +    private VlanDao _vlanDao;
 +    @Inject
 +    private IPAddressDao _ipAddressDao;
 +
 +    @Inject
 +    private RouterControlHelper _routerControlHelper;
 +
 +    @Autowired
 +    @Qualifier("networkHelper")
 +    protected NetworkHelper _networkHelper;
 +
 +    public void createVmDataCommand(final VirtualRouter router, final UserVm vm, final NicVO nic, final String publicKey, final Commands cmds) {
 +        final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText();
 +        final String zoneName = _dcDao.findById(router.getDataCenterId()).getName();
 +        cmds.addCommand(
 +                "vmdata",
 +                generateVmDataCommand(router, nic.getIPv4Address(), vm.getUserData(), serviceOffering, zoneName, nic.getIPv4Address(), vm.getHostName(), vm.getInstanceName(),
 +                        vm.getId(), vm.getUuid(), publicKey, nic.getNetworkId()));
 +    }
 +
 +    public void createApplyVpnUsersCommand(final List<? extends VpnUser> users, final VirtualRouter router, final Commands cmds) {
 +        final List<VpnUser> addUsers = new ArrayList<VpnUser>();
 +        final List<VpnUser> removeUsers = new ArrayList<VpnUser>();
 +        for (final VpnUser user : users) {
 +            if (user.getState() == VpnUser.State.Add || user.getState() == VpnUser.State.Active) {
 +                addUsers.add(user);
 +            } else if (user.getState() == VpnUser.State.Revoke) {
 +                removeUsers.add(user);
 +            }
 +        }
 +
 +        final VpnUsersCfgCommand cmd = new VpnUsersCfgCommand(addUsers, removeUsers);
 +        cmd.setAccessDetail(NetworkElementCommand.ACCOUNT_ID, String.valueOf(router.getAccountId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmds.addCommand("users", cmd);
 +    }
 +
-     public void createDhcpEntryCommand(final VirtualRouter router, final UserVm vm, final NicVO nic, final Commands cmds) {
++    public void createDhcpEntryCommand(final VirtualRouter router, final UserVm vm, final NicVO nic, boolean remove, final Commands cmds) {
 +        final DhcpEntryCommand dhcpCommand = new DhcpEntryCommand(nic.getMacAddress(), nic.getIPv4Address(), vm.getHostName(), nic.getIPv6Address(),
 +                _networkModel.getExecuteInSeqNtwkElmtCmd());
 +
 +        String gatewayIp = nic.getIPv4Gateway();
 +
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +
 +        dhcpCommand.setDefaultRouter(gatewayIp);
 +        dhcpCommand.setIp6Gateway(nic.getIPv6Gateway());
 +        String ipaddress = null;
 +        final NicVO domrDefaultNic = findDefaultDnsIp(vm.getId());
 +        if (domrDefaultNic != null) {
 +            ipaddress = domrDefaultNic.getIPv4Address();
 +        }
 +        dhcpCommand.setDefaultDns(ipaddress);
 +        dhcpCommand.setDuid(NetUtils.getDuidLL(nic.getMacAddress()));
 +        dhcpCommand.setDefault(nic.isDefaultNic());
++        dhcpCommand.setRemove(remove);
 +
 +        dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(nic.getNetworkId(), router.getId()));
 +        dhcpCommand.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmds.addCommand("dhcp", dhcpCommand);
 +    }
 +
 +    public void createIpAlias(final VirtualRouter router, final List<IpAliasTO> ipAliasTOs, final Long networkid, final Commands cmds) {
 +
 +        final String routerip = _routerControlHelper.getRouterIpInNetwork(networkid, router.getId());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        final CreateIpAliasCommand ipaliasCmd = new CreateIpAliasCommand(routerip, ipAliasTOs);
 +        ipaliasCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        ipaliasCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        ipaliasCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, routerip);
 +        ipaliasCmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmds.addCommand("ipalias", ipaliasCmd);
 +    }
 +
 +    public void configDnsMasq(final VirtualRouter router, final Network network, final Commands cmds) {
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        final List<NicIpAliasVO> ipAliasVOList = _nicIpAliasDao.listByNetworkIdAndState(network.getId(), NicIpAlias.State.active);
 +        final List<DhcpTO> ipList = new ArrayList<DhcpTO>();
 +
 +        final NicVO router_guest_nic = _nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId());
 +        final String cidr = NetUtils.getCidrFromGatewayAndNetmask(router_guest_nic.getIPv4Gateway(), router_guest_nic.getIPv4Netmask());
 +        final String[] cidrPair = cidr.split("\\/");
 +        final String cidrAddress = cidrPair[0];
 +        final long cidrSize = Long.parseLong(cidrPair[1]);
 +        final String startIpOfSubnet = NetUtils.getIpRangeStartIpFromCidr(cidrAddress, cidrSize);
 +
 +        ipList.add(new DhcpTO(router_guest_nic.getIPv4Address(), router_guest_nic.getIPv4Gateway(), router_guest_nic.getIPv4Netmask(), startIpOfSubnet));
 +        for (final NicIpAliasVO ipAliasVO : ipAliasVOList) {
 +            final DhcpTO DhcpTO = new DhcpTO(ipAliasVO.getIp4Address(), ipAliasVO.getGateway(), ipAliasVO.getNetmask(), ipAliasVO.getStartIpOfSubnet());
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("configDnsMasq : adding ip {" + DhcpTO.getGateway() + ", " + DhcpTO.getNetmask() + ", " + DhcpTO.getRouterIp() + ", " + DhcpTO.getStartIpOfSubnet()
 +                        + "}");
 +            }
 +            ipList.add(DhcpTO);
 +            ipAliasVO.setVmId(router.getId());
 +        }
 +        _dcDao.findById(router.getDataCenterId());
 +        final DnsMasqConfigCommand dnsMasqConfigCmd = new DnsMasqConfigCommand(ipList);
 +        dnsMasqConfigCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        dnsMasqConfigCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        dnsMasqConfigCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(network.getId(), router.getId()));
 +        dnsMasqConfigCmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand("dnsMasqConfig", dnsMasqConfigCmd);
 +    }
 +
 +    public void createApplyLoadBalancingRulesCommands(final List<LoadBalancingRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
 +        final LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()];
 +        int i = 0;
 +        // We don't support VR to be inline currently
 +        final boolean inline = false;
 +        for (final LoadBalancingRule rule : rules) {
 +            final boolean revoked = rule.getState().equals(FirewallRule.State.Revoke);
 +            final String protocol = rule.getProtocol();
 +            final String lb_protocol = rule.getLbProtocol();
 +            final String algorithm = rule.getAlgorithm();
 +            final String uuid = rule.getUuid();
 +
 +            final String srcIp = rule.getSourceIp().addr();
 +            final int srcPort = rule.getSourcePortStart();
 +            final List<LbDestination> destinations = rule.getDestinations();
 +            final List<LbStickinessPolicy> stickinessPolicies = rule.getStickinessPolicies();
 +            final LoadBalancerTO lb = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, inline, destinations, stickinessPolicies);
 +            lb.setLbProtocol(lb_protocol);
 +            lbs[i++] = lb;
 +        }
 +        String routerPublicIp = null;
 +
 +        if (router instanceof DomainRouterVO) {
 +            final DomainRouterVO domr = _routerDao.findById(router.getId());
 +            routerPublicIp = domr.getPublicIpAddress();
 +            if (routerPublicIp == null) {
 +                routerPublicIp = router.getPublicIpAddress();
 +            }
 +        }
 +
 +        final Network guestNetwork = _networkModel.getNetwork(guestNetworkId);
 +        final Nic nic = _nicDao.findByNtwkIdAndInstanceId(guestNetwork.getId(), router.getId());
 +        final NicProfile nicProfile = new NicProfile(nic, guestNetwork, nic.getBroadcastUri(), nic.getIsolationUri(), _networkModel.getNetworkRate(guestNetwork.getId(),
 +                router.getId()), _networkModel.isSecurityGroupSupportedInNetwork(guestNetwork), _networkModel.getNetworkTag(router.getHypervisorType(), guestNetwork));
 +        final NetworkOffering offering = _networkOfferingDao.findById(guestNetwork.getNetworkOfferingId());
 +        String maxconn = null;
 +        if (offering.getConcurrentConnections() == null) {
 +            maxconn = _configDao.getValue(Config.NetworkLBHaproxyMaxConn.key());
 +        } else {
 +            maxconn = offering.getConcurrentConnections().toString();
 +        }
 +
 +        final LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lbs, routerPublicIp, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()),
 +                router.getPrivateIpAddress(), _itMgr.toNicTO(nicProfile, router.getHypervisorType()), router.getVpcId(), maxconn, offering.isKeepAliveEnabled());
 +
 +        cmd.lbStatsVisibility = _configDao.getValue(Config.NetworkLBHaproxyStatsVisbility.key());
 +        cmd.lbStatsUri = _configDao.getValue(Config.NetworkLBHaproxyStatsUri.key());
 +        cmd.lbStatsAuth = _configDao.getValue(Config.NetworkLBHaproxyStatsAuth.key());
 +        cmd.lbStatsPort = _configDao.getValue(Config.NetworkLBHaproxyStatsPort.key());
 +
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createApplyPortForwardingRulesCommands(final List<? extends PortForwardingRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
 +        final List<PortForwardingRuleTO> rulesTO = new ArrayList<PortForwardingRuleTO>();
 +        if (rules != null) {
 +            for (final PortForwardingRule rule : rules) {
 +                final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
 +                final PortForwardingRuleTO ruleTO = new PortForwardingRuleTO(rule, null, sourceIp.getAddress().addr());
 +                rulesTO.add(ruleTO);
 +            }
 +        }
 +
 +        SetPortForwardingRulesCommand cmd = null;
 +
 +        if (router.getVpcId() != null) {
 +            cmd = new SetPortForwardingRulesVpcCommand(rulesTO);
 +        } else {
 +            cmd = new SetPortForwardingRulesCommand(rulesTO);
 +        }
 +
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createApplyStaticNatRulesCommands(final List<? extends StaticNatRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
 +        final List<StaticNatRuleTO> rulesTO = new ArrayList<StaticNatRuleTO>();
 +        if (rules != null) {
 +            for (final StaticNatRule rule : rules) {
 +                final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
 +                final StaticNatRuleTO ruleTO = new StaticNatRuleTO(rule, null, sourceIp.getAddress().addr(), rule.getDestIpAddress());
 +                rulesTO.add(ruleTO);
 +            }
 +        }
 +
 +        final SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO, router.getVpcId());
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createApplyFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
 +        final List<FirewallRuleTO> rulesTO = new ArrayList<FirewallRuleTO>();
 +        String systemRule = null;
 +        Boolean defaultEgressPolicy = false;
 +        if (rules != null) {
 +            if (rules.size() > 0) {
 +                if (rules.get(0).getTrafficType() == FirewallRule.TrafficType.Egress && rules.get(0).getType() == FirewallRule.FirewallRuleType.System) {
 +                    systemRule = String.valueOf(FirewallRule.FirewallRuleType.System);
 +                }
 +            }
 +            for (final FirewallRule rule : rules) {
 +                _rulesDao.loadSourceCidrs((FirewallRuleVO) rule);
 +                final FirewallRule.TrafficType traffictype = rule.getTrafficType();
 +                if (traffictype == FirewallRule.TrafficType.Ingress) {
 +                    final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
 +                    final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr(), Purpose.Firewall, traffictype);
 +                    rulesTO.add(ruleTO);
 +                } else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) {
 +                    final NetworkVO network = _networkDao.findById(guestNetworkId);
 +                    final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +                    defaultEgressPolicy = offering.isEgressDefaultPolicy();
 +                    assert rule.getSourceIpAddressId() == null : "ipAddressId should be null for egress firewall rule. ";
 +                    final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, "", Purpose.Firewall, traffictype, defaultEgressPolicy);
 +                    rulesTO.add(ruleTO);
 +                }
 +            }
 +        }
 +
 +        final SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        if (systemRule != null) {
 +            cmd.setAccessDetail(NetworkElementCommand.FIREWALL_EGRESS_DEFAULT, systemRule);
 +        } else {
 +            cmd.setAccessDetail(NetworkElementCommand.FIREWALL_EGRESS_DEFAULT, String.valueOf(defaultEgressPolicy));
 +        }
 +
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
 +        final List<FirewallRuleTO> rulesTO = new ArrayList<FirewallRuleTO>();
 +        String systemRule = null;
 +        Boolean defaultEgressPolicy = false;
 +        if (rules != null) {
 +            if (rules.size() > 0) {
 +                if (rules.get(0).getTrafficType() == FirewallRule.TrafficType.Egress && rules.get(0).getType() == FirewallRule.FirewallRuleType.System) {
 +                    systemRule = String.valueOf(FirewallRule.FirewallRuleType.System);
 +                }
 +            }
 +            for (final FirewallRule rule : rules) {
 +                _rulesDao.loadSourceCidrs((FirewallRuleVO) rule);
 +                _rulesDao.loadDestinationCidrs((FirewallRuleVO)rule);
 +                final FirewallRule.TrafficType traffictype = rule.getTrafficType();
 +                if (traffictype == FirewallRule.TrafficType.Ingress) {
 +                    final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
 +                    final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr(), Purpose.Firewall, traffictype);
 +                    rulesTO.add(ruleTO);
 +                } else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) {
 +                    final NetworkVO network = _networkDao.findById(guestNetworkId);
 +                    final NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
 +                    defaultEgressPolicy = offering.isEgressDefaultPolicy();
 +                    assert rule.getSourceIpAddressId() == null : "ipAddressId should be null for egress firewall rule. ";
 +                    final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, "", Purpose.Firewall, traffictype, defaultEgressPolicy);
 +                    rulesTO.add(ruleTO);
 +                }
 +            }
 +        }
 +
 +        final SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        if (systemRule != null) {
 +            cmd.setAccessDetail(NetworkElementCommand.FIREWALL_EGRESS_DEFAULT, systemRule);
 +        } else {
 +            cmd.setAccessDetail(NetworkElementCommand.FIREWALL_EGRESS_DEFAULT, String.valueOf(defaultEgressPolicy));
 +        }
 +
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createAssociateIPCommands(final VirtualRouter router, final List<? extends PublicIpAddress> ips, final Commands cmds, final long vmId) {
 +        final String ipAssocCommand = "IPAssocCommand";
 +        createRedundantAssociateIPCommands(router, ips, cmds, ipAssocCommand, false);
 +    }
 +
 +    public void createNetworkACLsCommands(final List<? extends NetworkACLItem> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId,
 +            final boolean privateGateway) {
 +        final List<NetworkACLTO> rulesTO = new ArrayList<NetworkACLTO>();
 +        String guestVlan = null;
 +        final Network guestNtwk = _networkDao.findById(guestNetworkId);
 +        final URI uri = guestNtwk.getBroadcastUri();
 +        if (uri != null) {
 +            guestVlan = BroadcastDomainType.getValue(uri);
 +        }
 +
 +        if (rules != null) {
 +            for (final NetworkACLItem rule : rules) {
 +                final NetworkACLTO ruleTO = new NetworkACLTO(rule, guestVlan, rule.getTrafficType());
 +                rulesTO.add(ruleTO);
 +            }
 +        }
 +
 +        NicTO nicTO = _networkHelper.getNicTO(router, guestNetworkId, null);
 +        final SetNetworkACLCommand cmd = new SetNetworkACLCommand(rulesTO, nicTO);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, guestVlan);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        if (privateGateway) {
 +            cmd.setAccessDetail(NetworkElementCommand.VPC_PRIVATE_GATEWAY, String.valueOf(VpcGateway.Type.Private));
 +        }
 +
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createPasswordCommand(final VirtualRouter router, final VirtualMachineProfile profile, final NicVO nic, final Commands cmds) {
 +        final String password = (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword);
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +
 +        // password should be set only on default network element
 +        if (password != null && nic.isDefaultNic()) {
 +            final SavePasswordCommand cmd = new SavePasswordCommand(password, nic.getIPv4Address(), profile.getVirtualMachine().getHostName(),
 +                    _networkModel.getExecuteInSeqNtwkElmtCmd());
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(nic.getNetworkId(), router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +            cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +            cmds.addCommand("password", cmd);
 +        }
 +
 +    }
 +
 +    public void createApplyStaticNatCommands(final List<? extends StaticNat> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
 +        final List<StaticNatRuleTO> rulesTO = new ArrayList<StaticNatRuleTO>();
 +        if (rules != null) {
 +            for (final StaticNat rule : rules) {
 +                final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
 +                final StaticNatRuleTO ruleTO = new StaticNatRuleTO(0, sourceIp.getAddress().addr(), null, null, rule.getDestIpAddress(), null, null, null, rule.isForRevoke(),
 +                        false);
 +                rulesTO.add(ruleTO);
 +            }
 +        }
 +
 +        final SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO, router.getVpcId());
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createStaticRouteCommands(final List<StaticRouteProfile> staticRoutes, final VirtualRouter router, final Commands cmds) {
 +        final SetStaticRouteCommand cmd = new SetStaticRouteCommand(staticRoutes);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createApplyVpnCommands(final boolean isCreate, final RemoteAccessVpn vpn, final VirtualRouter router, final Commands cmds) {
 +        final List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
 +
 +        createApplyVpnUsersCommand(vpnUsers, router, cmds);
 +
 +        final IpAddress ip = _networkModel.getIp(vpn.getServerAddressId());
 +
 +        // This block is needed due to the line 206 of the
 +        // RemoteAccessVpnManagenerImpl:
 +        // TODO: assumes one virtual network / domr per account per zone
 +        final String cidr;
 +        final Network network = _networkDao.findById(vpn.getNetworkId());
 +        if (network == null) {
 +            final Vpc vpc = _vpcDao.findById(vpn.getVpcId());
 +            cidr = vpc.getCidr();
 +        } else {
 +            cidr = network.getCidr();
 +        }
 +
 +        final RemoteAccessVpnCfgCommand startVpnCmd = new RemoteAccessVpnCfgCommand(isCreate, ip.getAddress().addr(), vpn.getLocalIp(), vpn.getIpRange(),
 +                vpn.getIpsecPresharedKey(), vpn.getVpcId() != null);
 +        startVpnCmd.setLocalCidr(cidr);
 +        startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        startVpnCmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmds.addCommand("startVpn", startVpnCmd);
 +    }
 +
 +    public void createVmDataCommandForVMs(final DomainRouterVO router, final Commands cmds, final long guestNetworkId) {
 +        final List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(guestNetworkId, VirtualMachine.State.Running, VirtualMachine.State.Migrating, VirtualMachine.State.Stopping);
 +        final DataCenterVO dc = _dcDao.findById(router.getDataCenterId());
 +        for (final UserVmVO vm : vms) {
 +            boolean createVmData = true;
 +            if (dc.getNetworkType() == NetworkType.Basic && router.getPodIdToDeployIn().longValue() != vm.getPodIdToDeployIn().longValue()) {
 +                createVmData = false;
 +            }
 +
 +            if (createVmData) {
 +                final NicVO nic = _nicDao.findByNtwkIdAndInstanceId(guestNetworkId, vm.getId());
 +                if (nic != null) {
 +                    s_logger.debug("Creating user data entry for vm " + vm + " on domR " + router);
 +
 +                    _userVmDao.loadDetails(vm);
 +                    createVmDataCommand(router, vm, nic, vm.getDetail("SSH.PublicKey"), cmds);
 +                }
 +            }
 +        }
 +    }
 +
 +    public void createDhcpEntryCommandsForVMs(final DomainRouterVO router, final Commands cmds, final long guestNetworkId) {
 +        final List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(guestNetworkId, VirtualMachine.State.Running, VirtualMachine.State.Migrating, VirtualMachine.State.Stopping);
 +        final DataCenterVO dc = _dcDao.findById(router.getDataCenterId());
 +        String dnsBasicZoneUpdates = _configDao.getValue(Config.DnsBasicZoneUpdates.key());
 +        for (final UserVmVO vm : vms) {
 +            if (dc.getNetworkType() == NetworkType.Basic && router.getPodIdToDeployIn().longValue() != vm.getPodIdToDeployIn().longValue()
 +                    && dnsBasicZoneUpdates.equalsIgnoreCase("pod")) {
 +                continue;
 +            }
 +
 +            final NicVO nic = _nicDao.findByNtwkIdAndInstanceId(guestNetworkId, vm.getId());
 +            if (nic != null) {
 +                s_logger.debug("Creating dhcp entry for vm " + vm + " on domR " + router + ".");
-                 createDhcpEntryCommand(router, vm, nic, cmds);
++                createDhcpEntryCommand(router, vm, nic, false, cmds);
 +            }
 +        }
 +    }
 +
 +    public void createDeleteIpAliasCommand(final DomainRouterVO router, final List<IpAliasTO> deleteIpAliasTOs, final List<IpAliasTO> createIpAliasTos, final long networkId,
 +            final Commands cmds) {
 +        final String routerip = _routerControlHelper.getRouterIpInNetwork(networkId, router.getId());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        final DeleteIpAliasCommand deleteIpaliasCmd = new DeleteIpAliasCommand(routerip, deleteIpAliasTOs, createIpAliasTos);
 +        deleteIpaliasCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        deleteIpaliasCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        deleteIpaliasCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, routerip);
 +        deleteIpaliasCmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmds.addCommand("deleteIpalias", deleteIpaliasCmd);
 +    }
 +
 +    public void createVpcAssociatePublicIPCommands(final VirtualRouter router, final List<? extends PublicIpAddress> ips, final Commands cmds,
 +            final Map<String, String> vlanMacAddress) {
 +
 +        final String ipAssocCommand = "IPAssocVpcCommand";
 +        if (router.getIsRedundantRouter()) {
 +            createRedundantAssociateIPCommands(router, ips, cmds, ipAssocCommand, true);
 +            return;
 +        }
 +
 +        Pair<IpAddressTO, Long> sourceNatIpAdd = null;
 +        Boolean addSourceNat = null;
 +        // Ensure that in multiple vlans case we first send all ip addresses of
 +        // vlan1, then all ip addresses of vlan2, etc..
 +        final Map<String, ArrayList<PublicIpAddress>> vlanIpMap = new HashMap<String, ArrayList<PublicIpAddress>>();
 +        for (final PublicIpAddress ipAddress : ips) {
 +            final String vlanTag = ipAddress.getVlanTag();
 +            ArrayList<PublicIpAddress> ipList = vlanIpMap.get(vlanTag);
 +            if (ipList == null) {
 +                ipList = new ArrayList<PublicIpAddress>();
 +            }
 +            // VR doesn't support release for sourceNat IP address; so reset the
 +            // state
 +            if (ipAddress.isSourceNat() && ipAddress.getState() == IpAddress.State.Releasing) {
 +                ipAddress.setState(IpAddress.State.Allocated);
 +            }
 +            ipList.add(ipAddress);
 +            vlanIpMap.put(vlanTag, ipList);
 +        }
 +
 +        for (final Map.Entry<String, ArrayList<PublicIpAddress>> vlanAndIp : vlanIpMap.entrySet()) {
 +            final List<PublicIpAddress> ipAddrList = vlanAndIp.getValue();
 +
 +            // Source nat ip address should always be sent first
 +            Collections.sort(ipAddrList, new Comparator<PublicIpAddress>() {
 +                @Override
 +                public int compare(final PublicIpAddress o1, final PublicIpAddress o2) {
 +                    final boolean s1 = o1.isSourceNat();
 +                    final boolean s2 = o2.isSourceNat();
 +                    return s1 ^ s2 ? s1 ^ true ? 1 : -1 : 0;
 +                }
 +            });
 +
 +
 +            // Get network rate - required for IpAssoc
 +            final Integer networkRate = _networkModel.getNetworkRate(ipAddrList.get(0).getNetworkId(), router.getId());
 +            final Network network = _networkModel.getNetwork(ipAddrList.get(0).getNetworkId());
 +
 +            final IpAddressTO[] ipsToSend = new IpAddressTO[ipAddrList.size()];
 +            int i = 0;
 +            boolean firstIP = true;
 +
 +            for (final PublicIpAddress ipAddr : ipAddrList) {
 +                final boolean add = ipAddr.getState() == IpAddress.State.Releasing ? false : true;
 +                boolean sourceNat = ipAddr.isSourceNat();
 +                /* enable sourceNAT for the first ip of the public interface
 +                * For additional public subnet source nat rule needs to be added for vm to reach ips in that subnet
 +                */
 +                if (firstIP) {
 +                    sourceNat = true;
 +                }
 +
 +                final String macAddress = vlanMacAddress.get(BroadcastDomainType.getValue(BroadcastDomainType.fromString(ipAddr.getVlanTag())));
 +
 +                final IpAddressTO ip = new IpAddressTO(ipAddr.getAccountId(), ipAddr.getAddress().addr(), add, firstIP, sourceNat, BroadcastDomainType.fromString(ipAddr.getVlanTag()).toString(), ipAddr.getGateway(),
 +                        ipAddr.getNetmask(), macAddress, networkRate, ipAddr.isOneToOneNat());
 +
 +                ip.setTrafficType(network.getTrafficType());
 +                ip.setNetworkName(_networkModel.getNetworkTag(router.getHypervisorType(), network));
 +                ipsToSend[i++] = ip;
 +                if (ipAddr.isSourceNat()) {
 +                    sourceNatIpAdd = new Pair<IpAddressTO, Long>(ip, ipAddr.getNetworkId());
 +                    addSourceNat = add;
 +                }
 +
 +                //for additional public subnet on delete it is not sure which ip is set to first ip. So on delete we
 +                //want to set sourcenat to true for all ips to delete source nat rules.
 +                if (!firstIP || add) {
 +                    firstIP = false;
 +                }
 +            }
 +            final IpAssocVpcCommand cmd = new IpAssocVpcCommand(ipsToSend);
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(ipAddrList.get(0).getNetworkId(), router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +            final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +            cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +            cmds.addCommand(ipAssocCommand, cmd);
 +        }
 +
 +        // set source nat ip
 +        if (sourceNatIpAdd != null) {
 +            final IpAddressTO sourceNatIp = sourceNatIpAdd.first();
 +            final SetSourceNatCommand cmd = new SetSourceNatCommand(sourceNatIp, addSourceNat);
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +            final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +            cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +            cmds.addCommand("SetSourceNatCommand", cmd);
 +        }
 +    }
 +
 +    public void createRedundantAssociateIPCommands(final VirtualRouter router, final List<? extends PublicIpAddress> ips, final Commands cmds, final String ipAssocCommand, final boolean isVPC) {
 +
 +        // Ensure that in multiple vlans case we first send all ip addresses of
 +        // vlan1, then all ip addresses of vlan2, etc..
 +        final Map<String, ArrayList<PublicIpAddress>> vlanIpMap = new HashMap<String, ArrayList<PublicIpAddress>>();
 +        for (final PublicIpAddress ipAddress : ips) {
 +            final String vlanTag = ipAddress.getVlanTag();
 +            ArrayList<PublicIpAddress> ipList = vlanIpMap.get(vlanTag);
 +            if (ipList == null) {
 +                ipList = new ArrayList<PublicIpAddress>();
 +            }
 +            // domR doesn't support release for sourceNat IP address; so reset
 +            // the state
 +            if (ipAddress.isSourceNat() && ipAddress.getState() == IpAddress.State.Releasing) {
 +                ipAddress.setState(IpAddress.State.Allocated);
 +            }
 +            ipList.add(ipAddress);
 +            vlanIpMap.put(vlanTag, ipList);
 +        }
 +
 +        final List<NicVO> nics = _nicDao.listByVmId(router.getId());
 +        String baseMac = null;
 +        for (final NicVO nic : nics) {
 +            final NetworkVO nw = _networkDao.findById(nic.getNetworkId());
 +            if (nw.getTrafficType() == TrafficType.Public) {
 +                baseMac = nic.getMacAddress();
 +                break;
 +            }
 +        }
 +
 +        for (final Map.Entry<String, ArrayList<PublicIpAddress>> vlanAndIp : vlanIpMap.entrySet()) {
 +            final List<PublicIpAddress> ipAddrList = vlanAndIp.getValue();
 +            // Source nat ip address should always be sent first
 +            Collections.sort(ipAddrList, new Comparator<PublicIpAddress>() {
 +                @Override
 +                public int compare(final PublicIpAddress o1, final PublicIpAddress o2) {
 +                    final boolean s1 = o1.isSourceNat();
 +                    final boolean s2 = o2.isSourceNat();
 +                    return s1 ^ s2 ? s1 ^ true ? 1 : -1 : 0;
 +                }
 +            });
 +
 +            // Get network rate - required for IpAssoc
 +            final Integer networkRate = _networkModel.getNetworkRate(ipAddrList.get(0).getNetworkId(), router.getId());
 +            final Network network = _networkModel.getNetwork(ipAddrList.get(0).getNetworkId());
 +
 +            final IpAddressTO[] ipsToSend = new IpAddressTO[ipAddrList.size()];
 +            int i = 0;
 +            boolean firstIP = true;
 +
 +            for (final PublicIpAddress ipAddr : ipAddrList) {
 +
 +                final boolean add = ipAddr.getState() == IpAddress.State.Releasing ? false : true;
 +                boolean sourceNat = ipAddr.isSourceNat();
 +                /* enable sourceNAT for the first ip of the public interface */
 +                if (firstIP) {
 +                    sourceNat = true;
 +                }
 +                final String vlanId = ipAddr.getVlanTag();
 +                final String vlanGateway = ipAddr.getGateway();
 +                final String vlanNetmask = ipAddr.getNetmask();
 +                String vifMacAddress = null;
 +                // For non-source nat IP, set the mac to be something based on
 +                // first public nic's MAC
 +                // We cannot depend on first ip because we need to deal with
 +                // first ip of other nics
 +                if (router.getVpcId() != null) {
 +                    //vifMacAddress = NetUtils.generateMacOnIncrease(baseMac, ipAddr.getVlanId());
 +                    vifMacAddress = ipAddr.getMacAddress();
 +                } else {
 +                    if (!sourceNat && ipAddr.getVlanId() != 0) {
 +                        vifMacAddress = NetUtils.generateMacOnIncrease(baseMac, ipAddr.getVlanId());
 +                    } else {
 +                        vifMacAddress = ipAddr.getMacAddress();
 +                    }
 +                }
 +
 +                final IpAddressTO ip = new IpAddressTO(ipAddr.getAccountId(), ipAddr.getAddress().addr(), add, firstIP, sourceNat, vlanId, vlanGateway, vlanNetmask,
 +                        vifMacAddress, networkRate, ipAddr.isOneToOneNat());
 +
 +                ip.setTrafficType(network.getTrafficType());
 +                ip.setNetworkName(_networkModel.getNetworkTag(router.getHypervisorType(), network));
 +                ipsToSend[i++] = ip;
 +                /*
 +                 * send the firstIP = true for the first Add, this is to create
 +                 * primary on interface
 +                 */
 +                if (!firstIP || add) {
 +                    firstIP = false;
 +                }
 +            }
 +
 +            Long associatedWithNetworkId = ipAddrList.get(0).getAssociatedWithNetworkId();
 +            if (associatedWithNetworkId == null || associatedWithNetworkId == 0) {
 +                associatedWithNetworkId = ipAddrList.get(0).getNetworkId();
 +            }
 +
 +            // for network if the ips does not have any rules, then only last ip
 +            final List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(associatedWithNetworkId, null);
 +            boolean hasSourceNat = false;
 +            if (isVPC && userIps.size() > 0 && userIps.get(0) != null) {
 +                // All ips should belong to a VPC
 +                final Long vpcId = userIps.get(0).getVpcId();
 +                final List<IPAddressVO> sourceNatIps = _ipAddressDao.listByAssociatedVpc(vpcId, true);
 +                if (sourceNatIps != null && sourceNatIps.size() > 0) {
 +                    hasSourceNat = true;
 +                }
 +            }
 +
 +            int ipsWithrules = 0;
 +            int ipsStaticNat = 0;
 +            for (IPAddressVO ip : userIps) {
 +                if ( _rulesDao.countRulesByIpIdAndState(ip.getId(), FirewallRule.State.Active) > 0){
 +                    ipsWithrules++;
 +                }
 +
 +                // check onetoonenat and also check if the ip "add":false. If there are 2 PF rules remove and
 +                // 1 static nat rule add
 +                if (ip.isOneToOneNat() && ip.getRuleState() == null) {
 +                    ipsStaticNat++;
 +                }
 +            }
 +
 +            final IpAssocCommand cmd = new IpAssocCommand(ipsToSend);
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(associatedWithNetworkId, router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +            final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +            cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +            // if there is 1 static nat then it will be checked for remove at the resource
 +            if (ipsWithrules == 0 && ipsStaticNat == 0 && !hasSourceNat) {
 +                // there is only one ip address for the network.
 +                cmd.setAccessDetail(NetworkElementCommand.NETWORK_PUB_LAST_IP, "true");
 +            } else {
 +                cmd.setAccessDetail(NetworkElementCommand.NETWORK_PUB_LAST_IP, "false");
 +            }
 +
 +            cmds.addCommand(ipAssocCommand, cmd);
 +        }
 +    }
 +
 +    public void createStaticRouteCommands(final List<StaticRouteProfile> staticRoutes, final DomainRouterVO router, final Commands cmds) {
 +        final SetStaticRouteCommand cmd = new SetStaticRouteCommand(staticRoutes);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand(cmd);
 +    }
 +
 +    public void createSite2SiteVpnCfgCommands(final Site2SiteVpnConnection conn, final boolean isCreate, final VirtualRouter router, final Commands cmds) {
 +        final Site2SiteCustomerGatewayVO gw = _s2sCustomerGatewayDao.findById(conn.getCustomerGatewayId());
 +        final Site2SiteVpnGatewayVO vpnGw = _s2sVpnGatewayDao.findById(conn.getVpnGatewayId());
 +        final IpAddress ip = _ipAddressDao.findById(vpnGw.getAddrId());
 +        final Vpc vpc = _vpcDao.findById(ip.getVpcId());
 +        final String localPublicIp = ip.getAddress().toString();
 +        final String localGuestCidr = vpc.getCidr();
 +        final String localPublicGateway = _vlanDao.findById(ip.getVlanId()).getVlanGateway();
 +        final String peerGatewayIp = gw.getGatewayIp();
 +        final String peerGuestCidrList = gw.getGuestCidrList();
 +        final String ipsecPsk = gw.getIpsecPsk();
 +        final String ikePolicy = gw.getIkePolicy();
 +        final String espPolicy = gw.getEspPolicy();
 +        final Long ikeLifetime = gw.getIkeLifetime();
 +        final Long espLifetime = gw.getEspLifetime();
 +        final Boolean dpd = gw.getDpd();
 +        final Boolean encap = gw.getEncap();
 +
 +        final Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(isCreate, localPublicIp, localPublicGateway, localGuestCidr, peerGatewayIp, peerGuestCidrList, ikePolicy,
 +                espPolicy, ipsecPsk, ikeLifetime, espLifetime, dpd, conn.isPassive(), encap);
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +        cmds.addCommand("applyS2SVpn", cmd);
 +    }
 +
 +    public void createVpcAssociatePrivateIPCommands(final VirtualRouter router, final List<PrivateIpAddress> ips, final Commands cmds, final boolean add) {
 +
 +        // Ensure that in multiple vlans case we first send all ip addresses of
 +        // vlan1, then all ip addresses of vlan2, etc..
 +        final Map<String, ArrayList<PrivateIpAddress>> vlanIpMap = new HashMap<String, ArrayList<PrivateIpAddress>>();
 +        for (final PrivateIpAddress ipAddress : ips) {
 +            final String vlanTag = ipAddress.getBroadcastUri();
 +            ArrayList<PrivateIpAddress> ipList = vlanIpMap.get(vlanTag);
 +            if (ipList == null) {
 +                ipList = new ArrayList<PrivateIpAddress>();
 +            }
 +
 +            ipList.add(ipAddress);
 +            vlanIpMap.put(vlanTag, ipList);
 +        }
 +
 +        for (final Map.Entry<String, ArrayList<PrivateIpAddress>> vlanAndIp : vlanIpMap.entrySet()) {
 +            final List<PrivateIpAddress> ipAddrList = vlanAndIp.getValue();
 +            final IpAddressTO[] ipsToSend = new IpAddressTO[ipAddrList.size()];
 +            int i = 0;
 +
 +            for (final PrivateIpAddress ipAddr : ipAddrList) {
 +                final Network network = _networkModel.getNetwork(ipAddr.getNetworkId());
 +                final IpAddressTO ip = new IpAddressTO(Account.ACCOUNT_ID_SYSTEM, ipAddr.getIpAddress(), add, false, ipAddr.getSourceNat(), ipAddr.getBroadcastUri(),
 +                        ipAddr.getGateway(), ipAddr.getNetmask(), ipAddr.getMacAddress(), null, false);
 +
 +                ip.setTrafficType(network.getTrafficType());
 +                ip.setNetworkName(_networkModel.getNetworkTag(router.getHypervisorType(), network));
 +                ipsToSend[i++] = ip;
 +
 +            }
 +            final IpAssocVpcCommand cmd = new IpAssocVpcCommand(ipsToSend);
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(ipAddrList.get(0).getNetworkId(), router.getId()));
 +            cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +            final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +            cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +            cmds.addCommand("IPAssocVpcCommand", cmd);
 +        }
 +    }
 +
 +    public SetupGuestNetworkCommand createSetupGuestNetworkCommand(final DomainRouterVO router, final boolean add, final NicProfile guestNic) {
 +        final Network network = _networkModel.getNetwork(guestNic.getNetworkId());
 +
 +        String defaultDns1 = null;
 +        String defaultDns2 = null;
 +
 +        final boolean dnsProvided = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, Provider.VPCVirtualRouter);
 +        final boolean dhcpProvided = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, Provider.VPCVirtualRouter);
 +
 +        final boolean setupDns = dnsProvided || dhcpProvided;
 +
 +        if (setupDns) {
 +            defaultDns1 = guestNic.getIPv4Dns1();
 +            defaultDns2 = guestNic.getIPv4Dns2();
 +        }
 +
 +        final Nic nic = _nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId());
 +        final String networkDomain = network.getNetworkDomain();
 +        final String dhcpRange = getGuestDhcpRange(guestNic, network, _entityMgr.findById(DataCenter.class, network.getDataCenterId()));
 +
 +        final NicProfile nicProfile = _networkModel.getNicProfile(router, nic.getNetworkId(), null);
 +
 +        final SetupGuestNetworkCommand setupCmd = new SetupGuestNetworkCommand(dhcpRange, networkDomain, router.getIsRedundantRouter(), defaultDns1, defaultDns2, add, _itMgr.toNicTO(nicProfile,
 +                router.getHypervisorType()));
 +
 +        final String brd = NetUtils.long2Ip(NetUtils.ip2Long(guestNic.getIPv4Address()) | ~NetUtils.ip2Long(guestNic.getIPv4Netmask()));
 +        setupCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        setupCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(network.getId(), router.getId()));
 +
 +        setupCmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, network.getGateway());
 +        setupCmd.setAccessDetail(NetworkElementCommand.GUEST_BRIDGE, brd);
 +        setupCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +
 +        if (network.getBroadcastDomainType() == BroadcastDomainType.Vlan) {
 +            final long guestVlanTag = Long.parseLong(BroadcastDomainType.Vlan.getValueFrom(network.getBroadcastUri()));
 +            setupCmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, String.valueOf(guestVlanTag));
 +        }
 +
 +        return setupCmd;
 +    }
 +
 +    private VmDataCommand generateVmDataCommand(final VirtualRouter router, final String vmPrivateIpAddress, final String userData, final String serviceOffering,
 +            final String zoneName, final String guestIpAddress, final String vmName, final String vmInstanceName, final long vmId, final String vmUuid, final String publicKey,
 +            final long guestNetworkId) {
 +        final VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName, _networkModel.getExecuteInSeqNtwkElmtCmd());
 +
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(guestNetworkId, router.getId()));
 +        cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
 +
 +        final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
 +        cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
 +
 +        cmd.addVmData("userdata", "user-data", userData);
 +        cmd.addVmData("metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering));
 +        cmd.addVmData("metadata", "availability-zone", StringUtils.unicodeEscape(zoneName));
 +        cmd.addVmData("metadata", "local-ipv4", guestIpAddress);
 +        cmd.addVmData("metadata", "local-hostname", StringUtils.unicodeEscape(vmName));
 +        if (dcVo.getNetworkType() == NetworkType.Basic) {
 +            cmd.addVmData("metadata", "public-ipv4", guestIpAddress);
 +            cmd.addVmData("metadata", "public-hostname", StringUtils.unicodeEscape(vmName));
 +        } else {
 +            if (router.getPublicIpAddress() == null) {
 +                cmd.addVmData("metadata", "public-ipv4", guestIpAddress);
 +            } else {
 +                cmd.addVmData("metadata", "public-ipv4", router.getPublicIpAddress());
 +            }
 +            cmd.addVmData("metadata", "public-hostname", router.getPublicIpAddress());
 +        }
 +        if (vmUuid == null) {
 +            cmd.addVmData("metadata", "instance-id", vmInstanceName);
 +            cmd.addVmData("metadata", "vm-id", String.valueOf(vmId));
 +        } else {
 +            cmd.addVmData("metadata", "instance-id", vmUuid);
 +            cmd.addVmData("metadata", "vm-id", vmUuid);
 +        }
 +        cmd.addVmData("metadata", "public-keys", publicKey);
 +
 +        String cloudIdentifier = _configDao.getValue("cloud.identifier");
 +        if (cloudIdentifier == null) {
 +            cloudIdentifier = "";
 +        } else {
 +            cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}";
 +        }
 +        cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier);
 +
 +        return cmd;
 +    }
 +
 +    private NicVO findGatewayIp(final long userVmId) {
 +        final NicVO defaultNic = _nicDao.findDefaultNicForVM(userVmId);
 +        return defaultNic;
 +    }
 +
 +    private NicVO findDefaultDnsIp(final long userVmId) {
 +        final NicVO defaultNic = _nicDao.findDefaultNicForVM(userVmId);
 +
 +        // check if DNS provider is the domR
 +        if (!_networkModel.isProviderSupportServiceInNetwork(defaultNic.getNetworkId(), Service.Dns, Provider.VirtualRouter)) {
 +            return null;
 +        }
 +
 +        final NetworkOffering offering = _networkOfferingDao.findById(_networkDao.findById(defaultNic.getNetworkId()).getNetworkOfferingId());
 +        if (offering.isRedundantRouter()) {
 +            return findGatewayIp(userVmId);
 +        }
 +
 +        final DataCenter dc = _dcDao.findById(_networkModel.getNetwork(defaultNic.getNetworkId()).getDataCenterId());
 +        final boolean isZoneBasic = dc.getNetworkType() == NetworkType.Basic;
 +
 +        // find domR's nic in the network
 +        NicVO domrDefaultNic;
 +        if (isZoneBasic) {
 +            domrDefaultNic = _nicDao.findByNetworkIdTypeAndGateway(defaultNic.getNetworkId(), VirtualMachine.Type.DomainRouter, defaultNic.getIPv4Gateway());
 +        } else {
 +            domrDefaultNic = _nicDao.findByNetworkIdAndType(defaultNic.getNetworkId(), VirtualMachine.Type.DomainRouter);
 +        }
 +        return domrDefaultNic;
 +    }
 +
 +    protected String getGuestDhcpRange(final NicProfile guestNic, final Network guestNetwork, final DataCenter dc) {
 +        String dhcpRange = null;
 +        // setup dhcp range
 +        if (dc.getNetworkType() == NetworkType.Basic) {
 +            final long cidrSize = NetUtils.getCidrSize(guestNic.getIPv4Netmask());
 +            final String cidr = NetUtils.getCidrSubNet(guestNic.getIPv4Gateway(), cidrSize);
 +            if (cidr != null) {
 +                dhcpRange = NetUtils.getIpRangeStartIpFromCidr(cidr, cidrSize);
 +            }
 +        } else if (dc.getNetworkType() == NetworkType.Advanced) {
 +            final String cidr = guestNetwork.getCidr();
 +            if (cidr != null) {
 +                dhcpRange = NetUtils.getDhcpRange(cidr);
 +            }
 +        }
 +        return dhcpRange;
 +    }
 +}
diff --cc server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 9762dc3,0000000..e6ba5e7
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,6638 -1,0 +1,6644 @@@
 +// 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.AccountVO;
 +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 userVmDetailsDao;
 +    @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 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);
 +
 +    private static final ConfigKey<Boolean> EnableAdditionalVmConfig = new ConfigKey<>("Advanced", Boolean.class, "enable.additional.vm.configuration",
 +            "false", "allow additional arbitrary configuration to vm", true, ConfigKey.Scope.Account);
 +    private static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false",
 +            "On destroy, force-stop takes this value ", 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.isEnablePassword()) {
 +            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");
 +        }
 +
 +        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 password reset");
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, userVm);
 +
 +        boolean result = resetVMPasswordInternal(vmId, password);
 +
 +        if (result) {
 +            userVm.setPassword(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.isEnablePassword()) {
 +            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 {
 +                final UserVmVO userVm = _vmDao.findById(vmId);
 +                _vmDao.loadDetails(userVm);
 +                // 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);
 +
 +                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.isEnablePassword()) {
 +            password = _mgr.generateRandomPassword();
 +        }
 +
 +        boolean result = resetVMSSHKeyInternal(vmId, sshPublicKey, password);
 +
 +        if (!result) {
 +            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.isEnablePassword()) {
 +            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 {
 +            final UserVmVO userVm = _vmDao.findById(vmId);
 +            _vmDao.loadDetails(userVm);
 +            userVm.setDetail("SSH.PublicKey", sshPublicKey);
 +            if (template.isEnablePassword()) {
 +                userVm.setPassword(password);
 +                //update the encrypted password in vm_details table too
 +                encryptAndStorePassword(userVm, password);
 +            } else {
 +                _vmDao.saveDetails(userVm);
 +            }
 +
 +            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());
 +            }
 +        }
 +
 +        macAddress = validateOrReplaceMacAddress(macAddress, network.getId());
 +
 +        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());
 +    }
 +
 +    /**
 +     * If the given MAC address is invalid it replaces the given MAC with the next available MAC address
 +     */
 +    protected String validateOrReplaceMacAddress(String macAddress, long networkId) {
 +        if (!NetUtils.isValidMac(macAddress)) {
 +            try {
 +                macAddress = _networkModel.getNextAvailableMacAddressInNetwork(networkId);
 +            } catch (InsufficientAddressCapacityException e) {
 +                throw new CloudRuntimeException(String.format("A MAC address cannot be generated for this NIC in the network [id=%s] ", networkId));
 +            }
 +        }
 +        return macAddress;
 +    }
 +
 +    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());
 +
 +            if (vmInstance.getState() != State.Stopped) {
 +                try {
 +                    VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance);
 +                    User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
 +                    ReservationContext context = new ReservationContextImpl(null, null, callerUser, caller);
 +                    DeployDestination dest = new DeployDestination(dc, null, null, null);
 +                    _networkMgr.prepare(vmProfile, dest, context);
 +                } catch (final Exception e) {
 +                    s_logger.info("Got exception: ", e);
 +                }
 +            }
 +
 +            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();
 +        VirtualMachine vm = (VirtualMachine) this._entityMgr.findById(VirtualMachine.class, vmId);
 +        if (vm == null) {
 +            throw new InvalidParameterValueException("Unable to find VM's UUID");
 +        }
 +        CallContext.current().setEventDetails("Vm Id: " + vm.getUuid());
 +
 +        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);
 +        }
 +
 +        // Check that the specified service offering ID is valid
 +        _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering);
 +
 +        ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId());
 +        int newCpu = newServiceOffering.getCpu();
 +        int newMemory = newServiceOffering.getRamSize();
 +        int newSpeed = newServiceOffering.getSpeed();
 +        int currentCpu = currentServiceOffering.getCpu();
 +        int currentMemory = currentServiceOffering.getRamSize();
 +        int currentSpeed = currentServiceOffering.getSpeed();
 +        int memoryDiff = newMemory - currentMemory;
 +        int cpuDiff = newCpu * newSpeed - currentCpu * currentSpeed;
 +
 +        // Don't allow to scale when (Any of the new values less than current values) OR (All current and new values are same)
 +        if ((newSpeed < currentSpeed || newMemory < currentMemory || newCpu < currentCpu) || (newSpeed == currentSpeed && newMemory == currentMemory && newCpu == currentCpu)) {
 +            throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering(speed=" + newSpeed + ",cpu=" + newCpu + ",memory=," + newMemory
 +                    + ")" + " should have at least one value(cpu/ram) greater than old value and no resource value less than older(speed=" + currentSpeed + ",cpu=" + currentCpu
 +                    + ",memory=," + currentMemory + ")");
 +        }
 +
 +        _offeringDao.loadDetails(currentServiceOffering);
 +        _offeringDao.loadDetails(newServiceOffering);
 +
 +        Map<String, String> currentDetails = currentServiceOffering.getDetails();
 +        Map<String, String> newDetails = newServiceOffering.getDetails();
 +        String currentVgpuType = currentDetails.get("vgpuType");
 +        String newVgpuType = newDetails.get("vgpuType");
 +        if(currentVgpuType != null) {
 +            if(newVgpuType == null || !newVgpuType.equalsIgnoreCase(currentVgpuType)) {
 +                throw new InvalidParameterValueException("Dynamic scaling of vGPU type is not supported. VM has vGPU Type: " + currentVgpuType);
 +            }
 +        }
 +
 +        // Check resource limits
 +        if (newCpu > currentCpu) {
 +            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu, newCpu - currentCpu);
 +        }
 +        if (newMemory > currentMemory) {
 +            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, newMemory - currentMemory);
 +        }
 +
 +        // Dynamically upgrade the running vms
 +        boolean success = false;
 +        if (vmInstance.getState().equals(State.Running)) {
 +            int retry = _scaleRetry;
 +            ExcludeList excludes = new ExcludeList();
 +
 +            // Check zone wide flag
 +            boolean enableDynamicallyScaleVm = EnableDynamicallyScaleVm.valueIn(vmInstance.getDataCenterId());
 +            if (!enableDynamicallyScaleVm) {
 +                throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin");
 +            }
 +
 +            // Check vm flag
 +            if (!vmInstance.isDynamicallyScalable()) {
 +                throw new CloudRuntimeException("Unable to Scale the vm: " + vmInstance.getUuid() + " as vm does not have tools to support dynamic scaling");
 +            }
 +
 +            // Check disable threshold for cluster is not crossed
 +            HostVO host = _hostDao.findById(vmInstance.getHostId());
 +            if (_capacityMgr.checkIfClusterCrossesThreshold(host.getClusterId(), cpuDiff, memoryDiff)) {
 +                throw new CloudRuntimeException("Unable to scale vm: " + vmInstance.getUuid() + " due to insufficient resources");
 +            }
 +
 +            while (retry-- != 0) { // It's != so that it can match -1.
 +                try {
 +                    boolean existingHostHasCapacity = false;
 +
 +                    // Increment CPU and Memory count accordingly.
 +                    if (newCpu > currentCpu) {
 +                        _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu));
 +                    }
 +
 +                    if (memoryDiff > 0) {
 +                        _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(memoryDiff));
 +                    }
 +
 +                    // #1 Check existing host has capacity
 +                    if (!excludes.shouldAvoid(ApiDBUtils.findHostById(vmInstance.getHostId()))) {
 +                        existingHostHasCapacity = _capacityMgr.checkIfHostHasCpuCapability(vmInstance.getHostId(), newCpu, newSpeed)
 +                                && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, (memoryDiff) * 1024L * 1024L, false,
 +                                        _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU),
 +                                        _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false);
 +                        excludes.addHost(vmInstance.getHostId());
 +                    }
 +
 +                    // #2 migrate the vm if host doesn't have capacity or is in avoid set
 +                    if (!existingHostHasCapacity) {
 +                        _itMgr.findHostAndMigrate(vmInstance.getUuid(), newServiceOfferingId, excludes);
 +                    }
 +
 +                    // #3 scale the vm now
 +                    _itMgr.upgradeVmDb(vmId, newServiceOfferingId);
 +                    if (newServiceOffering.isDynamic()) {
 +                        //save the custom values to the database.
 +                        saveCustomOfferingDetails(vmId, newServiceOffering);
 +                    }
 +                    vmInstance = _vmInstanceDao.findById(vmId);
 +                    _itMgr.reConfigureVm(vmInstance.getUuid(), currentServiceOffering, existingHostHasCapacity);
 +                    success = true;
 +                    if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) {
 +                        removeCustomOfferingDetails(vmId);
 +                    }
 +                    return success;
 +                } catch (InsufficientCapacityException e) {
 +                    s_logger.warn("Received exception while scaling ", e);
 +                } catch (ResourceUnavailableException e) {
 +                    s_logger.warn("Received exception while scaling ", e);
 +                } catch (ConcurrentOperationException e) {
 +                    s_logger.warn("Received exception while scaling ", e);
 +                } catch (Exception e) {
 +                    s_logger.warn("Received exception while scaling ", e);
 +                } finally {
 +                    if (!success) {
 +                        _itMgr.upgradeVmDb(vmId, currentServiceOffering.getId()); // rollback
 +
 +                        // Decrement CPU and Memory count accordingly.
 +                        if (newCpu > currentCpu) {
 +                            _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu));
 +                        }
 +                        //restoring old service offering will take care of removing new SO.
 +                        if(currentServiceOffering.isDynamic()){
 +                            saveCustomOfferingDetails(vmId, currentServiceOffering);
 +                        }
 +
 +                        if (memoryDiff > 0) {
 +                            _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(memoryDiff));
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        return success;
 +    }
 +
 +    @Override
 +    public void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) {
 +        //save the custom values to the database.
 +        Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vmId);
 +        details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString());
 +        details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString());
 +        details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString());
 +        List<UserVmDetailVO> detailList = new ArrayList<UserVmDetailVO>();
 +        for (Map.Entry<String, String> entry: details.entrySet()) {
 +            UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true);
 +            detailList.add(detailVO);
 +        }
 +        userVmDetailsDao.saveDetails(detailList);
 +    }
 +
 +    @Override
 +    public void removeCustomOfferingDetails(long vmId) {
 +        Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vmId);
 +        details.remove(UsageEventVO.DynamicParameters.cpuNumber.name());
 +        details.remove(UsageEventVO.DynamicParameters.cpuSpeed.name());
 +        details.remove(UsageEventVO.DynamicParameters.memory.name());
 +        List<UserVmDetailVO> detailList = new ArrayList<UserVmDetailVO>();
 +        for(Map.Entry<String, String> entry: details.entrySet()) {
 +            UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true);
 +            detailList.add(detailVO);
 +        }
 +        userVmDetailsDao.saveDetails(detailList);
 +    }
 +
 +    @Override
 +    public HashMap<Long, VmStatsEntry> getVirtualMachineStatistics(long hostId, String hostName, List<Long> vmIds) throws CloudRuntimeException {
 +        HashMap<Long, VmStatsEntry> vmStatsById = new HashMap<Long, VmStatsEntry>();
 +
 +        if (vmIds.isEmpty()) {
 +            return vmStatsById;
 +        }
 +
 +        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 GetVmStatsCommand(vmNames, _hostDao.findById(hostId).getGuid(), hostName));
 +        if (answer == null || !answer.getResult()) {
 +            s_logger.warn("Unable to obtain VM statistics.");
 +            return null;
 +        } else {
 +            HashMap<String, VmStatsEntry> vmStatsByName = ((GetVmStatsAnswer)answer).getVmStatsMap();
 +
 +            if (vmStatsByName == null) {
 +                s_logger.warn("Unable to obtain VM statistics.");
 +                return null;
 +            }
 +
 +            for (Map.Entry<String, VmStatsEntry> entry : vmStatsByName.entrySet()) {
 +                vmStatsById.put(vmIds.get(vmNames.indexOf(entry.getKey())), entry.getValue());
 +            }
 +        }
 +
 +        return vmStatsById;
 +    }
 +
 +    @Override
 +    public HashMap<String, VolumeStatsEntry> getVolumeStatistics(long clusterId, String poolUuid, StoragePoolType poolType, List<String> volumeLocator, int timeout) {
 +        List<HostVO> neighbors = _resourceMgr.listHostsInClusterByStatus(clusterId, Status.Up);
 +        for (HostVO neighbor : neighbors) {
 +            GetVolumeStatsCommand cmd = new GetVolumeStatsCommand(poolType, poolUuid, volumeLocator);
 +            if (timeout > 0) {
 +                cmd.setWait(timeout/1000);
 +            }
 +            Answer answer = _agentMgr.easySend(neighbor.getId(), cmd);
 +            if (answer instanceof GetVolumeStatsAnswer){
 +                GetVolumeStatsAnswer volstats = (GetVolumeStatsAnswer)answer;
 +                return volstats.getVolumeStats();
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    @DB
 +    public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException, CloudRuntimeException {
 +
 +        final Long vmId = cmd.getId();
 +        Account caller = CallContext.current().getCallingAccount();
 +        final Long userId = caller.getAccountId();
 +
 +        // Verify input parameters
 +        final UserVmVO vm = _vmDao.findById(vmId);
 +
 +        if (vm == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        // When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVm is false for the caller.
 +        if (!_accountMgr.isAdmin(userId) && !AllowUserExpungeRecoverVm.valueIn(userId)) {
 +            throw new PermissionDeniedException("Recovering a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set.");
 +        }
 +
 +        if (vm.getRemoved() != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Unable to find vm or vm is removed: " + vmId);
 +            }
 +            throw new InvalidParameterValueException("Unable to find vm by id " + vmId);
 +        }
 +
 +        if (vm.getState() != State.Destroyed) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("vm is not in the right state: " + vmId);
 +            }
 +            throw new InvalidParameterValueException("Vm with id " + vmId + " is not in the right state");
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Recovering vm " + vmId);
 +        }
 +
 +        Transaction.execute(new TransactionCallbackWithExceptionNoReturn<ResourceAllocationException>() {
 +            @Override public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException {
 +
 +                Account account = _accountDao.lockRow(vm.getAccountId(), true);
 +
 +                // if the account is deleted, throw error
 +                if (account.getRemoved() != null) {
 +                    throw new CloudRuntimeException("Unable to recover VM as the account is deleted");
 +                }
 +
 +                // Get serviceOffering for Virtual Machine
 +                ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
 +
 +                // First check that the maximum number of UserVMs, CPU and Memory limit for the given
 +                // accountId will not be exceeded
 +                resourceLimitCheck(account, vm.isDisplayVm(), new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize()));
 +
 +                _haMgr.cancelDestroy(vm, vm.getHostId());
 +
 +                try {
 +                    if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) {
 +                        s_logger.debug("Unable to recover the vm because it is not in the correct state: " + vmId);
 +                        throw new InvalidParameterValueException("Unable to recover the vm because it is not in the correct state: " + vmId);
 +                    }
 +                } catch (NoTransitionException e) {
 +                    throw new InvalidParameterValueException("Unable to recover the vm because it is not in the correct state: " + vmId);
 +                }
 +
 +                // Recover the VM's disks
 +                List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
 +                for (VolumeVO volume : volumes) {
 +                    if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
 +                        // Create an event
 +                        Long templateId = volume.getTemplateId();
 +                        Long diskOfferingId = volume.getDiskOfferingId();
 +                        Long offeringId = null;
 +                        if (diskOfferingId != null) {
 +                            DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId);
 +                            if (offering != null && (offering.getType() == DiskOfferingVO.Type.Disk)) {
 +                                offeringId = offering.getId();
 +                            }
 +                        }
 +                        UsageEventUtils
 +                        .publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), offeringId,
 +                                templateId, volume.getSize(), Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume());
 +                    }
 +                }
 +
 +                //Update Resource Count for the given account
 +                resourceCountIncrement(account.getId(), vm.isDisplayVm(), new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize()));
 +            }
 +        });
 +
 +        return _vmDao.findById(vmId);
 +    }
 +
 +    @Override
 +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
 +        _name = name;
 +
 +        if (_configDao == null) {
 +            throw new ConfigurationException("Unable to get the configuration dao.");
 +        }
 +
 +        Map<String, String> configs = _configDao.getConfiguration("AgentManager", params);
 +
 +        _instance = configs.get("instance.name");
 +        if (_instance == null) {
 +            _instance = "DEFAULT";
 +        }
 +
 +        String workers = configs.get("expunge.workers");
 +        int wrks = NumbersUtil.parseInt(workers, 10);
 +        capacityReleaseInterval = NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()), 3600);
 +
 +        String time = configs.get("expunge.interval");
 +        _expungeInterval = NumbersUtil.parseInt(time, 86400);
 +        time = configs.get("expunge.delay");
 +        _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval);
 +
 +        _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger"));
 +
 +        String vmIpWorkers = configs.get(VmIpFetchTaskWorkers.value());
 +        int vmipwrks = NumbersUtil.parseInt(vmIpWorkers, 10);
 +
 +        _vmIpFetchExecutor =   Executors.newScheduledThreadPool(vmipwrks, new NamedThreadFactory("UserVm-ipfetch"));
 +
 +        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
 +        int _usageAggregationRange  = NumbersUtil.parseInt(aggregationRange, 1440);
 +        int HOURLY_TIME = 60;
 +        final int DAILY_TIME = 60 * 24;
 +        if (_usageAggregationRange == DAILY_TIME) {
 +            _dailyOrHourly = true;
 +        } else if (_usageAggregationRange == HOURLY_TIME) {
 +            _dailyOrHourly = true;
 +        } else {
 +            _dailyOrHourly = false;
 +        }
 +
 +        _itMgr.registerGuru(VirtualMachine.Type.User, this);
 +
 +        VirtualMachine.State.getStateMachine().registerListener(new UserVmStateListener(_usageEventDao, _networkDao, _nicDao, _offeringDao, _vmDao, this, _configDao));
 +
 +        String value = _configDao.getValue(Config.SetVmInternalNameUsingDisplayName.key());
 +        _instanceNameFlag = (value == null) ? false : Boolean.parseBoolean(value);
 +
 +        _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2);
 +
 +        _vmIpFetchThreadExecutor = Executors.newFixedThreadPool(VmIpFetchThreadPoolMax.value(), new NamedThreadFactory("vmIpFetchThread"));
 +
 +        s_logger.info("User VM Manager is configured.");
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public String getName() {
 +        return _name;
 +    }
 +
 +    @Override
 +    public boolean start() {
 +        _executor.scheduleWithFixedDelay(new ExpungeTask(), _expungeInterval, _expungeInterval, TimeUnit.SECONDS);
 +        _vmIpFetchExecutor.scheduleWithFixedDelay(new VmIpFetchTask(), VmIpFetchWaitInterval.value(), VmIpFetchWaitInterval.value(), TimeUnit.SECONDS);
 +        loadVmDetailsInMapForExternalDhcpIp();
 +        return true;
 +    }
 +
 +    private void loadVmDetailsInMapForExternalDhcpIp() {
 +
 +        List<NetworkVO> networks = _networkDao.listByGuestType(Network.GuestType.Shared);
 +
 +        for (NetworkVO network: networks) {
 +            if(_networkModel.isSharedNetworkWithoutServices(network.getId())) {
 +                List<NicVO> nics = _nicDao.listByNetworkId(network.getId());
 +
 +                for (NicVO nic : nics) {
 +
 +                    if (nic.getIPv4Address() == null) {
 +                        long nicId = nic.getId();
 +                        long vmId = nic.getInstanceId();
 +                        VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
 +
 +                        // only load running vms. For stopped vms get loaded on starting
 +                        if (vmInstance.getState() == State.Running) {
 +                            VmAndCountDetails vmAndCount = new VmAndCountDetails(vmId, VmIpFetchTrialMax.value());
 +                            vmIdCountMap.put(nicId, vmAndCount);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean stop() {
 +        _executor.shutdown();
 +        _vmIpFetchExecutor.shutdown();
 +        return true;
 +    }
 +
 +    public String getRandomPrivateTemplateName() {
 +        return UUID.randomUUID().toString();
 +    }
 +
 +    @Override
 +    public boolean expunge(UserVmVO vm, long callerUserId, Account caller) {
 +        vm = _vmDao.acquireInLockTable(vm.getId());
 +        if (vm == null) {
 +            return false;
 +        }
 +        try {
 +
 +            releaseNetworkResourcesOnExpunge(vm.getId());
 +
 +            List<VolumeVO> rootVol = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
 +            // expunge the vm
 +            _itMgr.advanceExpunge(vm.getUuid());
 +            // Update Resource count
 +            if (vm.getAccountId() != Account.ACCOUNT_ID_SYSTEM && !rootVol.isEmpty()) {
 +                _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.volume);
 +                _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, new Long(rootVol.get(0).getSize()));
 +            }
 +
 +            // Only if vm is not expunged already, cleanup it's resources
 +            if (vm.getRemoved() == null) {
 +                // Cleanup vm resources - all the PF/LB/StaticNat rules
 +                // associated with vm
 +                s_logger.debug("Starting cleaning up vm " + vm + " resources...");
 +                if (cleanupVmResources(vm.getId())) {
 +                    s_logger.debug("Successfully cleaned up vm " + vm + " resources as a part of expunge process");
 +                } else {
 +                    s_logger.warn("Failed to cleanup resources as a part of vm " + vm + " expunge");
 +                    return false;
 +                }
 +
 +                _vmDao.remove(vm.getId());
 +            }
 +
 +            return true;
 +
 +        } catch (ResourceUnavailableException e) {
 +            s_logger.warn("Unable to expunge  " + vm, e);
 +            return false;
 +        } catch (OperationTimedoutException e) {
 +            s_logger.warn("Operation time out on expunging " + vm, e);
 +            return false;
 +        } catch (ConcurrentOperationException e) {
 +            s_logger.warn("Concurrent operations on expunging " + vm, e);
 +            return false;
 +        } finally {
 +            _vmDao.releaseFromLockTable(vm.getId());
 +        }
 +    }
 +
 +    /**
 +     * Release network resources, it was done on vm stop previously.
 +     * @param id vm id
 +     * @throws ConcurrentOperationException
 +     * @throws ResourceUnavailableException
 +     */
 +    private void releaseNetworkResourcesOnExpunge(long id) throws ConcurrentOperationException, ResourceUnavailableException {
 +        final VMInstanceVO vmInstance = _vmDao.findById(id);
 +        if (vmInstance != null){
 +            final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vmInstance);
 +            _networkMgr.release(profile, false);
 +        }
 +        else {
 +            s_logger.error("Couldn't find vm with id = " + id + ", unable to release network resources");
 +        }
 +    }
 +
 +    private boolean cleanupVmResources(long vmId) {
 +        boolean success = true;
 +        // Remove vm from security groups
 +        _securityGroupMgr.removeInstanceFromGroups(vmId);
 +
 +        // Remove vm from instance group
 +        removeInstanceFromInstanceGroup(vmId);
 +
 +        // cleanup firewall rules
 +        if (_firewallMgr.revokeFirewallRulesForVm(vmId)) {
 +            s_logger.debug("Firewall rules are removed successfully as a part of vm id=" + vmId + " expunge");
 +        } else {
 +            success = false;
 +            s_logger.warn("Fail to remove firewall rules as a part of vm id=" + vmId + " expunge");
 +        }
 +
 +        // cleanup port forwarding rules
 +        if (_rulesMgr.revokePortForwardingRulesForVm(vmId)) {
 +            s_logger.debug("Port forwarding rules are removed successfully as a part of vm id=" + vmId + " expunge");
 +        } else {
 +            success = false;
 +            s_logger.warn("Fail to remove port forwarding rules as a part of vm id=" + vmId + " expunge");
 +        }
 +
 +        // cleanup load balancer rules
 +        if (_lbMgr.removeVmFromLoadBalancers(vmId)) {
 +            s_logger.debug("Removed vm id=" + vmId + " from all load balancers as a part of expunge process");
 +        } else {
 +            success = false;
 +            s_logger.warn("Fail to remove vm id=" + vmId + " from load balancers as a part of expunge process");
 +        }
 +
 +        // If vm is assigned to static nat, disable static nat for the ip
 +        // address and disassociate ip if elasticIP is enabled
 +        List<IPAddressVO> ips = _ipAddressDao.findAllByAssociatedVmId(vmId);
 +
 +        for (IPAddressVO ip : ips) {
 +            try {
 +                if (_rulesMgr.disableStaticNat(ip.getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM, true)) {
 +                    s_logger.debug("Disabled 1-1 nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge");
 +                } else {
 +                    s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge");
 +                    success = false;
 +                }
 +            } catch (ResourceUnavailableException e) {
 +                success = false;
 +                s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge because resource is unavailable", e);
 +            }
 +        }
 +
 +        return success;
 +    }
 +
 +    @Override
 +    public void deletePrivateTemplateRecord(Long templateId) {
 +        if (templateId != null) {
 +            _templateDao.remove(templateId);
 +        }
 +    }
 +
 +    // used for vm transitioning to error state
 +    private void updateVmStateForFailedVmCreation(Long vmId, Long hostId) {
 +
 +        UserVmVO vm = _vmDao.findById(vmId);
 +
 +        if (vm != null) {
 +            if (vm.getState().equals(State.Stopped)) {
 +                s_logger.debug("Destroying vm " + vm + " as it failed to create on Host with Id:" + hostId);
 +                try {
 +                    _itMgr.stateTransitTo(vm, VirtualMachine.Event.OperationFailedToError, null);
 +                } catch (NoTransitionException e1) {
 +                    s_logger.warn(e1.getMessage());
 +                }
 +                // destroy associated volumes for vm in error state
 +                // get all volumes in non destroyed state
 +                List<VolumeVO> volumesForThisVm = _volsDao.findUsableVolumesForInstance(vm.getId());
 +                for (VolumeVO volume : volumesForThisVm) {
 +                    if (volume.getState() != Volume.State.Destroy) {
 +                        volumeMgr.destroyVolume(volume);
 +                    }
 +                }
 +                String msg = "Failed to deploy Vm with Id: " + vmId + ", on Host with Id: " + hostId;
 +                _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg);
 +
 +                // Get serviceOffering for Virtual Machine
 +                ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
 +
 +                // Update Resource Count for the given account
 +                resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize()));
 +            }
 +        }
 +    }
 +
 +
 +
 +    private class VmIpFetchTask extends ManagedContextRunnable {
 +
 +        @Override
 +        protected void runInContext() {
 +            GlobalLock scanLock = GlobalLock.getInternLock("vmIpFetch");
 +            try {
 +                if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {
 +                    try {
 +
 +                        for (Entry<Long, VmAndCountDetails> entry:   vmIdCountMap.entrySet()) {
 +                            long nicId = entry.getKey();
 +                            VmAndCountDetails vmIdAndCount = entry.getValue();
 +                            long vmId = vmIdAndCount.getVmId();
 +
 +                            if (vmIdAndCount.getRetrievalCount() <= 0) {
 +                                vmIdCountMap.remove(nicId);
 +                                s_logger.debug("Vm " + vmId +" nic "+nicId + " count is zero .. removing vm nic from map ");
 +
 +                                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 addr fetch failed ");
 +
 +                                continue;
 +                            }
 +
 +
 +                            UserVm userVm = _vmDao.findById(vmId);
 +                            VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
 +                            NicVO nicVo = _nicDao.findById(nicId);
 +                            NetworkVO network = _networkDao.findById(nicVo.getNetworkId());
 +
 +                            VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(userVm);
 +                            VirtualMachine vm = vmProfile.getVirtualMachine();
 +                            boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
 +
 +                            _vmIpFetchThreadExecutor.execute(new VmIpAddrFetchThread(vmId, nicId, vmInstance.getInstanceName(),
 +                                    isWindows, vm.getHostId(), network.getCidr()));
 +
 +                        }
 +                    } catch (Exception e) {
 +                        s_logger.error("Caught the Exception in VmIpFetchTask", e);
 +                    } finally {
 +                        scanLock.unlock();
 +                    }
 +                }
 +            } finally {
 +                scanLock.releaseRef();
 +            }
 +
 +        }
 +    }
 +
 +
 +    private class ExpungeTask extends ManagedContextRunnable {
 +        public ExpungeTask() {
 +        }
 +
 +        @Override
 +        protected void runInContext() {
 +            GlobalLock scanLock = GlobalLock.getInternLock("UserVMExpunge");
 +            try {
 +                if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {
 +                    try {
 +                        List<UserVmVO> vms = _vmDao.findDestroyedVms(new Date(System.currentTimeMillis() - ((long)_expungeDelay << 10)));
 +                        if (s_logger.isInfoEnabled()) {
 +                            if (vms.size() == 0) {
 +                                s_logger.trace("Found " + vms.size() + " vms to expunge.");
 +                            } else {
 +                                s_logger.info("Found " + vms.size() + " vms to expunge.");
 +                            }
 +                        }
 +                        for (UserVmVO vm : vms) {
 +                            try {
 +                                expungeVm(vm.getId());
 +                            } catch (Exception e) {
 +                                s_logger.warn("Unable to expunge " + vm, e);
 +                            }
 +                        }
 +                    } catch (Exception e) {
 +                        s_logger.error("Caught the following Exception", e);
 +                    } finally {
 +                        scanLock.unlock();
 +                    }
 +                }
 +            } finally {
 +                scanLock.releaseRef();
 +            }
 +        }
 +
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_UPDATE, eventDescription = "updating Vm")
 +    public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException {
 +        validateInputsAndPermissionForUpdateVirtualMachineCommand(cmd);
 +
 +        String displayName = cmd.getDisplayName();
 +        String group = cmd.getGroup();
 +        Boolean ha = cmd.getHaEnable();
 +        Boolean isDisplayVm = cmd.getDisplayVm();
 +        Long id = cmd.getId();
 +        Long osTypeId = cmd.getOsTypeId();
 +        String userData = cmd.getUserData();
 +        Boolean isDynamicallyScalable = cmd.isDynamicallyScalable();
 +        String hostName = cmd.getHostName();
 +        Map<String,String> details = cmd.getDetails();
 +        List<Long> securityGroupIdList = getSecurityGroupIdList(cmd);
 +        boolean cleanupDetails = cmd.isCleanupDetails();
 +        String extraConfig = cmd.getExtraConfig();
 +
 +        UserVmVO vmInstance = _vmDao.findById(cmd.getId());
 +        long accountId = vmInstance.getAccountId();
 +
 +        if (isDisplayVm != null && isDisplayVm != vmInstance.isDisplay()) {
 +            updateDisplayVmFlag(isDisplayVm, id, vmInstance);
 +        }
 +        if (cleanupDetails){
 +            userVmDetailsDao.removeDetails(id);
 +        }
 +        else {
 +            if (MapUtils.isNotEmpty(details)) {
 +                vmInstance.setDetails(details);
 +                _vmDao.saveDetails(vmInstance);
 +            }
 +            if (StringUtils.isNotBlank(extraConfig) && EnableAdditionalVmConfig.valueIn(accountId)) {
 +                AccountVO account = _accountDao.findById(accountId);
 +                addExtraConfig(vmInstance, account, extraConfig);
 +            }
 +        }
 +        return updateVirtualMachine(id, displayName, group, ha, isDisplayVm, osTypeId, userData, isDynamicallyScalable,
 +                cmd.getHttpMethod(), cmd.getCustomId(), hostName, cmd.getInstanceName(), securityGroupIdList, cmd.getDhcpOptionsMap());
 +    }
 +
 +    protected void updateDisplayVmFlag(Boolean isDisplayVm, Long id, UserVmVO vmInstance) {
 +        vmInstance.setDisplayVm(isDisplayVm);
 +
 +        // Resource limit changes
 +        ServiceOffering offering = _serviceOfferingDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId());
 +        _resourceLimitMgr.changeResourceCount(vmInstance.getAccountId(), ResourceType.user_vm, isDisplayVm);
 +        _resourceLimitMgr.changeResourceCount(vmInstance.getAccountId(), ResourceType.cpu, isDisplayVm, new Long(offering.getCpu()));
 +        _resourceLimitMgr.changeResourceCount(vmInstance.getAccountId(), ResourceType.memory, isDisplayVm, new Long(offering.getRamSize()));
 +
 +        // Usage
 +        saveUsageEvent(vmInstance);
 +
 +        // take care of the root volume as well.
 +        List<VolumeVO> rootVols = _volsDao.findByInstanceAndType(id, Volume.Type.ROOT);
 +        if (!rootVols.isEmpty()) {
 +            _volumeService.updateDisplay(rootVols.get(0), isDisplayVm);
 +        }
 +
 +        // take care of the data volumes as well.
 +        List<VolumeVO> dataVols = _volsDao.findByInstanceAndType(id, Volume.Type.DATADISK);
 +        for (Volume dataVol : dataVols) {
 +            _volumeService.updateDisplay(dataVol, isDisplayVm);
 +        }
 +    }
 +
 +    protected void validateInputsAndPermissionForUpdateVirtualMachineCommand(UpdateVMCmd cmd) {
 +        UserVmVO vmInstance = _vmDao.findById(cmd.getId());
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("unable to find virtual machine with id: " + cmd.getId());
 +        }
 +        validateGuestOsIdForUpdateVirtualMachineCommand(cmd);
 +        Account caller = CallContext.current().getCallingAccount();
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +    }
 +
 +    protected void validateGuestOsIdForUpdateVirtualMachineCommand(UpdateVMCmd cmd) {
 +        Long osTypeId = cmd.getOsTypeId();
 +        if (osTypeId != null) {
 +            GuestOSVO guestOS = _guestOSDao.findById(osTypeId);
 +            if (guestOS == null) {
 +                throw new InvalidParameterValueException("Please specify a valid guest OS ID.");
 +            }
 +        }
 +    }
 +
 +    private void saveUsageEvent(UserVmVO vm) {
 +
 +        // If vm not destroyed
 +        if( vm.getState() != State.Destroyed && vm.getState() != State.Expunging && vm.getState() != State.Error){
 +
 +            if(vm.isDisplayVm()){
 +                //1. Allocated VM Usage Event
 +                generateUsageEvent(vm, true, EventTypes.EVENT_VM_CREATE);
 +
 +                if(vm.getState() == State.Running || vm.getState() == State.Stopping){
 +                    //2. Running VM Usage Event
 +                    generateUsageEvent(vm, true, EventTypes.EVENT_VM_START);
 +
 +                    // 3. Network offering usage
 +                    generateNetworkUsageForVm(vm, true, EventTypes.EVENT_NETWORK_OFFERING_ASSIGN);
 +                }
 +
 +            }else {
 +                //1. Allocated VM Usage Event
 +                generateUsageEvent(vm, true, EventTypes.EVENT_VM_DESTROY);
 +
 +                if(vm.getState() == State.Running || vm.getState() == State.Stopping){
 +                    //2. Running VM Usage Event
 +                    generateUsageEvent(vm, true, EventTypes.EVENT_VM_STOP);
 +
 +                    // 3. Network offering usage
 +                    generateNetworkUsageForVm(vm, true, EventTypes.EVENT_NETWORK_OFFERING_REMOVE);
 +                }
 +            }
 +        }
 +
 +    }
 +
 +    private void generateNetworkUsageForVm(VirtualMachine vm, boolean isDisplay, String eventType){
 +
 +        List<NicVO> nics = _nicDao.listByVmId(vm.getId());
 +        for (NicVO nic : nics) {
 +            NetworkVO network = _networkDao.findById(nic.getNetworkId());
 +            long isDefault = (nic.isDefaultNic()) ? 1 : 0;
 +            UsageEventUtils.publishUsageEvent(eventType, vm.getAccountId(), vm.getDataCenterId(), vm.getId(),
 +                    Long.toString(nic.getId()), network.getNetworkOfferingId(), null, isDefault, vm.getClass().getName(), vm.getUuid(), isDisplay);
 +        }
 +
 +    }
 +
 +    @Override
 +    public UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData,
 +            Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList, Map<String, Map<Integer, String>> extraDhcpOptionsMap)
 +                    throws ResourceUnavailableException, InsufficientCapacityException {
 +        UserVmVO vm = _vmDao.findById(id);
 +        if (vm == null) {
 +            throw new CloudRuntimeException("Unable to find virtual machine with id " + id);
 +        }
 +
 +        if(instanceName != null){
 +            VMInstanceVO vmInstance = _vmInstanceDao.findVMByInstanceName(instanceName);
 +            if(vmInstance != null && vmInstance.getId() != id){
 +                throw new CloudRuntimeException("Instance name : " + instanceName + " is not unique");
 +            }
 +        }
 +
 +        if (vm.getState() == State.Error || vm.getState() == State.Expunging) {
 +            s_logger.error("vm is not in the right state: " + id);
 +            throw new InvalidParameterValueException("Vm with id " + id + " is not in the right state");
 +        }
 +
 +        if (displayName == null) {
 +            displayName = vm.getDisplayName();
 +        }
 +
 +        if (ha == null) {
 +            ha = vm.isHaEnabled();
 +        }
 +
 +        ServiceOffering offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
 +        if (!offering.isOfferHA() && ha) {
 +            throw new InvalidParameterValueException("Can't enable ha for the vm as it's created from the Service offering having HA disabled");
 +        }
 +
 +        if (isDisplayVmEnabled == null) {
 +            isDisplayVmEnabled = vm.isDisplayVm();
 +        }
 +
 +        boolean updateUserdata = false;
 +        if (userData != null) {
 +            // check and replace newlines
 +            userData = userData.replace("\\n", "");
 +            userData = validateUserData(userData, httpMethod);
 +            // update userData on domain router.
 +            updateUserdata = true;
 +        } else {
 +            userData = vm.getUserData();
 +        }
 +
 +        if (isDynamicallyScalable == null) {
 +            isDynamicallyScalable = vm.isDynamicallyScalable();
 +        }
 +
 +        if (osTypeId == null) {
 +            osTypeId = vm.getGuestOSId();
 +        }
 +
 +        if (group != null) {
 +            addInstanceToGroup(id, group);
 +        }
 +
 +        if (isDynamicallyScalable == null) {
 +            isDynamicallyScalable = vm.isDynamicallyScalable();
 +        }
 +
 +        boolean isVMware = (vm.getHypervisorType() == HypervisorType.VMware);
 +
 +        if (securityGroupIdList != null && isVMware) {
 +            throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor");
 +        } else {
 +            // Get default guest network in Basic zone
 +            Network defaultNetwork = null;
 +            try {
 +                DataCenterVO zone = _dcDao.findById(vm.getDataCenterId());
 +
 +                if (zone.getNetworkType() == NetworkType.Basic) {
 +                    // Get default guest network in Basic zone
 +                    defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId());
 +                } else if (zone.isSecurityGroupEnabled()) {
 +                    NicVO defaultNic = _nicDao.findDefaultNicForVM(vm.getId());
 +                    if (defaultNic != null) {
 +                        defaultNetwork = _networkDao.findById(defaultNic.getNetworkId());
 +                    }
 +                }
 +            } catch (InvalidParameterValueException e) {
 +                if(s_logger.isDebugEnabled()) {
 +                    s_logger.debug(e.getMessage(),e);
 +                }
 +                defaultNetwork = _networkModel.getDefaultNetworkForVm(id);
 +            }
 +
 +            if (securityGroupIdList != null && _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkModel.canAddDefaultSecurityGroup()) {
 +                if (vm.getState() == State.Stopped) {
 +                    // Remove instance from security groups
 +                    _securityGroupMgr.removeInstanceFromGroups(id);
 +                    // Add instance in provided groups
 +                    _securityGroupMgr.addInstanceToGroups(id, securityGroupIdList);
 +                } else {
 +                    throw new InvalidParameterValueException("Virtual machine must be stopped prior to update security groups ");
 +                }
 +            }
 +        }
 +        List<? extends Nic> nics = _nicDao.listByVmId(vm.getId());
 +        if (hostName != null) {
 +            // Check is hostName is RFC compliant
 +            checkNameForRFCCompliance(hostName);
 +
 +            if (vm.getHostName().equalsIgnoreCase(hostName)) {
 +                s_logger.debug("Vm " + vm + " is already set with the hostName specified: " + hostName);
 +                hostName = null;
 +            }
 +
 +            // Verify that vm's hostName is unique
 +
 +            List<NetworkVO> vmNtwks = new ArrayList<NetworkVO>(nics.size());
 +            for (Nic nic : nics) {
 +                vmNtwks.add(_networkDao.findById(nic.getNetworkId()));
 +            }
 +            checkIfHostNameUniqueInNtwkDomain(hostName, vmNtwks);
 +        }
 +
 +        List<NetworkVO> networks = nics.stream()
 +                .map(nic -> _networkDao.findById(nic.getNetworkId()))
 +                .collect(Collectors.toList());
 +
 +        verifyExtraDhcpOptionsNetwork(extraDhcpOptionsMap, networks);
 +        for (Nic nic : nics) {
 +            _networkMgr.saveExtraDhcpOptions(networks.stream()
 +                    .filter(network -> network.getId() == nic.getNetworkId())
 +                    .findFirst()
 +                    .get()
 +                    .getUuid(), nic.getId(), extraDhcpOptionsMap);
 +        }
 +
 +        _vmDao.updateVM(id, displayName, ha, osTypeId, userData, isDisplayVmEnabled, isDynamicallyScalable, customId, hostName, instanceName);
 +
 +        if (updateUserdata) {
 +            boolean result = updateUserDataInternal(_vmDao.findById(id));
 +            if (result) {
 +                s_logger.debug("User data successfully updated for vm id=" + id);
 +            } else {
 +                throw new CloudRuntimeException("Failed to reset userdata for the virtual machine ");
 +            }
 +        }
 +
 +        return _vmDao.findById(id);
 +    }
 +
 +    private boolean updateUserDataInternal(UserVm vm) throws ResourceUnavailableException, InsufficientCapacityException {
 +        VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
 +
 +        List<? extends Nic> nics = _nicDao.listByVmId(vm.getId());
 +        if (nics == null || nics.isEmpty()) {
 +            s_logger.error("unable to find any nics for vm " + vm.getUuid());
 +            return false;
 +        }
 +
 +        boolean userDataApplied = false;
 +        for (Nic nic : nics) {
 +            userDataApplied |= applyUserData(template.getHypervisorType(), vm, nic);
 +        }
 +        return userDataApplied;
 +    }
 +
 +    protected boolean applyUserData(HypervisorType hyperVisorType, UserVm vm, Nic nic) throws ResourceUnavailableException, InsufficientCapacityException {
 +        Network network = _networkDao.findById(nic.getNetworkId());
 +        NicProfile nicProfile = new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(
 +                hyperVisorType, network));
 +        VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm);
 +
 +        if (_networkModel.areServicesSupportedByNetworkOffering(network.getNetworkOfferingId(), Service.UserData)) {
 +            UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network);
 +            if (element == null) {
 +                throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update");
 +            }
 +            boolean result = element.saveUserData(network, nicProfile, vmProfile);
 +            if (!result) {
 +                s_logger.error("Failed to update userdata for vm " + vm + " and nic " + nic);
 +            } else {
 +                return true;
 +            }
 +        } else {
 +            s_logger.debug("Not applying userdata for nic id=" + nic.getId() + " in vm id=" + vmProfile.getId() + " because it is not supported in network id=" + network.getId());
 +        }
 +        return false;
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true)
 +    public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
 +        return startVirtualMachine(cmd.getId(), cmd.getHostId(), null, cmd.getDeploymentPlanner()).first();
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true)
 +    public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException {
 +        Account caller = CallContext.current().getCallingAccount();
 +        Long vmId = cmd.getId();
 +
 +        // Verify input parameters
 +        UserVmVO vmInstance = _vmDao.findById(vmId);
 +        if (vmInstance == null) {
 +            throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
 +        }
 +
 +        _accountMgr.checkAccess(caller, null, true, vmInstance);
 +
 +        checkIfHostOfVMIsInPrepareForMaintenanceState(vmInstance.getHostId(), vmId, "Reboot");
 +
 +        // If the VM is Volatile in nature, on reboot discard the VM's root disk and create a new root disk for it: by calling restoreVM
 +        long serviceOfferingId = vmInstance.getServiceOfferingId();
 +        ServiceOfferingVO offering = _serviceOfferingDao.findById(vmInstance.getId(), serviceOfferingId);
... 5226 lines suppressed ...


Mime
View raw message